diff --git a/docs/dev/data-identity.rst b/docs/dev/data-identity.rst index ae7076941d..0c638ffbaa 100644 --- a/docs/dev/data-identity.rst +++ b/docs/dev/data-identity.rst @@ -17,6 +17,13 @@ contains both a pointer to the C++ object itself and a pointer to a ``type_ident by that pointer. Note that the userdata object does not own the objects pointed to by these pointers, and the Lua engine is never responsible for managing their lifetimes. +Most identities are instances of the single template class ``type_identity_for``, which wraps the C++ type ``T``. +``type_identity_for`` derives from a runtime base class selected at compile time by inspection of ``T`` +(via ``std::is_enum``, ``std::is_pointer``, ``std::is_arithmetic``, container traits, and similar checks), +and implements the applicable virtual methods with ``if constexpr`` dispatch on the properties of ``T``. +A type can override the selection by defining a ``df_identity_base`` typedef naming the desired base; +the code generator emits this typedef for all generated compound types. + ``type_identity`` defines the following public methods: - ``byte_size``: returns the size, in bytes, of the object held @@ -33,7 +40,7 @@ never responsible for managing their lifetimes. - ``is_primitive``: indicates that ``lua_read`` will store a *copy* of the object on the Lua stack instead of a non-owning reference to it. Used for types that have direct representations in Lua: numbers, booleans, simple strings -- ``is_constructed``: indicates that creating a C++ instance of this type requires the use of a possibly nontrivial constructor. A type identity that is both primitive and constructed cannot be inserted into a container. At the moment the only type identity that is both primitive and constructed is ``stl_string_identity``, which wraps the C++ ``std::string`` type. +- ``is_constructed``: indicates that creating a C++ instance of this type requires the use of a possibly nontrivial constructor. A type identity that is both primitive and constructed cannot be inserted into a container. At the moment the only type identity that is both primitive and constructed is ``type_identity_for``, which wraps the C++ ``std::string`` type. - ``is_container``: indicates that the type is a container and thus implements the methods specific to ``container_identity`` @@ -41,7 +48,9 @@ never responsible for managing their lifetimes. - ``copy``: copy the object at ``src`` onto ``tgt``. This uses ``memmove`` for primitive types, and C++ copy-assignment (when possible) for other types -There are plethora of subclasses of ``type_identity``: +The identity class tree is divided into two layers: the *runtime base classes*, which are ordinary +(non-template) classes used for polymorphic dispatch, and the single leaf template ``type_identity_for``, +which derives from the appropriate runtime base for the C++ type ``T`` that it wraps: * ``type_identity`` the abstract base class of all type identities @@ -55,69 +64,49 @@ There are plethora of subclasses of ``type_identity``: * ``struct_identity`` C++ ``class`` or ``structure`` - * ``global_identity`` holds, as a quasiobject, handles for all of the known Dwarf Fortress program-scope static objects as if they were fields of an object called ``global`` - * ``union_identity`` C++ ``union`` * ``other_vectors_identity`` special-case identity for the categorized subvectors of objects that appears in many of Dwarf Fortress's "handler" classes * ``virtual_identity`` polymorphic C++ ``class`` or ``structure`` having a virtual table to handle virtual dispatch - * ``stl_string_identity`` ``std::string`` - * ``xlsx_file_handle_identity`` special case * ``xlsx_sheet_handle_identity`` special case * ``container_identity`` "containers" generally. note that all container types are homogeneous (that is, the elements of the container must all be of the same type). abstract base class - * ``bit_container_identity`` for containers that contain bools stored one element per *bit* (rather than per byte) - - * ``bit_array_identity`` Dwarf Fortress's ``BitArray`` type - - * ``stl_bit_vector_identity`` ``std::vector`` + * ``bit_container_identity`` for containers that contain bools stored one element per *bit* (rather than per byte); also provides the ``get_item``/``set_item`` virtual interface * ``buffer_container_identity`` C++ static arrays and raw C++ pointers acting as arrays of unspecified bound - * ``enum_list_attr_identity`` (template) metaobject with metadata about a C++ enumeration; may also include additional metadata - * ``ptr_container_identity`` containers that contain pointers - * ``stl_ptr_container_identity`` containers that are of the form ``std::vector`` for some ``T`` - - * ``ro_stl_container_identity`` (template) "read only containers" - - * ``ro_stl_assoc_container_identity`` (template) ``std::map`` and ``std::unordered_map`` - - * ``stl_container_identity`` (template) ``std::vector`` where ``T`` is *not* a pointer (and not ``bool``) - * ``opaque_identity`` opaque wrapper around any type, provides no functionality - * ``stl_string_identity`` ``std::string`` - * ``function_identity_base`` abstract base class for ``function_identity`` * ``function_identity`` (template) wrapper around a C++ function that can be invoked from Lua - * ``primitive_identity`` wrapper around a primitive type. primitive types are fixed-length objects with no internal structure - - * ``bool_identity`` ``bool`` - - * ``number_identity_base`` abstract base for numeric types - - * ``float_identity_base`` abstract base for floating point types - - * ``float_identity`` (template) ``double`` and ``float`` - - * ``integer_identity_base`` abstract base for integral types + * ``primitive_identity_base`` abstract base class for primitive types. primitive types are fixed-length objects with no internal structure - * ``integer_identity`` (template) ``int8_t``, ``int16_t``, ``int32_t``, ``size_t``, etc. lots of these + * ``number_identity_base`` abstract base for numeric types (and ``bool``); provides the ``isInteger`` discriminator - * ``pointer_identity`` any arbitrary C++ pointer (other than ``char*``) + * ``pointer_identity_base`` abstract base class for pointer identities; provides ``getTarget`` - * ``ptr_string_identity`` C-style (``char *``) string +* ``type_identity_for`` the leaf template that provides the identity of the C++ type ``T``. Its runtime + base class is selected by compile-time inspection of ``T`` (or by a ``df_identity_base`` typedef in ``T``), + covering all of the categories above: enums, bitfields, structs, unions, virtual classes, numbers, + pointers, C strings, ``std::string``, ``std::filesystem::path``, static arrays, containers, and opaque + types. Container categories are detected by capability rather than by type name: any type that provides + index-addressable elements (``t[i]`` yielding a ``T&``), bit-element access (a bool proxy or + ``is_set``/``set`` accessors), mapped iteration (``mapped_type`` plus ``begin``/``end``), read-only + iteration, or a ``{ size, items[] }`` table (``enum_list_attr``) is treated as the corresponding kind of + container without being listed explicitly. -Types marked with "(template)" are C++ template types, all parameterized by a single typename. + ``global_identity`` is an alias for ``type_identity_for``, where ``global_object`` is an + empty placeholder type: it holds, as a quasiobject, handles for all of the known Dwarf Fortress + program-scope static objects as if they were fields of an object called ``global``. Type identity object lifetime and mutability ============================================ diff --git a/library/DataDefs.cpp b/library/DataDefs.cpp index 97dc331406..ce11f174ed 100644 --- a/library/DataDefs.cpp +++ b/library/DataDefs.cpp @@ -272,7 +272,7 @@ bool struct_identity::is_equivalent(const struct_identity* other) const return true; } -const std::string pointer_identity::getFullName() const +const std::string pointer_identity_base::getFullName() const { return (target ? target->getFullName() : std::string("void")) + "*"; } diff --git a/library/DataIdentity.cpp b/library/DataIdentity.cpp index 111166d90e..cc6ea64fa6 100644 --- a/library/DataIdentity.cpp +++ b/library/DataIdentity.cpp @@ -21,38 +21,35 @@ // without it the macro generates a syntax error when type is a template specification namespace df { -#define NUMBER_IDENTITY_TRAITS(category, type, name) \ - const category##_identity identity_traits::identity(name); -#define INTEGER_IDENTITY_TRAITS(type, name) NUMBER_IDENTITY_TRAITS(integer, type, name) -#define FLOAT_IDENTITY_TRAITS(type) NUMBER_IDENTITY_TRAITS(float, type, #type) +#define NUMBER_IDENTITY_TRAITS(type, name) \ + const type_identity_for identity_traits::identity(name); #define OPAQUE_IDENTITY_TRAITS_NAME(name, ...) \ - const opaque_identity identity_traits<__VA_ARGS__ >::identity(sizeof(__VA_ARGS__), allocator_fn<__VA_ARGS__ >, name) + const type_identity_for<__VA_ARGS__ > identity_traits<__VA_ARGS__ >::identity(sizeof(__VA_ARGS__), allocator_fn<__VA_ARGS__ >, name) #define OPAQUE_IDENTITY_TRAITS(...) OPAQUE_IDENTITY_TRAITS_NAME(#__VA_ARGS__, __VA_ARGS__ ) - INTEGER_IDENTITY_TRAITS(char, "char"); - INTEGER_IDENTITY_TRAITS(signed char, "int8_t"); - INTEGER_IDENTITY_TRAITS(unsigned char, "uint8_t"); - INTEGER_IDENTITY_TRAITS(short, "int16_t"); - INTEGER_IDENTITY_TRAITS(unsigned short, "uint16_t"); - INTEGER_IDENTITY_TRAITS(int, "int32_t"); - INTEGER_IDENTITY_TRAITS(unsigned int, "uint32_t"); - INTEGER_IDENTITY_TRAITS(long, "long"); - INTEGER_IDENTITY_TRAITS(unsigned long, "unsigned long"); - INTEGER_IDENTITY_TRAITS(long long, "int64_t"); - INTEGER_IDENTITY_TRAITS(unsigned long long, "uint64_t"); - INTEGER_IDENTITY_TRAITS(wchar_t, "wchar_t"); - FLOAT_IDENTITY_TRAITS(float); - FLOAT_IDENTITY_TRAITS(double); - - const bool_identity identity_traits::identity; - const stl_string_identity identity_traits::identity; - const path_identity identity_traits::identity; - const ptr_string_identity identity_traits::identity; - const ptr_string_identity identity_traits::identity; - const pointer_identity identity_traits::identity; - const stl_ptr_vector_identity identity_traits >::identity; - const stl_bit_vector_identity identity_traits >::identity; - const bit_array_identity identity_traits >::identity; + NUMBER_IDENTITY_TRAITS(char, "char"); + NUMBER_IDENTITY_TRAITS(signed char, "int8_t"); + NUMBER_IDENTITY_TRAITS(unsigned char, "uint8_t"); + NUMBER_IDENTITY_TRAITS(short, "int16_t"); + NUMBER_IDENTITY_TRAITS(unsigned short, "uint16_t"); + NUMBER_IDENTITY_TRAITS(int, "int32_t"); + NUMBER_IDENTITY_TRAITS(unsigned int, "uint32_t"); + NUMBER_IDENTITY_TRAITS(long, "long"); + NUMBER_IDENTITY_TRAITS(unsigned long, "unsigned long"); + NUMBER_IDENTITY_TRAITS(long long, "int64_t"); + NUMBER_IDENTITY_TRAITS(unsigned long long, "uint64_t"); + NUMBER_IDENTITY_TRAITS(wchar_t, "wchar_t"); + NUMBER_IDENTITY_TRAITS(float, "float"); + NUMBER_IDENTITY_TRAITS(double, "double"); + NUMBER_IDENTITY_TRAITS(bool, "bool"); + const type_identity_for identity_traits::identity; + const type_identity_for identity_traits::identity; + const type_identity_for identity_traits::identity; + const type_identity_for identity_traits::identity; + const type_identity_for identity_traits::identity(nullptr); + const type_identity_for > identity_traits >::identity; + const type_identity_for > identity_traits >::identity; + const type_identity_for > identity_traits >::identity; OPAQUE_IDENTITY_TRAITS(std::condition_variable); OPAQUE_IDENTITY_TRAITS(std::fstream); @@ -70,6 +67,6 @@ namespace df { const buffer_container_identity buffer_container_identity::base_instance; - const stl_container_identity > stl_vector_int32_t_identity("vector", identity_traits::get()); - const stl_container_identity > stl_vector_int16_t_identity("vector", identity_traits::get()); + const type_identity_for > stl_vector_int32_t_identity("vector", identity_traits::get()); + const type_identity_for > stl_vector_int16_t_identity("vector", identity_traits::get()); } diff --git a/library/LuaTypes.cpp b/library/LuaTypes.cpp index 0f990a6b84..c9922689ca 100644 --- a/library/LuaTypes.cpp +++ b/library/LuaTypes.cpp @@ -115,88 +115,14 @@ void enum_identity::lua_write(lua_State *state, int fname_idx, void *ptr, int va base_type->lua_write(state, fname_idx, ptr, val_index); } -void df::integer_identity_base::lua_read(lua_State *state, int fname_idx, void *ptr) const -{ - lua_pushinteger(state, read(ptr)); -} - -void df::integer_identity_base::lua_write(lua_State *state, int fname_idx, void *ptr, int val_index) const -{ - int is_num = 0; - auto value = lua_tointegerx(state, val_index, &is_num); - if (!is_num) - field_error(state, fname_idx, "integer expected", "write"); - write(ptr, value); -} - -void df::float_identity_base::lua_read(lua_State *state, int fname_idx, void *ptr) const -{ - lua_pushnumber(state, read(ptr)); -} - -void df::float_identity_base::lua_write(lua_State *state, int fname_idx, void *ptr, int val_index) const -{ - if (!lua_isnumber(state, val_index)) - field_error(state, fname_idx, "number expected", "write"); - - write(ptr, lua_tonumber(state, val_index)); -} - -void df::bool_identity::lua_read(lua_State *state, int fname_idx, void *ptr) const -{ - lua_pushboolean(state, *(bool*)ptr); -} - -void df::bool_identity::lua_write(lua_State *state, int fname_idx, void *ptr, int val_index) const -{ - char *pb = (char*)ptr; - - if (lua_isboolean(state, val_index) || lua_isnil(state, val_index)) - *pb = lua_toboolean(state, val_index); - else if (lua_isnumber(state, val_index)) - *pb = lua_tointeger(state, val_index); - else - field_error(state, fname_idx, "boolean or number expected", "write"); -} - -void df::ptr_string_identity::lua_read(lua_State *state, int fname_idx, void *ptr) const -{ - auto pstr = (char**)ptr; - if (*pstr) - lua_pushstring(state, *pstr); - else - lua_pushnil(state); -} - -void df::ptr_string_identity::lua_write(lua_State *state, int fname_idx, void *ptr, int val_index) const -{ - field_error(state, fname_idx, "raw pointer string", "write"); -} - -void df::stl_string_identity::lua_read(lua_State *state, int fname_idx, void *ptr) const -{ - auto pstr = (std::string*)ptr; - lua_pushlstring(state, pstr->data(), pstr->size()); -} - -void df::stl_string_identity::lua_write(lua_State *state, int fname_idx, void *ptr, int val_index) const -{ - size_t size; - const char *bytes = lua_tolstring(state, val_index, &size); - if (!bytes) - field_error(state, fname_idx, "string expected", "write"); - - *(std::string*)ptr = std::string(bytes, size); -} - -void df::path_identity::lua_read(lua_State* state, int fname_idx, void* ptr) const +void DFHack::lua_read_path(lua_State* state, void* ptr) { auto ppath = (std::filesystem::path*)ptr; auto str = DFHack::Filesystem::as_string(*ppath); lua_pushlstring(state, (char*)str.data(), str.size()); } -void df::path_identity::lua_write(lua_State* state, int fname_idx, void* ptr, int val_index) const +void DFHack::lua_write_path(lua_State* state, int fname_idx, void* ptr, int val_index) { size_t size; const char* bytes = lua_tolstring(state, val_index, &size); @@ -207,12 +133,12 @@ void df::path_identity::lua_write(lua_State* state, int fname_idx, void* ptr, in *(std::filesystem::path*)ptr = std::filesystem::path(str); } -void df::pointer_identity::lua_read(lua_State *state, int fname_idx, void *ptr, const type_identity *target) +void df::pointer_identity_base::lua_read(lua_State *state, int fname_idx, void *ptr, const type_identity *target) { push_object_internal(state, target, *(void**)ptr); } -void df::pointer_identity::lua_read(lua_State *state, int fname_idx, void *ptr) const +void df::pointer_identity_base::lua_read(lua_State *state, int fname_idx, void *ptr) const { lua_read(state, fname_idx, ptr, target); } @@ -276,7 +202,7 @@ static bool is_null(lua_State *state, int val_index) !lua_touserdata(state, val_index)); } -void df::pointer_identity::lua_write(lua_State *state, int fname_idx, void *ptr, +void df::pointer_identity_base::lua_write(lua_State *state, int fname_idx, void *ptr, const type_identity *target, int val_index) { auto pptr = (void**)ptr; @@ -300,7 +226,7 @@ void df::pointer_identity::lua_write(lua_State *state, int fname_idx, void *ptr, } } -void df::pointer_identity::lua_write(lua_State *state, int fname_idx, void *ptr, int val_index) const +void df::pointer_identity_base::lua_write(lua_State *state, int fname_idx, void *ptr, int val_index) const { lua_write(state, fname_idx, ptr, target, val_index); } @@ -373,14 +299,14 @@ void ptr_container_identity::lua_item_read(lua_State *state, int fname_idx, void { auto id = (type_identity*)lua_touserdata(state, UPVAL_ITEM_ID); void *pitem = item_pointer(&df::identity_traits::identity, ptr, idx); - df::pointer_identity::lua_read(state, fname_idx, pitem, id); + df::pointer_identity_base::lua_read(state, fname_idx, pitem, id); } void ptr_container_identity::lua_item_write(lua_State *state, int fname_idx, void *ptr, int idx, int val_index) const { auto id = (type_identity*)lua_touserdata(state, UPVAL_ITEM_ID); void *pitem = item_pointer(&df::identity_traits::identity, ptr, idx); - df::pointer_identity::lua_write(state, fname_idx, pitem, id, val_index); + df::pointer_identity_base::lua_write(state, fname_idx, pitem, id, val_index); } bool ptr_container_identity::lua_insert2(lua_State *state, int fname_idx, void *ptr, int idx, int val_index) const @@ -388,7 +314,7 @@ bool ptr_container_identity::lua_insert2(lua_State *state, int fname_idx, void * auto id = (type_identity*)lua_touserdata(state, UPVAL_ITEM_ID); void *pitem = NULL; - df::pointer_identity::lua_write(state, fname_idx, &pitem, id, val_index); + df::pointer_identity_base::lua_write(state, fname_idx, &pitem, id, val_index); return insert(ptr, idx, pitem); } @@ -522,7 +448,7 @@ static void read_field(lua_State *state, const struct_field_info *field, void *p return; case struct_field_info::POINTER: - df::pointer_identity::lua_read(state, 2, ptr, field->type); + df::pointer_identity_base::lua_read(state, 2, ptr, field->type); return; case struct_field_info::CONTAINER: @@ -608,7 +534,7 @@ static void write_field(lua_State *state, const struct_field_info *field, void * return; case struct_field_info::POINTER: - df::pointer_identity::lua_write(state, 2, ptr, field->type, value_idx); + df::pointer_identity_base::lua_write(state, 2, ptr, field->type, value_idx); return; case struct_field_info::STATIC_ARRAY: @@ -1771,10 +1697,10 @@ void other_vectors_identity::build_metatable(lua_State *state) const SetPtrMethods(state, base+1, base+2); } -void global_identity::build_metatable(lua_State *state) const +void DFHack::build_global_metatable(lua_State *state, const struct_identity *id) { int base = lua_gettop(state); - MakeFieldMetatable(state, this, meta_global_index, meta_global_newindex, meta_struct_next, true); + MakeFieldMetatable(state, id, meta_global_index, meta_global_newindex, meta_struct_next, true); SetStructMethod(state, base+1, base+2, meta_global_field_reference, "_field"); SetPtrMethods(state, base+1, base+2); } @@ -1840,12 +1766,12 @@ void LuaWrapper::push_adhoc_pointer(lua_State *state, void *ptr, const type_iden { /* * HACK: relies on - * 1) pointer_identity destructor being no-op + * 1) pointer_identity_base destructor being no-op * 2) lua gc never moving objects in memory */ - void *newobj = lua_newuserdata(state, sizeof(pointer_identity)); - id = new (newobj) pointer_identity(target); + void *newobj = lua_newuserdata(state, sizeof(pointer_identity_base)); + id = new (newobj) pointer_identity_base(target); SaveInTable(state, const_cast(target), &DFHACK_PTR_IDTABLE_TOKEN); lua_pop(state, 1); diff --git a/library/LuaWrapper.cpp b/library/LuaWrapper.cpp index df81ea808e..330af3ec85 100644 --- a/library/LuaWrapper.cpp +++ b/library/LuaWrapper.cpp @@ -283,8 +283,8 @@ bool LuaWrapper::is_type_compatible(lua_State *state, const type_identity *type1 { case IDTYPE_POINTER: return is_type_compatible(state, - ((const pointer_identity*)type1)->getTarget(), 0, - ((const pointer_identity*)type2)->getTarget(), 0, + ((const pointer_identity_base*)type1)->getTarget(), 0, + ((const pointer_identity_base*)type2)->getTarget(), 0, exact_equal); break; @@ -304,8 +304,8 @@ bool LuaWrapper::is_type_compatible(lua_State *state, const type_identity *type1 case IDTYPE_STL_PTR_VECTOR: { - auto b1 = (const df::stl_ptr_vector_identity*)type1; - auto b2 = (const df::stl_ptr_vector_identity*)type2; + auto b1 = (const df::ptr_container_identity*)type1; + auto b2 = (const df::ptr_container_identity*)type2; const type_identity *item1 = b1->getItemType(), *item2 = b2->getItemType(); fetch_container_details(state, meta1, &item1, NULL); diff --git a/library/include/BitArray.h b/library/include/BitArray.h index 7658e33102..3de148c57a 100644 --- a/library/include/BitArray.h +++ b/library/include/BitArray.h @@ -609,6 +609,8 @@ namespace DFHack template struct DfOtherVectors { + using dfhack_other_vectors = void; + std::vector & operator[](O other_id) { CHECK_INVALID_ARGUMENT(size_t(other_id) < sizeof(T) / sizeof(std::vector)); diff --git a/library/include/DataDefs.h b/library/include/DataDefs.h index 467816e69c..20c61c7cb6 100644 --- a/library/include/DataDefs.h +++ b/library/include/DataDefs.h @@ -77,6 +77,8 @@ namespace DFHack using TAllocateFn = void *(*)(void*, const void*); + template class type_identity_for; + class DFHACK_EXPORT type_identity { const size_t size; @@ -327,16 +329,14 @@ namespace DFHack bool is_equivalent(const struct_identity* other) const; }; - class DFHACK_EXPORT global_identity : public struct_identity { - public: - global_identity(const struct_field_info *fields) - : struct_identity(0,NULL,NULL,"global",NULL,fields) {} - - virtual identity_type type() const override { return IDTYPE_GLOBAL; } - - virtual void build_metatable(lua_State *state) const override; + // Placeholder type wrapped by the identity of the global "object", + // which does not correspond to an actual C++ type. + struct global_object { + using df_identity_base = struct_identity; }; + using global_identity = type_identity_for; + class DFHACK_EXPORT union_identity : public struct_identity { public: union_identity(size_t size, TAllocateFn alloc, @@ -516,6 +516,7 @@ inline int linear_index(const DFHack::enum_list_attr &lst, const st namespace df { + using DFHack::type_identity_for; using DFHack::type_identity; using DFHack::compound_identity; using DFHack::virtual_ptr; @@ -603,8 +604,16 @@ namespace df template struct identity_traits {}; + /* + * Compound types (structs, unions, classes) are recognized either by an + * explicit df_identity_base typedef that derives from compound_identity + * (emitted by the code generator), or by the _identity member itself + * being convertible to a compound identity. + */ template - requires requires () { { &T::_identity } -> std::convertible_to; } + requires (requires { typename T::df_identity_base; } && + std::is_base_of_v) || + requires () { { &T::_identity } -> std::convertible_to; } struct identity_traits { static const bool is_primitive = false; static const compound_identity *get() { return &T::_identity; } diff --git a/library/include/DataIdentity.h b/library/include/DataIdentity.h index 85f10229e1..424d8e131d 100644 --- a/library/include/DataIdentity.h +++ b/library/include/DataIdentity.h @@ -24,11 +24,14 @@ distribution. #pragma once +#include #include #include +#include #include #include #include +#include #include #include #include @@ -74,10 +77,11 @@ namespace DFHack virtual void lua_write(lua_State *state, int fname_idx, void *ptr, int val_index) const override; }; - class DFHACK_EXPORT primitive_identity : public type_identity { - public: - primitive_identity(size_t size) : type_identity(size) {}; + class DFHACK_EXPORT primitive_identity_base : public type_identity { + protected: + primitive_identity_base(size_t size) : type_identity(size) {}; + public: virtual identity_type type() const override { return IDTYPE_PRIMITIVE; } }; @@ -92,12 +96,12 @@ namespace DFHack virtual identity_type type() const override { return IDTYPE_OPAQUE; } }; - class DFHACK_EXPORT pointer_identity : public primitive_identity { + class DFHACK_EXPORT pointer_identity_base : public primitive_identity_base { const type_identity *target; public: - pointer_identity(const type_identity *target = NULL) - : primitive_identity(sizeof(void*)), target(target) {}; + pointer_identity_base(const type_identity *target = NULL) + : primitive_identity_base(sizeof(void*)), target(target) {}; virtual identity_type type() const override { return IDTYPE_POINTER; } @@ -197,173 +201,31 @@ namespace DFHack namespace df { using DFHack::function_identity_base; - using DFHack::primitive_identity; + using DFHack::primitive_identity_base; using DFHack::opaque_identity; - using DFHack::pointer_identity; + using DFHack::pointer_identity_base; using DFHack::container_identity; using DFHack::ptr_container_identity; using DFHack::bit_container_identity; - class DFHACK_EXPORT number_identity_base : public primitive_identity { + class DFHACK_EXPORT number_identity_base : public primitive_identity_base { const char *name; public: number_identity_base(size_t size, const char *name) - : primitive_identity(size), name(name) {}; + : primitive_identity_base(size), name(name) {}; const std::string getFullName() const override { return name; } - virtual void lua_read(lua_State *state, int fname_idx, void *ptr) const override = 0; - virtual void lua_write(lua_State *state, int fname_idx, void *ptr, int val_index) const override = 0; - - }; - - class DFHACK_EXPORT integer_identity_base : public number_identity_base { - public: - integer_identity_base(size_t size, const char *name) - : number_identity_base(size, name) {} - - virtual void lua_read(lua_State *state, int fname_idx, void *ptr) const override; - virtual void lua_write(lua_State *state, int fname_idx, void *ptr, int val_index) const override; - - protected: - virtual int64_t read(void *ptr) const = 0; - virtual void write(void *ptr, int64_t val) const = 0; - }; - - class DFHACK_EXPORT float_identity_base : public number_identity_base { - public: - float_identity_base(size_t size, const char *name) - : number_identity_base(size, name) {} - - virtual void lua_read(lua_State *state, int fname_idx, void *ptr) const override; - virtual void lua_write(lua_State *state, int fname_idx, void *ptr, int val_index) const override; - - protected: - virtual double read(void *ptr) const = 0; - virtual void write(void *ptr, double val) const = 0; - }; - - template - class integer_identity : public integer_identity_base { - public: - integer_identity(const char *name) : integer_identity_base(sizeof(T), name) {} - - protected: - virtual int64_t read(void *ptr) const override { return int64_t(*(T*)ptr); } - virtual void write(void *ptr, int64_t val) const override { *(T*)ptr = T(val); } - }; - - template - class float_identity : public float_identity_base { - public: - float_identity(const char *name) : float_identity_base(sizeof(T), name) {} - - protected: - virtual double read(void *ptr) const override { return double(*(T*)ptr); } - virtual void write(void *ptr, double val) const override { *(T*)ptr = T(val); } - }; - - class DFHACK_EXPORT bool_identity : public primitive_identity { - public: - bool_identity() : primitive_identity(sizeof(bool)) {}; - - const std::string getFullName() const override { return "bool"; } - - virtual void lua_read(lua_State *state, int fname_idx, void *ptr) const override; - virtual void lua_write(lua_State *state, int fname_idx, void *ptr, int val_index) const override; - }; - - class DFHACK_EXPORT ptr_string_identity : public primitive_identity { - public: - ptr_string_identity() : primitive_identity(sizeof(char*)) {}; - - const std::string getFullName() const override { return "char*"; } - - virtual void lua_read(lua_State *state, int fname_idx, void *ptr) const override; - virtual void lua_write(lua_State *state, int fname_idx, void *ptr, int val_index) const override; + virtual bool isInteger() const { return false; } }; - class DFHACK_EXPORT stl_string_identity : public DFHack::constructed_identity { - public: - stl_string_identity() - : constructed_identity(sizeof(std::string), &allocator_fn) - {}; - - const std::string getFullName() const override { return "string"; } - - virtual DFHack::identity_type type() const override { return DFHack::IDTYPE_PRIMITIVE; } - - virtual bool isPrimitive() const override { return true; } - - virtual void lua_read(lua_State *state, int fname_idx, void *ptr) const override; - virtual void lua_write(lua_State *state, int fname_idx, void *ptr, int val_index) const override; - }; - - class DFHACK_EXPORT path_identity : public DFHack::constructed_identity { - public: - path_identity() - : constructed_identity(sizeof(std::filesystem::path), &allocator_fn) - { - }; - - const std::string getFullName() const override { return "path"; } - - virtual DFHack::identity_type type() const override { return DFHack::IDTYPE_PRIMITIVE; } - - virtual bool isPrimitive() const override { return true; } - - virtual void lua_read(lua_State* state, int fname_idx, void* ptr) const override; - virtual void lua_write(lua_State* state, int fname_idx, void* ptr, int val_index) const override; - }; - - - class DFHACK_EXPORT stl_ptr_vector_identity : public ptr_container_identity { - public: - using container = std::vector; - - /* - * This class assumes that std::vector is equivalent - * in layout and behavior to std::vector for any T. - */ - - stl_ptr_vector_identity(const type_identity *item = NULL, const enum_identity *ienum = NULL) - : ptr_container_identity(sizeof(container), &df::allocator_fn, item, ienum) - {}; - - const std::string getFullName(const type_identity *item) const override { - return "vector" + ptr_container_identity::getFullName(item); - } - - virtual DFHack::identity_type type() const override { return DFHack::IDTYPE_STL_PTR_VECTOR; } - - virtual bool resize(void *ptr, int size) const override { - (*(container*)ptr).resize(size); - return true; - } - virtual bool erase(void *ptr, int size) const override { - auto &ct = *(container*)ptr; - ct.erase(ct.begin()+size); - return true; - } - virtual bool insert(void *ptr, int idx, void *item) const override { - auto &ct = *(container*)ptr; - ct.insert(ct.begin()+idx, item); - return true; - } - - protected: - virtual int item_count(void *ptr, CountMode) const override { - return (int)((container*)ptr)->size(); - }; - virtual void *item_pointer(const type_identity *, void *ptr, int idx) const override { - return &(*(container*)ptr)[idx]; - } - }; - -// Due to export issues, this stuff can only work in the main dll -#ifdef BUILD_DFHACK_LIB - class buffer_container_identity : public container_identity { + /* + * Identity for statically-sized arrays (C arrays and std::array). + * Unlike other container identities the item count is fixed, + * so instances can also serve as the identity of ad-hoc buffers. + */ + class DFHACK_EXPORT buffer_container_identity : public container_identity { int size; public: @@ -377,6 +239,7 @@ namespace df size_t byte_size() const override { return getItemType()->byte_size()*size; } + using container_identity::getFullName; const std::string getFullName(const type_identity *item) const override; int getSize() const { return size; } @@ -390,236 +253,581 @@ namespace df return ((uint8_t*)ptr) + idx * item->byte_size(); } }; -#endif - - template - class stl_container_identity : public container_identity { - const char *name; +} - public: - stl_container_identity(const char *name, const type_identity *item, const enum_identity *ienum = NULL) - : container_identity(sizeof(T), &allocator_fn, item, ienum), name(name) - {} +namespace DFHack +{ + namespace detail + { + /* + * Compile-time type inspection used by type_identity_for to pick + * the appropriate behavior for the wrapped C++ type. + */ - const std::string getFullName(const type_identity *item) const override { - return name + container_identity::getFullName(item); - } + template struct is_std_array : std::false_type {}; + template struct is_std_array> : std::true_type {}; - virtual bool resize(void *ptr, int size) const override { - (*(T*)ptr).resize(size); - return true; - } - virtual bool erase(void *ptr, int size) const override { - auto &ct = *(T*)ptr; - ct.erase(ct.begin()+size); - return true; - } - virtual bool insert(void *ptr, int idx, void *item) const override { - auto &ct = *(T*)ptr; - ct.insert(ct.begin()+idx, *(typename T::value_type*)item); - return true; - } - virtual bool lua_insert2(lua_State* state, int fname_idx, void* ptr, int idx, int val_index) const override - { - using VT = typename T::value_type; - VT tmp{}; - auto id = (type_identity*)lua_touserdata(state, DFHack::LuaWrapper::UPVAL_ITEM_ID); - auto pitem = DFHack::LuaWrapper::get_object_internal(state, id, val_index, false); - bool useTemporary = (!pitem && id->isPrimitive()); - - if (useTemporary) - { - pitem = &tmp; - id->lua_write(state, fname_idx, pitem, val_index); - } + template struct is_bit_array : std::false_type {}; + template struct is_bit_array> : std::true_type {}; - if (id != item || !pitem) - DFHack::LuaWrapper::field_error(state, fname_idx, "incompatible object type", "insert"); + template struct is_enum_field : std::false_type {}; + template struct is_enum_field> : std::true_type {}; - return insert(ptr, idx, pitem); - } + // C strings are represented by char* and const char* + template + concept c_string = std::is_same_v || std::is_same_v; - protected: - virtual int item_count(void *ptr, CountMode) const override { return (int)((T*)ptr)->size(); } - virtual void *item_pointer(const type_identity *item, void *ptr, int idx) const override { - return &(*(T*)ptr)[idx]; - } - }; + /* + * Container capability probes. These describe the behavioral axes + * that the container identity implementation dispatches on, so any + * type with a vector-like, array-like, bit-like, or associative + * interface is handled without being listed explicitly. + */ + template + concept has_value_type = requires { typename T::value_type; }; + + // element count is available either as size() or as a size member + template + concept sized = requires(const T& t) { { t.size() } -> std::convertible_to; } + || requires(const T& t) { { t.size } -> std::convertible_to; }; + + // dense random-access storage: t[i] yields a reference whose + // address is a value_type* (the vector/array contract) + template + concept random_access = has_value_type && sized && requires(T& t, int i) { + { &t[i] } -> std::convertible_to; + }; -#ifdef BUILD_DFHACK_LIB - template - class ro_stl_container_identity : public container_identity { - protected: - const char *name; + // indexable bit elements through a proxy object (vector and similar) + template + concept bit_indexable = has_value_type && sized && !random_access && + requires(T& t, int i, bool v) { + { t[i] } -> std::convertible_to; + t[i] = v; + }; + + // bit elements through explicit accessors; by convention such + // containers report byte counts from size() and resize() (BitArray). + // BitArray indexes by the enum type and is matched by name. + template + concept bit_accessors = is_bit_array::value || + (sized && requires(T& t, int i, bool v) { + { t.is_set(i) } -> std::convertible_to; + t.set(i, v); + }); + + // key-mapped iteration (std::map, std::unordered_map, and similar) + template + concept mapped = has_value_type && requires { typename T::mapped_type; } && + requires(T& t) { t.begin(); t.end(); }; + + // iterable but not index-addressable: read-only sequential access + // (std::set and similar) + template + concept ro_sequence = has_value_type && sized && + requires(T& t) { t.begin(); t.end(); } && + !random_access && !bit_indexable && !mapped; + + // an aggregate exposing a fixed { size, items[] } table (enum_list_attr) + template + concept item_list = sized && requires(const T& t, int i) { t.items[i]; }; + + // bit containers of either flavor; mapped types that happen to + // satisfy the proxy probes (e.g. map) are excluded + template + concept bit_container = (bit_indexable || bit_accessors) && !mapped; + + // sequence containers with mutable, index-addressable elements + template + concept seq_container = random_access && + !std::is_pointer_v; + + // containers of pointer elements use pointer-container semantics + // and are stored internally as containers of void* + template + concept ptr_container = random_access && + std::is_pointer_v; + + // read-only containers accessed by iteration + template + concept assoc_container = ro_sequence || mapped; + + template + concept any_container = seq_container || ptr_container || + bit_container || assoc_container || item_list; + + // mutable-sequence operations + template + concept resizable = requires(T& t, int n) { t.resize(n); }; + + template + concept index_erasable = requires(T& t, int i) { t.erase(t.begin() + i); }; + + template + concept index_insertable = has_value_type && + requires(T& t, int i, typename T::value_type v) { t.insert(t.begin() + i, v); }; + + template + concept stl_string = std::is_same_v; + + template + concept fs_path = std::is_same_v; + + // df::bitfield_traits exists only for generated bitfield types + template + concept df_bitfield = requires { typename df::bitfield_traits::base_type; }; + + // generated compound types (struct/union/class) carry a static + // _identity member + template + concept has_identity_member = requires { T::_identity; }; + + // DfOtherVectors marks its descendants with a nested typedef + template + concept other_vectors = requires { typename T::dfhack_other_vectors; }; + + // an explicit df_identity_base typedef always wins; this lets the + // code generator (and hand-written types) pick the base directly + template + concept has_explicit_base = requires { typename T::df_identity_base; }; + + // The underlying storage actually manipulated by a container identity. + // Pointer-element containers are manipulated as containers of void*, + // and BitArray is assumed layout-equivalent to BitArray, + // matching the assumptions of the original code. + template struct container_storage { using type = T; }; + template struct container_storage> { using type = BitArray; }; + template class C, typename E, typename... A> + requires (sizeof(C) > 0) + struct container_storage> { using type = C; }; + template using container_storage_t = typename container_storage::type; + + template struct base_tag { using type = B; }; - public: - ro_stl_container_identity(const char *name, const type_identity *item, const enum_identity *ienum = NULL) - : container_identity(sizeof(T), &allocator_fn, item, ienum), name(name) - {} + /* + * The intermediate base for container-like T; implements the + * container_identity virtuals using constexpr dispatch on T. + */ + template + class container_impl : public std::conditional_t< + bit_container, bit_container_identity, + std::conditional_t, ptr_container_identity, container_identity>> { + using cbase = std::conditional_t< + bit_container, bit_container_identity, + std::conditional_t, ptr_container_identity, container_identity>>; + using storage = container_storage_t; + + const char *name; + const type_identity *key_id; + + // item tables like enum_list_attr describe static external + // data and have no allocator + static constexpr TAllocateFn alloc_fn() { + if constexpr (item_list) + return NULL; + else + return &df::allocator_fn; + } - const std::string getFullName(const type_identity *item) const override { - return name + container_identity::getFullName(item); - } + static size_t element_count(const storage &ct) { + if constexpr (requires { { ct.size() } -> std::convertible_to; }) + return (size_t)ct.size(); + else + return (size_t)ct.size; + } - virtual bool is_readonly() const override { return true; } - virtual bool resize(void *ptr, int size) const override { return false; } - virtual bool erase(void *ptr, int size) const override { return false; } - virtual bool insert(void *ptr, int idx, void *item) const override { return false; } + public: + // sequence and read-only containers + container_impl(const char *name, const type_identity *item, const enum_identity *ienum = NULL) + requires (!bit_container && !mapped) + : cbase(sizeof(storage), alloc_fn(), item, ienum), name(name), key_id(NULL) + {} + + // bit containers take an index enum instead of an item identity + container_impl(const char *name, const enum_identity *ienum) + requires bit_container + : cbase(sizeof(storage), alloc_fn(), ienum), name(name), key_id(NULL) + {} + + // mapped containers additionally take a key identity + container_impl(const char *name, const type_identity *key, const type_identity *item) + requires mapped + : cbase(sizeof(storage), alloc_fn(), item, NULL), name(name), key_id(key) + {} + + const std::string getFullName() const override { return getFullName(this->item); } + + const std::string getFullName(const type_identity *item) const override { + if constexpr (mapped) + return std::string(name) + "<" + key_id->getFullName() + ", " + item->getFullName() + ">"; + else if constexpr (is_bit_array::value) + return "BitArray<>"; + else + return std::string(name) + cbase::getFullName(item); + } - protected: - virtual int item_count(void *ptr, CountMode) const override { return (int)((T*)ptr)->size(); } - virtual void *item_pointer(const type_identity *item, void *ptr, int idx) const override { - auto iter = (*(T*)ptr).begin(); - for (; idx > 0; idx--) ++iter; - return (void*)&*iter; - } - }; + virtual bool is_readonly() const override { + return assoc_container; + } - template - class ro_stl_assoc_container_identity : public ro_stl_container_identity { - const type_identity *key_identity; - const type_identity *item_identity; + virtual bool resize(void *ptr, int size) const override { + if constexpr (bit_accessors) { + ((storage*)ptr)->resize((size+7)/8); + return true; + } + else if constexpr (resizable) { + (*(storage*)ptr).resize(size); + return true; + } + else + return false; + } + virtual bool erase(void *ptr, int index) const override { + if constexpr (index_erasable) { + auto &ct = *(storage*)ptr; + ct.erase(ct.begin()+index); + return true; + } + else + return false; + } + virtual bool insert(void *ptr, int index, void *pitem) const override { + if constexpr (ptr_container && index_insertable) { + auto &ct = *(storage*)ptr; + ct.insert(ct.begin()+index, pitem); + return true; + } + else if constexpr (!bit_container && index_insertable) { + auto &ct = *(storage*)ptr; + ct.insert(ct.begin()+index, *(typename T::value_type*)pitem); + return true; + } + else + return false; + } + virtual bool lua_insert2(lua_State* state, int fname_idx, void* ptr, int idx, int val_index) const override { + if constexpr (seq_container && index_insertable) { + using VT = typename T::value_type; + VT tmp{}; + auto id = (type_identity*)lua_touserdata(state, DFHack::LuaWrapper::UPVAL_ITEM_ID); + auto pitem = DFHack::LuaWrapper::get_object_internal(state, id, val_index, false); + bool useTemporary = (!pitem && id->isPrimitive()); + + if (useTemporary) + { + pitem = &tmp; + id->lua_write(state, fname_idx, pitem, val_index); + } + + if (id != this->item || !pitem) + DFHack::LuaWrapper::field_error(state, fname_idx, "incompatible object type", "insert"); + + return insert(ptr, idx, pitem); + } + else + return cbase::lua_insert2(state, fname_idx, ptr, idx, val_index); + } - public: - ro_stl_assoc_container_identity(const char *name, const type_identity *key, const type_identity *item) - : ro_stl_container_identity(name, item), - key_identity(key), - item_identity(item) - {} + protected: + virtual int item_count(void *ptr, container_identity::CountMode cnt) const override { + if constexpr (bit_accessors) + return cnt == container_identity::COUNT_LEN ? (int)(element_count(*(storage*)ptr) * 8) : -1; + else if constexpr (item_list) + return cnt == container_identity::COUNT_WRITE ? 0 : (int)element_count(*(storage*)ptr); + else + return (int)element_count(*(storage*)ptr); + } + virtual void *item_pointer(const type_identity *item, void *ptr, int idx) const override { + if constexpr (bit_container) + return NULL; + else if constexpr (item_list) + return (void*)&((storage*)ptr)->items[idx]; + else if constexpr (mapped) { + auto iter = (*(storage*)ptr).begin(); + for (; idx > 0; idx--) ++iter; + return (void*)&iter->second; + } + else if constexpr (ro_sequence) { + auto iter = (*(storage*)ptr).begin(); + for (; idx > 0; idx--) ++iter; + return (void*)&*iter; + } + else + return &(*(storage*)ptr)[idx]; + } + // get_item/set_item only exist in bit_container_identity; for + // other bases these are ordinary (unused) member functions, and + // they implicitly override the virtuals for bit containers. + bool get_item(void *ptr, int idx) const { + if constexpr (bit_accessors) + return ((storage*)ptr)->is_set(idx); + else if constexpr (bit_indexable) + return (*(storage*)ptr)[idx]; + else + return false; + } + void set_item(void *ptr, int idx, bool val) const { + if constexpr (bit_accessors) + ((storage*)ptr)->set(idx, val); + else if constexpr (bit_indexable) + (*(storage*)ptr)[idx] = val; + } + }; - virtual const std::string getFullName(const type_identity*) const override { - return std::string(ro_stl_assoc_container_identity::name) + "<" + key_identity->getFullName() + ", " + item_identity->getFullName() + ">"; + template + constexpr auto select_identity_base() { + if constexpr (has_explicit_base) + return base_tag{}; + else if constexpr (std::is_enum_v || is_enum_field::value) + return base_tag{}; + else if constexpr (df_bitfield) + return base_tag{}; + else if constexpr (has_identity_member) { + if constexpr (std::is_union_v) + return base_tag{}; + else if constexpr (other_vectors) + return base_tag{}; + else if constexpr (std::is_polymorphic_v) + return base_tag{}; + else + return base_tag{}; + } + else if constexpr (c_string) + return base_tag{}; + else if constexpr (std::is_same_v) + return base_tag{}; + else if constexpr (std::is_pointer_v) + return base_tag{}; + else if constexpr (std::is_arithmetic_v) + return base_tag{}; + else if constexpr (stl_string || fs_path) + return base_tag{}; + else if constexpr (std::is_array_v || is_std_array::value) + return base_tag{}; + else if constexpr (any_container) + return base_tag>{}; + else if constexpr (std::is_class_v) + return base_tag{}; + else + static_assert(!sizeof(T*), "type_identity_for: no identity category for this type"); } + } - protected: - virtual void *item_pointer(const type_identity *item, void *ptr, int idx) const override { - auto iter = (*(T*)ptr).begin(); - for (; idx > 0; idx--) ++iter; - return (void*)&iter->second; - } - }; + template + using identity_base_of_t = typename decltype(detail::select_identity_base())::type; + + DFHACK_EXPORT void build_global_metatable(lua_State *state, const struct_identity *id); + DFHACK_EXPORT void lua_read_path(lua_State *state, void *ptr); + DFHACK_EXPORT void lua_write_path(lua_State *state, int fname_idx, void *ptr, int val_index); + + /* + * The identity of the C++ type T. The base class is selected by + * compile-time inspection of T (or an explicit df_identity_base + * typedef in T), and the remaining behavior is implemented here + * with constexpr dispatch on the properties of T. + */ + template + class type_identity_for : public identity_base_of_t { + using base = identity_base_of_t; - class bit_array_identity : public bit_container_identity { public: - /* - * This class assumes that BitArray is equivalent - * in layout and behavior to BitArray for any T. - */ - - using container = BitArray; - - bit_array_identity(const enum_identity *ienum = NULL) - : bit_container_identity(sizeof(container), &allocator_fn, ienum) - {} - - virtual const std::string getFullName(const type_identity *item) const override { - return "BitArray<>"; + // forwards everything to the base class constructor; covers all + // compound identities, arrays, and opaque types + template + requires (sizeof...(Args) > 0 && std::is_constructible_v) + explicit type_identity_for(Args&&... args) : base(std::forward(args)...) {} + + // numbers take only a name; the size comes from T + explicit type_identity_for(const char *name) + requires std::is_same_v + : base(sizeof(T), name) {} + + // C strings + type_identity_for() + requires detail::c_string + : base(sizeof(T)) {} + + // std::string and std::filesystem::path + type_identity_for() + requires (detail::stl_string || detail::fs_path) + : base(sizeof(T), &df::allocator_fn) {} + + // pointers derive the target from identity_traits, or take an + // explicit target (used for void*) + type_identity_for() + requires (std::is_pointer_v && std::is_same_v) + : base(df::identity_traits>::get()) {} + + // named sequence/set containers + type_identity_for(const char *name, const type_identity *item, const enum_identity *ienum = NULL) + requires (std::is_same_v> && + !detail::bit_container && !detail::mapped) + : base(name, item, ienum) {} + + // named mapped containers additionally take a key identity + type_identity_for(const char *name, const type_identity *key, const type_identity *item) + requires detail::mapped + : base(name, key, item) {} + + // containers of pointer elements + type_identity_for(const type_identity *item = NULL, const enum_identity *ienum = NULL) + requires detail::ptr_container + : base("vector", item, ienum) {} + + // std::vector and BitArray + type_identity_for(const enum_identity *ienum = NULL) + requires detail::bit_container + : base("vector", ienum) {} + + // the global object wraps no actual type + explicit type_identity_for(const struct_field_info *fields) + requires std::is_same_v + : base(0, NULL, NULL, "global", NULL, fields) {} + + virtual identity_type type() const override { + if constexpr (detail::stl_string || detail::fs_path) + return IDTYPE_PRIMITIVE; + else if constexpr (detail::ptr_container) + return IDTYPE_STL_PTR_VECTOR; + else if constexpr (std::is_same_v) + return IDTYPE_GLOBAL; + else + return base::type(); } - virtual bool resize(void *ptr, int size) const override { - ((container*)ptr)->resize((size+7)/8); - return true; + virtual const std::string getFullName() const override { + if constexpr (detail::c_string) + return "char*"; + else if constexpr (detail::stl_string) + return "string"; + else if constexpr (detail::fs_path) + return "path"; + else + return base::getFullName(); } - protected: - virtual int item_count(void *ptr, CountMode cnt) const override { - return cnt == COUNT_LEN ? ((container*)ptr)->size() * 8 : -1; + virtual bool isPrimitive() const override { + if constexpr (detail::stl_string || detail::fs_path) + return true; + else + return base::isPrimitive(); } - virtual bool get_item(void *ptr, int idx) const override { - return ((container*)ptr)->is_set(idx); - } - virtual void set_item(void *ptr, int idx, bool val) const override { - ((container*)ptr)->set(idx, val); - } - }; -#endif - - class DFHACK_EXPORT stl_bit_vector_identity : public bit_container_identity { - public: - using container = std::vector; - stl_bit_vector_identity(const enum_identity *ienum = NULL) - : bit_container_identity(sizeof(container), &df::allocator_fn, ienum) - {} - - const std::string getFullName(const type_identity *item) const override { - return "vector" + bit_container_identity::getFullName(item); + // isInteger only exists in number_identity_base; for other bases + // this is an ordinary member function, and it implicitly overrides + // the virtual for numbers. + bool isInteger() const { + return std::is_integral_v && !std::is_same_v; } - virtual bool resize(void *ptr, int size) const override { - (*(container*)ptr).resize(size); - return true; + virtual void lua_read(lua_State *state, int fname_idx, void *ptr) const override { + if constexpr (std::is_same_v) + lua_pushboolean(state, *(T*)ptr); + else if constexpr (std::is_floating_point_v) + lua_pushnumber(state, double(*(T*)ptr)); + else if constexpr (std::is_integral_v) + lua_pushinteger(state, int64_t(*(T*)ptr)); + else if constexpr (detail::c_string) { + auto pstr = *(T*)ptr; + if (pstr) + lua_pushstring(state, pstr); + else + lua_pushnil(state); + } + else if constexpr (detail::stl_string) { + auto pstr = (T*)ptr; + lua_pushlstring(state, pstr->data(), pstr->size()); + } + else if constexpr (detail::fs_path) + DFHack::lua_read_path(state, ptr); + else + base::lua_read(state, fname_idx, ptr); } - protected: - virtual int item_count(void *ptr, CountMode) const override { - return (int)((container*)ptr)->size(); - } - virtual bool get_item(void *ptr, int idx) const override { - return (*(container*)ptr)[idx]; - } - virtual void set_item(void *ptr, int idx, bool val) const override { - (*(container*)ptr)[idx] = val; - } - }; + virtual void lua_write(lua_State *state, int fname_idx, void *ptr, int val_index) const override { + if constexpr (std::is_same_v) { + char *pb = (char*)ptr; -#ifdef BUILD_DFHACK_LIB - template - class enum_list_attr_identity : public container_identity { - public: - using container = enum_list_attr; - - enum_list_attr_identity(const type_identity *item) - : container_identity(sizeof(container), NULL, item, NULL) - {} + if (lua_isboolean(state, val_index) || lua_isnil(state, val_index)) + *pb = lua_toboolean(state, val_index); + else if (lua_isnumber(state, val_index)) + *pb = lua_tointeger(state, val_index); + else + DFHack::LuaWrapper::field_error(state, fname_idx, "boolean or number expected", "write"); + } + else if constexpr (std::is_floating_point_v) { + if (!lua_isnumber(state, val_index)) + DFHack::LuaWrapper::field_error(state, fname_idx, "number expected", "write"); - const std::string getFullName(const type_identity *item) const override { - return "enum_list_attr" + container_identity::getFullName(item); + *(T*)ptr = T(lua_tonumber(state, val_index)); + } + else if constexpr (std::is_integral_v) { + int is_num = 0; + auto value = lua_tointegerx(state, val_index, &is_num); + if (!is_num) + DFHack::LuaWrapper::field_error(state, fname_idx, "integer expected", "write"); + *(T*)ptr = T(value); + } + else if constexpr (detail::c_string) + DFHack::LuaWrapper::field_error(state, fname_idx, "raw pointer string", "write"); + else if constexpr (detail::stl_string) { + size_t size; + const char *bytes = lua_tolstring(state, val_index, &size); + if (!bytes) + DFHack::LuaWrapper::field_error(state, fname_idx, "string expected", "write"); + + *(T*)ptr = std::string(bytes, size); + } + else if constexpr (detail::fs_path) + DFHack::lua_write_path(state, fname_idx, ptr, val_index); + else + base::lua_write(state, fname_idx, ptr, val_index); } - protected: - virtual int item_count(void *ptr, CountMode cm) const override { - return cm == COUNT_WRITE ? 0 : (int)((container*)ptr)->size; - } - virtual void *item_pointer(const type_identity *item, void *ptr, int idx) const override { - return (void*)&((container*)ptr)->items[idx]; + virtual void build_metatable(lua_State *state) const override { + if constexpr (std::is_same_v) + DFHack::build_global_metatable(state, this); + else + base::build_metatable(state); } }; -#endif +} + +namespace df +{ + using DFHack::type_identity_for; -#define NUMBER_IDENTITY_TRAITS(category, type) \ +#define NUMBER_IDENTITY_TRAITS(type) \ template<> struct DFHACK_EXPORT identity_traits { \ static const bool is_primitive = true; \ - static const category##_identity identity; \ - static const category##_identity_base *get() { return &identity; } \ + static const type_identity_for identity; \ + static const type_identity_for *get() { return &identity; } \ }; -#define INTEGER_IDENTITY_TRAITS(type) NUMBER_IDENTITY_TRAITS(integer, type) -#define FLOAT_IDENTITY_TRAITS(type) NUMBER_IDENTITY_TRAITS(float, type) - // the space after the use of "type" in OPAQUE_IDENTITY_TRAITS is _required_ // without it the macro generates a syntax error when type is a template specification #define OPAQUE_IDENTITY_TRAITS(...) \ template<> struct DFHACK_EXPORT identity_traits<__VA_ARGS__ > { \ - static const opaque_identity identity; \ - static const opaque_identity *get() { return &identity; } \ - }; - - INTEGER_IDENTITY_TRAITS(char); - INTEGER_IDENTITY_TRAITS(signed char); - INTEGER_IDENTITY_TRAITS(unsigned char); - INTEGER_IDENTITY_TRAITS(short); - INTEGER_IDENTITY_TRAITS(unsigned short); - INTEGER_IDENTITY_TRAITS(int); - INTEGER_IDENTITY_TRAITS(unsigned int); - INTEGER_IDENTITY_TRAITS(long); - INTEGER_IDENTITY_TRAITS(unsigned long); - INTEGER_IDENTITY_TRAITS(long long); - INTEGER_IDENTITY_TRAITS(unsigned long long); - INTEGER_IDENTITY_TRAITS(wchar_t); - FLOAT_IDENTITY_TRAITS(float); - FLOAT_IDENTITY_TRAITS(double); + static const type_identity_for<__VA_ARGS__ > identity; \ + static const type_identity_for<__VA_ARGS__ > *get() { return &identity; } \ + }; + + NUMBER_IDENTITY_TRAITS(char); + NUMBER_IDENTITY_TRAITS(signed char); + NUMBER_IDENTITY_TRAITS(unsigned char); + NUMBER_IDENTITY_TRAITS(short); + NUMBER_IDENTITY_TRAITS(unsigned short); + NUMBER_IDENTITY_TRAITS(int); + NUMBER_IDENTITY_TRAITS(unsigned int); + NUMBER_IDENTITY_TRAITS(long); + NUMBER_IDENTITY_TRAITS(unsigned long); + NUMBER_IDENTITY_TRAITS(long long); + NUMBER_IDENTITY_TRAITS(unsigned long long); + NUMBER_IDENTITY_TRAITS(wchar_t); + NUMBER_IDENTITY_TRAITS(float); + NUMBER_IDENTITY_TRAITS(double); + NUMBER_IDENTITY_TRAITS(bool); OPAQUE_IDENTITY_TRAITS(wchar_t*); OPAQUE_IDENTITY_TRAITS(std::condition_variable); OPAQUE_IDENTITY_TRAITS(std::fstream); @@ -646,54 +854,46 @@ namespace df }; #endif - template<> struct DFHACK_EXPORT identity_traits { - static const bool is_primitive = true; - static const bool_identity identity; - static const bool_identity *get() { return &identity; } - }; - template<> struct DFHACK_EXPORT identity_traits { static const bool is_primitive = true; - static const stl_string_identity identity; - static const stl_string_identity *get() { return &identity; } + static const type_identity_for identity; + static const type_identity_for *get() { return &identity; } }; template<> struct DFHACK_EXPORT identity_traits { static const bool is_primitive = true; - static const path_identity identity; - static const path_identity* get() { return &identity; } + static const type_identity_for identity; + static const type_identity_for* get() { return &identity; } }; template<> struct DFHACK_EXPORT identity_traits { static const bool is_primitive = true; - static const ptr_string_identity identity; - static const ptr_string_identity *get() { return &identity; } + static const type_identity_for identity; + static const type_identity_for *get() { return &identity; } }; template<> struct DFHACK_EXPORT identity_traits { static const bool is_primitive = true; - static const ptr_string_identity identity; - static const ptr_string_identity *get() { return &identity; } + static const type_identity_for identity; + static const type_identity_for *get() { return &identity; } }; template<> struct DFHACK_EXPORT identity_traits { static const bool is_primitive = true; - static const pointer_identity identity; - static const pointer_identity *get() { return &identity; } + static const type_identity_for identity; + static const type_identity_for *get() { return &identity; } }; template<> struct DFHACK_EXPORT identity_traits > { - static const stl_ptr_vector_identity identity; - static const stl_ptr_vector_identity *get() { return &identity; } + static const type_identity_for > identity; + static const type_identity_for > *get() { return &identity; } }; template<> struct DFHACK_EXPORT identity_traits > { - static const stl_bit_vector_identity identity; - static const stl_bit_vector_identity *get() { return &identity; } + static const type_identity_for > identity; + static const type_identity_for > *get() { return &identity; } }; #undef NUMBER_IDENTITY_TRAITS -#undef INTEGER_IDENTITY_TRAITS -#undef FLOAT_IDENTITY_TRAITS #undef OPAQUE_IDENTITY_TRAITS // Container declarations @@ -706,7 +906,7 @@ namespace df template struct identity_traits { static const bool is_primitive = true; - static const pointer_identity *get(); + static const pointer_identity_base *get(); }; #ifdef BUILD_DFHACK_LIB @@ -725,7 +925,7 @@ namespace df #endif template struct identity_traits > { - static const stl_ptr_vector_identity *get(); + static const ptr_container_identity *get(); }; // explicit specializations for these two types @@ -763,7 +963,7 @@ namespace df }; template<> struct identity_traits > { - static const bit_array_identity identity; + static const type_identity_for > identity; static const bit_container_identity *get() { return &identity; } }; @@ -785,54 +985,53 @@ namespace df #ifdef BUILD_DFHACK_LIB template inline const enum_identity *identity_traits >::get() { - static const enum_identity identity(identity_traits::get(), identity_traits::get()); + static const type_identity_for > identity(identity_traits::get(), identity_traits::get()); return &identity; } #endif template - inline const pointer_identity *identity_traits::get() { - static const pointer_identity identity(identity_traits::get()); + inline const pointer_identity_base *identity_traits::get() { + static const type_identity_for identity; return &identity; } #ifdef BUILD_DFHACK_LIB template inline const container_identity *identity_traits::get() { - static const buffer_container_identity identity(sz, identity_traits::get()); + static const type_identity_for identity(sz, identity_traits::get()); return &identity; } template inline const container_identity* identity_traits>::get() { - static const buffer_container_identity identity(sz, identity_traits::get()); + static const type_identity_for > identity(static_cast(sz), df::identity_traits::get()); return &identity; } template inline const container_identity *identity_traits >::get() { - using container = std::vector; - static const stl_container_identity identity("vector", identity_traits::get()); + static const type_identity_for > identity("vector", identity_traits::get()); return &identity; } #endif template - inline const stl_ptr_vector_identity *identity_traits >::get() { - static const stl_ptr_vector_identity identity(identity_traits::get()); + inline const ptr_container_identity *identity_traits >::get() { + static const type_identity_for > identity(identity_traits::get()); return &identity; } // explicit specializations for these two types // for availability in plugins - extern const DFHACK_EXPORT stl_container_identity > stl_vector_int32_t_identity; + extern const DFHACK_EXPORT type_identity_for > stl_vector_int32_t_identity; inline const container_identity* identity_traits >::get() { return &stl_vector_int32_t_identity; } - extern const DFHACK_EXPORT stl_container_identity > stl_vector_int16_t_identity; + extern const DFHACK_EXPORT type_identity_for > stl_vector_int16_t_identity; inline const container_identity* identity_traits >::get() { return &stl_vector_int16_t_identity; } @@ -840,56 +1039,50 @@ namespace df #ifdef BUILD_DFHACK_LIB template inline const container_identity *identity_traits >::get() { - using container = std::deque; - static const stl_container_identity identity("deque", identity_traits::get()); + static const type_identity_for > identity("deque", identity_traits::get()); return &identity; } template inline const container_identity *identity_traits >::get() { - using container = std::set; - static const ro_stl_container_identity identity("set", identity_traits::get()); + static const type_identity_for > identity("set", identity_traits::get()); return &identity; } template inline const container_identity* identity_traits >::get() { - using container = std::unordered_set; - static const ro_stl_container_identity identity("unordered_set", identity_traits::get()); + static const type_identity_for > identity("unordered_set", identity_traits::get()); return &identity; } template inline const container_identity *identity_traits>::get() { - using container = std::map; - static const ro_stl_assoc_container_identity identity("map", identity_traits::get(), identity_traits::get()); + static const type_identity_for > identity("map", identity_traits::get(), identity_traits::get()); return &identity; } template inline const container_identity *identity_traits>::get() { - using container = std::unordered_map; - static const ro_stl_assoc_container_identity identity("unordered_map", identity_traits::get(), identity_traits::get()); + static const type_identity_for > identity("unordered_map", identity_traits::get(), identity_traits::get()); return &identity; } template inline const bit_container_identity *identity_traits >::get() { - static const bit_array_identity identity(identity_traits::get()); + static const type_identity_for > identity(identity_traits::get()); return &identity; } template inline const container_identity *identity_traits >::get() { - using container = DfArray; - static const stl_container_identity identity("DfArray", identity_traits::get()); + static const type_identity_for > identity("DfArray", identity_traits::get()); return &identity; } template inline const container_identity *identity_traits >::get() { - static const enum_list_attr_identity identity(identity_traits::get()); + static const type_identity_for > identity("enum_list_attr", identity_traits::get()); return &identity; } #endif diff --git a/library/include/LuaTools.h b/library/include/LuaTools.h index 4e40789c4a..f929c562f3 100644 --- a/library/include/LuaTools.h +++ b/library/include/LuaTools.h @@ -38,6 +38,7 @@ distribution. #include "Core.h" #include "ColorText.h" #include "DataDefs.h" +#include "DataIdentity.h" #include "df/coord.h" #include "df/coord2d.h" diff --git a/library/include/LuaWrapper.h b/library/include/LuaWrapper.h index 258d4d254a..d47b8b6a01 100644 --- a/library/include/LuaWrapper.h +++ b/library/include/LuaWrapper.h @@ -139,13 +139,13 @@ namespace LuaWrapper { /** * Report an error while accessing a field (index = field name). */ - [[noreturn]] void field_error(lua_State *state, int index, const char *err, const char *mode); + [[noreturn]] DFHACK_EXPORT void field_error(lua_State *state, int index, const char *err, const char *mode); /* * If is_method is true, these use UPVAL_TYPETABLE to save a hash lookup. */ void push_object_internal(lua_State *state, const type_identity *type, void *ptr, bool in_method = true); - void *get_object_internal(lua_State *state, const type_identity *type, int val_index, bool exact_type, bool in_method = true); + DFHACK_EXPORT void *get_object_internal(lua_State *state, const type_identity *type, int val_index, bool exact_type, bool in_method = true); void push_adhoc_pointer(lua_State *state, void *ptr, const type_identity *target); diff --git a/library/xml b/library/xml index 3bfa5aa5ae..659eebd4f7 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 3bfa5aa5ae3fdbe4e77d22f8125b1823c5847ccc +Subproject commit 659eebd4f7789df2c33a85461caf9f99bc8a5431 diff --git a/plugins/devel/check-structures-sanity/dispatch.cpp b/plugins/devel/check-structures-sanity/dispatch.cpp index a013048e8c..200db9f326 100644 --- a/plugins/devel/check-structures-sanity/dispatch.cpp +++ b/plugins/devel/check-structures-sanity/dispatch.cpp @@ -303,14 +303,12 @@ void Checker::dispatch_primitive(const QueueItem & item, const CheckedStructure FAIL("invalid value for bool: " << int(val)); } } - else if (dynamic_cast(cs.identity)) + else if (auto num_identity = dynamic_cast(cs.identity)) { - check_possible_pointer(item, cs); + if (num_identity->isInteger()) + check_possible_pointer(item, cs); // TODO check ints? - } - else if (dynamic_cast(cs.identity)) - { // TODO check floats? } else @@ -336,7 +334,7 @@ void Checker::dispatch_pointer(const QueueItem & item, const CheckedStructure & } QueueItem target_item(item.path, target_ptr); - auto target = static_cast(cs.identity)->getTarget(); + auto target = static_cast(cs.identity)->getTarget(); if (!target) { check_unknown_pointer(target_item); diff --git a/plugins/devel/check-structures-sanity/types.cpp b/plugins/devel/check-structures-sanity/types.cpp index 4c8a8f6ae2..84257a6ef7 100644 --- a/plugins/devel/check-structures-sanity/types.cpp +++ b/plugins/devel/check-structures-sanity/types.cpp @@ -156,24 +156,24 @@ bool CheckedStructure::has_type_at_offset(const CheckedStructure & type, size_t const type_identity *Checker::wrap_in_stl_ptr_vector(const type_identity *base) { - static std::map> wrappers; + static std::map>>> wrappers; auto it = wrappers.find(base); if (it != wrappers.end()) { return it->second.get(); } - return (wrappers[base] = std::make_unique(base, nullptr)).get(); + return (wrappers[base] = std::make_unique>>(base, nullptr)).get(); } const type_identity *Checker::wrap_in_pointer(const type_identity *base) { - static std::map> wrappers; + static std::map> wrappers; auto it = wrappers.find(base); if (it != wrappers.end()) { return it->second.get(); } - return (wrappers[base] = std::make_unique(base)).get(); + return (wrappers[base] = std::make_unique(base)).get(); } std::map> known_types_by_size;