ruby: add raw stl::string allocation, add _cpp_delete, tweak readme

develop
jj 2012-08-09 14:40:14 +02:00
parent 64a8443b5a
commit 7a03f93dbd
3 changed files with 155 additions and 38 deletions

@ -23,7 +23,7 @@ All ruby code runs while the main DF process and other plugins are suspended.
DFHack console
--------------
The ruby plugin defines 1 dfhack console command:
The ruby plugin defines one new dfhack console command:
rb_eval <ruby expression> ; evaluate a ruby expression and show the result in
the console. Ex: rb_eval df.unit_find().name.first_name
You can use single-quotes for strings ; avoid double-quotes that are parsed
@ -50,7 +50,7 @@ The script can access the console command arguments through the global variable
'$script_args', which is an array of ruby Strings.
The help string displayed in dfhack 'ls' command is the first line of the
script, if it is a comment (starts with '# ').
script, if it is a comment (ie starts with '# ').
Ruby helper functions
@ -67,7 +67,9 @@ obj1 and 2 should respond to #pos and #x #y #z.
Returns the MapBlock for the coordinates or nil.
df.map_tile_at(pos)
Returns a MapTile, holds all information relative to the map tile.
Returns a MapTile, holding all informations wrt the map tile (read&write).
This class is a ruby specific extention, to facilitate interaction with the
DF map data. Check out hack/ruby/map.rb.
df.each_map_block { |b| }
df.each_map_block_z(zlevel) { |b| }
@ -142,9 +144,16 @@ The ruby classes defined in ruby-autogen.rb are accessors to the underlying
df C++ objects in-memory. To allocate a new C++ object for use in DF, use the
RubyClass.cpp_new method (see buildings.rb for exemples), works for Compounds
only.
A special Compound DFHack::StlString is available for allocating a single c++
stl::string, so that you can call vmethods that take a string pointer argument
(eg getName).
ex: s = DFHack::StlString.cpp_new ; df.building_find.getName(s) ; p s.str
Deallocation is not supported. You may manually call df.free if you know
what you are doing (maps directly to the native malloc/free)
Deallocation may work, using the compound method _cpp_delete. Use with caution,
may crash your DF session. It may be simpler to just leak the memory.
_cpp_delete will try to free all memory directly used by the compound, eg
strings and vectors. It will *not* call the class destructor, and will not free
stuff behind pointers.
C++ std::string fields may be directly re-allocated using standard ruby strings,
e.g. some_unit.name.nickname = 'moo'
@ -160,11 +169,13 @@ To delete an element, vector.delete_at(index)
You can binary search an element in a vector for a given numeric field value:
df.world.unit.all.binsearch(42, :id)
will find the element whose 'id' field is 42 (needs the vector to be initially
will find the entry whose 'id' field is 42 (needs the vector to be initially
sorted by this field). The binsearch 2nd argument defaults to :id.
Any numeric field defined as being an enum value will be converted to a ruby
Symbol. This works for array indexes too.
ex: df.unit_find(:selected).status.labors[:HAUL_FOOD] = true
df.map_tile_at(df.cursor).designation.liquid_type = :Water
Virtual method calls are supported for C++ objects, with a maximum of 4
arguments. Arguments / return value are interpreted as Compound/Enums as
@ -194,7 +205,7 @@ Change current unit profession
Center the screen on unit ID '123'
df.center_viewscreen(df.unit_find(123))
Find an item at a given position, show its C++ classname
Find an item under the game cursor and show its C++ classname
p df.item_find(df.cursor)._rtti_classname
Find the raws name of the plant under cursor
@ -205,15 +216,29 @@ Dig a channel under the cursor
df.map_designation_at(df.cursor).dig = :Channel
df.map_block_at(df.cursor).flags.designated = true
Spawn 2/7 magma on the tile of the dwarf nicknamed 'hotfeet'
hot = df.unit_citizens.find { |u| u.name.nickname == 'hotfeet' }
df.map_tile_at(hot).spawn_magma(2)
Plugin compilation
------------------
The plugin consists of the *.rb file including user comfort functions and
describing basic classes used by the autogenerated code, and ruby-autogen.rb,
the auto-generated code.
The plugin consists of the main ruby.cpp native plugin and the *.rb files.
The native plugin handles only low-level ruby-to-df interaction (eg raw memory
read/write, and dfhack integration), and the .rb files hold end-user helper
functions.
On dfhack start, the native plugin will initialize the ruby interpreter, and
load hack/ruby/ruby.rb. This one then loads all other .rb files.
autogen is output by codegen.pl from dfhack/library/include/df/codegen.out.xml
The DF internal structures are described in ruby-autogen.rb .
It is output by ruby/codegen.pl, from dfhack/library/include/df/codegen.out.xml
It contains architecture-specific data (eg DF internal structures field offsets,
which differ between Windows and Linux. Linux and Macosx are the same, as they
both use gcc).
It is stored inside the build directory (eg build/plugins/ruby/ruby-autogen.rb)
For exemple,
<ld:global-type ld:meta="struct-type" type-name="unit">
@ -230,6 +255,7 @@ Will generate
The syntax for the 'field' method in ruby-autogen.rb is:
1st argument = name of the method
2nd argument = offset of this field from the beginning of the current struct.
This field depends on the compiler used by Toady to generate DF.
The block argument describes the type of the field: uint32, ptr to global...
Primitive type access is done through native methods from ruby.cpp (vector length,

@ -7,6 +7,7 @@ module DFHack
def _at(addr) ; d = dup ; d._memaddr = addr ; d ; end
def _get ; self ; end
def _cpp_init ; end
def _cpp_delete ; end
end
class Compound < MemStruct
@ -114,6 +115,11 @@ module DFHack
def _cpp_init
_fields_ancestors.each { |n, o, s| s._at(@_memaddr+o)._cpp_init }
end
def _cpp_delete
_fields_ancestors.each { |n, o, s| s._at(@_memaddr+o)._cpp_delete }
DFHack.free(@_memaddr)
@_memaddr = nil # turn future segfaults in harmless ruby exceptions
end
def _set(h)
case h
when Hash; h.each { |k, v| send("#{k}=", v) }
@ -372,6 +378,9 @@ module DFHack
def _cpp_init
_length.times { |i| _tgat(i)._cpp_init }
end
def _cpp_delete
_length.times { |i| _tgat(i)._cpp_delete }
end
alias length _length
alias size _length
def _tgat(i)
@ -423,10 +432,10 @@ module DFHack
DFHack.memory_vector32_ptrat(@_memaddr, idx)
end
def insert_at(idx, val)
DFHack.memory_vector32_insert(@_memaddr, idx, val)
DFHack.memory_vector32_insertat(@_memaddr, idx, val)
end
def delete_at(idx)
DFHack.memory_vector32_delete(@_memaddr, idx)
DFHack.memory_vector32_deleteat(@_memaddr, idx)
end
def _set(v)
@ -434,6 +443,12 @@ module DFHack
v.each_with_index { |e, i| self[i] = e } # patch entries
end
def self._cpp_new
new._at DFHack.memory_vector_new
end
def _cpp_delete
DFHack.memory_vector_delete(@_memaddr)
end
def _cpp_init
DFHack.memory_vector_init(@_memaddr)
end
@ -496,10 +511,10 @@ module DFHack
DFHack.memory_vector16_ptrat(@_memaddr, idx)
end
def insert_at(idx, val)
DFHack.memory_vector16_insert(@_memaddr, idx, val)
DFHack.memory_vector16_insertat(@_memaddr, idx, val)
end
def delete_at(idx)
DFHack.memory_vector16_delete(@_memaddr, idx)
DFHack.memory_vector16_deleteat(@_memaddr, idx)
end
end
class StlVector8 < StlVector32
@ -510,10 +525,10 @@ module DFHack
DFHack.memory_vector8_ptrat(@_memaddr, idx)
end
def insert_at(idx, val)
DFHack.memory_vector8_insert(@_memaddr, idx, val)
DFHack.memory_vector8_insertat(@_memaddr, idx, val)
end
def delete_at(idx)
DFHack.memory_vector8_delete(@_memaddr, idx)
DFHack.memory_vector8_deleteat(@_memaddr, idx)
end
end
class StlBitVector < StlVector32
@ -522,10 +537,10 @@ module DFHack
DFHack.memory_vectorbool_length(@_memaddr)
end
def insert_at(idx, val)
DFHack.memory_vectorbool_insert(@_memaddr, idx, val)
DFHack.memory_vectorbool_insertat(@_memaddr, idx, val)
end
def delete_at(idx)
DFHack.memory_vectorbool_delete(@_memaddr, idx)
DFHack.memory_vectorbool_deleteat(@_memaddr, idx)
end
def [](idx)
idx += length if idx < 0
@ -541,6 +556,12 @@ module DFHack
DFHack.memory_vectorbool_setat(@_memaddr, idx, v)
end
end
def self._cpp_new
new._at DFHack.memory_vectorbool_new
end
def _cpp_delete
DFHack.memory_vectorbool_delete(@_memaddr)
end
end
class StlString < MemStruct
def _get
@ -551,6 +572,12 @@ module DFHack
DFHack.memory_write_stlstring(@_memaddr, v)
end
def self._cpp_new
new._at DFHack.memory_stlstring_new
end
def _cpp_delete
DFHack.memory_stlstring_delete(@_memaddr)
end
def _cpp_init
DFHack.memory_stlstring_init(@_memaddr)
end
@ -574,7 +601,7 @@ module DFHack
def length
DFHack.memory_bitarray_length(@_memaddr)
end
# TODO _cpp_init
# TODO _cpp_init, _cpp_delete
def size ; length ; end
def resize(len)
DFHack.memory_bitarray_resize(@_memaddr, len)
@ -608,7 +635,7 @@ module DFHack
def length ; _length ; end
def size ; _length ; end
# TODO _cpp_init
# TODO _cpp_init, _cpp_delete
def _tgat(i)
@_tg._at(_ptr + i*@_tglen) if i >= 0 and i < _length
end
@ -702,6 +729,21 @@ module DFHack
def self.sym(v) ; (!v || (v == 0)) ? false : true ; end
end
class StlString < MemHack::Compound
field(:str, 0) { stl_string }
def self.cpp_new(init=nil)
s = MemHack::StlString._cpp_new
s._set(init) if init
new._at(s._memaddr)
end
def _cpp_delete
MemHack::StlString.new._at(@_memaddr+0)._cpp_delete
@_memaddr = nil
end
end
# cpp rtti name -> rb class
@rtti_n2c = {}
@rtti_c2n = {}

@ -528,7 +528,7 @@ static VALUE rb_dfmalloc(VALUE self, VALUE len)
if (!ptr)
return Qnil;
memset(ptr, 0, FIX2INT(len));
return rb_uint2inum((long)ptr);
return rb_uint2inum((uint32_t)ptr);
}
static VALUE rb_dffree(VALUE self, VALUE ptr)
@ -599,6 +599,18 @@ static VALUE rb_dfmemory_write_float(VALUE self, VALUE addr, VALUE val)
// stl::string
static VALUE rb_dfmemory_stlstring_new(VALUE self)
{
std::string *ptr = new std::string;
return rb_uint2inum((uint32_t)ptr);
}
static VALUE rb_dfmemory_stlstring_delete(VALUE self, VALUE addr)
{
std::string *ptr = (std::string*)rb_num2ulong(addr);
if (ptr)
delete ptr;
return Qtrue;
}
static VALUE rb_dfmemory_stlstring_init(VALUE self, VALUE addr)
{
// XXX THIS IS TERRIBLE
@ -621,6 +633,18 @@ static VALUE rb_dfmemory_write_stlstring(VALUE self, VALUE addr, VALUE val)
// vector access
static VALUE rb_dfmemory_vec_new(VALUE self)
{
std::vector<uint8_t> *ptr = new std::vector<uint8_t>;
return rb_uint2inum((uint32_t)ptr);
}
static VALUE rb_dfmemory_vec_delete(VALUE self, VALUE addr)
{
std::vector<uint8_t> *ptr = (std::vector<uint8_t>*)rb_num2ulong(addr);
if (ptr)
delete ptr;
return Qtrue;
}
static VALUE rb_dfmemory_vec_init(VALUE self, VALUE addr)
{
std::vector<uint8_t> *ptr = new std::vector<uint8_t>;
@ -638,13 +662,13 @@ static VALUE rb_dfmemory_vec8_ptrat(VALUE self, VALUE addr, VALUE idx)
std::vector<uint8_t> *v = (std::vector<uint8_t>*)rb_num2ulong(addr);
return rb_uint2inum((uint32_t)&v->at(FIX2INT(idx)));
}
static VALUE rb_dfmemory_vec8_insert(VALUE self, VALUE addr, VALUE idx, VALUE val)
static VALUE rb_dfmemory_vec8_insertat(VALUE self, VALUE addr, VALUE idx, VALUE val)
{
std::vector<uint8_t> *v = (std::vector<uint8_t>*)rb_num2ulong(addr);
v->insert(v->begin()+FIX2INT(idx), rb_num2ulong(val));
return Qtrue;
}
static VALUE rb_dfmemory_vec8_delete(VALUE self, VALUE addr, VALUE idx)
static VALUE rb_dfmemory_vec8_deleteat(VALUE self, VALUE addr, VALUE idx)
{
std::vector<uint8_t> *v = (std::vector<uint8_t>*)rb_num2ulong(addr);
v->erase(v->begin()+FIX2INT(idx));
@ -662,13 +686,13 @@ static VALUE rb_dfmemory_vec16_ptrat(VALUE self, VALUE addr, VALUE idx)
std::vector<uint16_t> *v = (std::vector<uint16_t>*)rb_num2ulong(addr);
return rb_uint2inum((uint32_t)&v->at(FIX2INT(idx)));
}
static VALUE rb_dfmemory_vec16_insert(VALUE self, VALUE addr, VALUE idx, VALUE val)
static VALUE rb_dfmemory_vec16_insertat(VALUE self, VALUE addr, VALUE idx, VALUE val)
{
std::vector<uint16_t> *v = (std::vector<uint16_t>*)rb_num2ulong(addr);
v->insert(v->begin()+FIX2INT(idx), rb_num2ulong(val));
return Qtrue;
}
static VALUE rb_dfmemory_vec16_delete(VALUE self, VALUE addr, VALUE idx)
static VALUE rb_dfmemory_vec16_deleteat(VALUE self, VALUE addr, VALUE idx)
{
std::vector<uint16_t> *v = (std::vector<uint16_t>*)rb_num2ulong(addr);
v->erase(v->begin()+FIX2INT(idx));
@ -686,13 +710,13 @@ static VALUE rb_dfmemory_vec32_ptrat(VALUE self, VALUE addr, VALUE idx)
std::vector<uint32_t> *v = (std::vector<uint32_t>*)rb_num2ulong(addr);
return rb_uint2inum((uint32_t)&v->at(FIX2INT(idx)));
}
static VALUE rb_dfmemory_vec32_insert(VALUE self, VALUE addr, VALUE idx, VALUE val)
static VALUE rb_dfmemory_vec32_insertat(VALUE self, VALUE addr, VALUE idx, VALUE val)
{
std::vector<uint32_t> *v = (std::vector<uint32_t>*)rb_num2ulong(addr);
v->insert(v->begin()+FIX2INT(idx), rb_num2ulong(val));
return Qtrue;
}
static VALUE rb_dfmemory_vec32_delete(VALUE self, VALUE addr, VALUE idx)
static VALUE rb_dfmemory_vec32_deleteat(VALUE self, VALUE addr, VALUE idx)
{
std::vector<uint32_t> *v = (std::vector<uint32_t>*)rb_num2ulong(addr);
v->erase(v->begin()+FIX2INT(idx));
@ -700,6 +724,24 @@ static VALUE rb_dfmemory_vec32_delete(VALUE self, VALUE addr, VALUE idx)
}
// vector<bool>
static VALUE rb_dfmemory_vecbool_new(VALUE self)
{
std::vector<bool> *ptr = new std::vector<bool>;
return rb_uint2inum((uint32_t)ptr);
}
static VALUE rb_dfmemory_vecbool_delete(VALUE self, VALUE addr)
{
std::vector<bool> *ptr = (std::vector<bool>*)rb_num2ulong(addr);
if (ptr)
delete ptr;
return Qtrue;
}
static VALUE rb_dfmemory_vecbool_init(VALUE self, VALUE addr)
{
std::vector<bool> *ptr = new std::vector<bool>;
memcpy((void*)rb_num2ulong(addr), (void*)ptr, sizeof(*ptr));
return Qtrue;
}
static VALUE rb_dfmemory_vecbool_length(VALUE self, VALUE addr)
{
std::vector<bool> *v = (std::vector<bool>*)rb_num2ulong(addr);
@ -716,13 +758,13 @@ static VALUE rb_dfmemory_vecbool_setat(VALUE self, VALUE addr, VALUE idx, VALUE
v->at(FIX2INT(idx)) = (BOOL_ISFALSE(val) ? 0 : 1);
return Qtrue;
}
static VALUE rb_dfmemory_vecbool_insert(VALUE self, VALUE addr, VALUE idx, VALUE val)
static VALUE rb_dfmemory_vecbool_insertat(VALUE self, VALUE addr, VALUE idx, VALUE val)
{
std::vector<bool> *v = (std::vector<bool>*)rb_num2ulong(addr);
v->insert(v->begin()+FIX2INT(idx), (BOOL_ISFALSE(val) ? 0 : 1));
return Qtrue;
}
static VALUE rb_dfmemory_vecbool_delete(VALUE self, VALUE addr, VALUE idx)
static VALUE rb_dfmemory_vecbool_deleteat(VALUE self, VALUE addr, VALUE idx)
{
std::vector<bool> *v = (std::vector<bool>*)rb_num2ulong(addr);
v->erase(v->begin()+FIX2INT(idx));
@ -834,27 +876,34 @@ static void ruby_bind_dfhack(void) {
rb_define_singleton_method(rb_cDFHack, "memory_write_int32", RUBY_METHOD_FUNC(rb_dfmemory_write_int32), 2);
rb_define_singleton_method(rb_cDFHack, "memory_write_float", RUBY_METHOD_FUNC(rb_dfmemory_write_float), 2);
rb_define_singleton_method(rb_cDFHack, "memory_stlstring_new", RUBY_METHOD_FUNC(rb_dfmemory_stlstring_new), 0);
rb_define_singleton_method(rb_cDFHack, "memory_stlstring_delete", RUBY_METHOD_FUNC(rb_dfmemory_stlstring_delete), 1);
rb_define_singleton_method(rb_cDFHack, "memory_stlstring_init", RUBY_METHOD_FUNC(rb_dfmemory_stlstring_init), 1);
rb_define_singleton_method(rb_cDFHack, "memory_read_stlstring", RUBY_METHOD_FUNC(rb_dfmemory_read_stlstring), 1);
rb_define_singleton_method(rb_cDFHack, "memory_write_stlstring", RUBY_METHOD_FUNC(rb_dfmemory_write_stlstring), 2);
rb_define_singleton_method(rb_cDFHack, "memory_vector_new", RUBY_METHOD_FUNC(rb_dfmemory_vec_new), 0);
rb_define_singleton_method(rb_cDFHack, "memory_vector_delete", RUBY_METHOD_FUNC(rb_dfmemory_vec_delete), 1);
rb_define_singleton_method(rb_cDFHack, "memory_vector_init", RUBY_METHOD_FUNC(rb_dfmemory_vec_init), 1);
rb_define_singleton_method(rb_cDFHack, "memory_vector8_length", RUBY_METHOD_FUNC(rb_dfmemory_vec8_length), 1);
rb_define_singleton_method(rb_cDFHack, "memory_vector8_ptrat", RUBY_METHOD_FUNC(rb_dfmemory_vec8_ptrat), 2);
rb_define_singleton_method(rb_cDFHack, "memory_vector8_insert", RUBY_METHOD_FUNC(rb_dfmemory_vec8_insert), 3);
rb_define_singleton_method(rb_cDFHack, "memory_vector8_delete", RUBY_METHOD_FUNC(rb_dfmemory_vec8_delete), 2);
rb_define_singleton_method(rb_cDFHack, "memory_vector8_insertat", RUBY_METHOD_FUNC(rb_dfmemory_vec8_insertat), 3);
rb_define_singleton_method(rb_cDFHack, "memory_vector8_deleteat", RUBY_METHOD_FUNC(rb_dfmemory_vec8_deleteat), 2);
rb_define_singleton_method(rb_cDFHack, "memory_vector16_length", RUBY_METHOD_FUNC(rb_dfmemory_vec16_length), 1);
rb_define_singleton_method(rb_cDFHack, "memory_vector16_ptrat", RUBY_METHOD_FUNC(rb_dfmemory_vec16_ptrat), 2);
rb_define_singleton_method(rb_cDFHack, "memory_vector16_insert", RUBY_METHOD_FUNC(rb_dfmemory_vec16_insert), 3);
rb_define_singleton_method(rb_cDFHack, "memory_vector16_delete", RUBY_METHOD_FUNC(rb_dfmemory_vec16_delete), 2);
rb_define_singleton_method(rb_cDFHack, "memory_vector16_insertat", RUBY_METHOD_FUNC(rb_dfmemory_vec16_insertat), 3);
rb_define_singleton_method(rb_cDFHack, "memory_vector16_deleteat", RUBY_METHOD_FUNC(rb_dfmemory_vec16_deleteat), 2);
rb_define_singleton_method(rb_cDFHack, "memory_vector32_length", RUBY_METHOD_FUNC(rb_dfmemory_vec32_length), 1);
rb_define_singleton_method(rb_cDFHack, "memory_vector32_ptrat", RUBY_METHOD_FUNC(rb_dfmemory_vec32_ptrat), 2);
rb_define_singleton_method(rb_cDFHack, "memory_vector32_insert", RUBY_METHOD_FUNC(rb_dfmemory_vec32_insert), 3);
rb_define_singleton_method(rb_cDFHack, "memory_vector32_delete", RUBY_METHOD_FUNC(rb_dfmemory_vec32_delete), 2);
rb_define_singleton_method(rb_cDFHack, "memory_vector32_insertat", RUBY_METHOD_FUNC(rb_dfmemory_vec32_insertat), 3);
rb_define_singleton_method(rb_cDFHack, "memory_vector32_deleteat", RUBY_METHOD_FUNC(rb_dfmemory_vec32_deleteat), 2);
rb_define_singleton_method(rb_cDFHack, "memory_vectorbool_new", RUBY_METHOD_FUNC(rb_dfmemory_vecbool_new), 0);
rb_define_singleton_method(rb_cDFHack, "memory_vectorbool_delete", RUBY_METHOD_FUNC(rb_dfmemory_vecbool_delete), 1);
rb_define_singleton_method(rb_cDFHack, "memory_vectorbool_init", RUBY_METHOD_FUNC(rb_dfmemory_vecbool_init), 1);
rb_define_singleton_method(rb_cDFHack, "memory_vectorbool_length", RUBY_METHOD_FUNC(rb_dfmemory_vecbool_length), 1);
rb_define_singleton_method(rb_cDFHack, "memory_vectorbool_at", RUBY_METHOD_FUNC(rb_dfmemory_vecbool_at), 2);
rb_define_singleton_method(rb_cDFHack, "memory_vectorbool_setat", RUBY_METHOD_FUNC(rb_dfmemory_vecbool_setat), 3);
rb_define_singleton_method(rb_cDFHack, "memory_vectorbool_insert", RUBY_METHOD_FUNC(rb_dfmemory_vecbool_insert), 3);
rb_define_singleton_method(rb_cDFHack, "memory_vectorbool_delete", RUBY_METHOD_FUNC(rb_dfmemory_vecbool_delete), 2);
rb_define_singleton_method(rb_cDFHack, "memory_vectorbool_insertat", RUBY_METHOD_FUNC(rb_dfmemory_vecbool_insertat), 3);
rb_define_singleton_method(rb_cDFHack, "memory_vectorbool_deleteat", RUBY_METHOD_FUNC(rb_dfmemory_vecbool_deleteat), 2);
rb_define_singleton_method(rb_cDFHack, "memory_bitarray_length", RUBY_METHOD_FUNC(rb_dfmemory_bitarray_length), 1);
rb_define_singleton_method(rb_cDFHack, "memory_bitarray_resize", RUBY_METHOD_FUNC(rb_dfmemory_bitarray_resize), 2);
rb_define_singleton_method(rb_cDFHack, "memory_bitarray_isset", RUBY_METHOD_FUNC(rb_dfmemory_bitarray_isset), 2);