diff --git a/docs/source/usertypes.rst b/docs/source/usertypes.rst index 89bf1d36..081be5ed 100644 --- a/docs/source/usertypes.rst +++ b/docs/source/usertypes.rst @@ -23,4 +23,7 @@ The examples folder also has a number of really great examples for you to see. T * Member methods, properties, variables and functions taking ``self&`` arguments modify data directly - Work on a copy by taking or returning a copy by value. * The actual metatable associated with the usertype has a long name and is defined to be opaque by the Sol implementation. -* Containers get pushed as special usertypes, but can be disabled if problems arising as detailed :doc:`here`. \ No newline at end of file +* Containers get pushed as special usertypes, but can be disabled if problems arising as detailed :doc:`here`. +* You can use bitfields but it requires some finesse on your part. We have an example to help you get started `here that uses a few tricks`_. + +.. _here that uses a few tricks: https://github.com/ThePhD/sol2/blob/develop/examples/usertype_bitfields.cpp diff --git a/examples/usertype_bitfields.cpp b/examples/usertype_bitfields.cpp new file mode 100644 index 00000000..271045ba --- /dev/null +++ b/examples/usertype_bitfields.cpp @@ -0,0 +1,164 @@ +#define SOL_CHECK_ARGUMENTS +#include + +#include +#include +#include +#include + +namespace itsy_bitsy { + + template + struct bit_type { + typedef uint64_t type; + }; + + template + struct bit_type> { + typedef bool type; + }; + + template + struct bit_type 2 && sz <= 16)>> { + typedef uint16_t type; + }; + + template + struct bit_type 16 && sz <= 32)>> { + typedef uint32_t type; + }; + + template + struct bit_type 32 && sz <= 64)>> { + typedef uint64_t type; + }; + + template + using bit_type_t = typename bit_type::type; + + template + bool vcxx_warning_crap(std::true_type, V val) { + return val != 0; + } + + template + T vcxx_warning_crap(std::false_type, V val) { + return static_cast(val); + } + + template + auto vcxx_warning_crap(V val) { + return vcxx_warning_crap(std::is_same(), val); + } + + template + void write(Base& b, bit_type_t bits) { + typedef bit_type_t aligned_type; + static const std::size_t aligned_type_bit_size = sizeof(aligned_type) * CHAR_BIT; + static_assert(sizeof(Base) * CHAR_BIT >= (bit_target + size), "bit offset and size are too large for the desired structure."); + static_assert((bit_target % aligned_type_bit_size) <= ((bit_target + size) % aligned_type_bit_size), "bit offset and size cross beyond largest integral constant boundary."); + + const std::size_t aligned_target = (bit_target + size) / aligned_type_bit_size; + const aligned_type bits_left = static_cast(bit_target - aligned_target); + const aligned_type shifted_mask = ((static_cast(1) << size) - 1) << bits_left; + const aligned_type compl_shifted_mask = ~shifted_mask; + // Jump by native size of a pointer to target + // then OR the bits + aligned_type* jumper = static_cast(static_cast(&b)); + jumper += aligned_target; + aligned_type& aligned = *jumper; + aligned &= compl_shifted_mask; + aligned |= (static_cast(bits) << bits_left); + } + + template + bit_type_t read(Base& b) { + typedef bit_type_t aligned_type; + typedef bit_type_t field_type; + static const std::size_t aligned_type_bit_size = sizeof(aligned_type) * CHAR_BIT; + static_assert(sizeof(Base) * CHAR_BIT >= (bit_target + size), "bit offset and size are too large for the desired structure."); + static_assert((bit_target % aligned_type_bit_size) <= ((bit_target + size) % aligned_type_bit_size), "bit offset and size cross beyond largest integral constant boundary."); + + const std::size_t aligned_target = (bit_target + size) / aligned_type_bit_size; + const aligned_type bits_left = static_cast(bit_target - aligned_target); + const aligned_type mask = (static_cast(1) << size) - 1; + // Jump by native size of a pointer to target + // then OR the bits + aligned_type* jumper = static_cast(static_cast(&b)); + jumper += aligned_target; + const aligned_type& aligned = *jumper; + aligned_type field_bits = (aligned >> bits_left) & mask; + field_type bits = vcxx_warning_crap(field_bits); + return bits; + } + +} + +#include +#include + +#if defined(_MSC_VER) || defined(__MINGW32__) +#pragma pack(1) +struct flags_t { +#else +struct __attribute__((packed, aligned(1))) flags_t { +#endif + uint8_t C : 1; + uint8_t N : 1; + uint8_t PV : 1; + uint8_t _3 : 1; + uint8_t H : 1; + uint8_t _5 : 1; + uint8_t Z : 1; + uint8_t S : 1; + uint16_t D : 14; +} flags{}; + +int main() { + std::cout << "=== usertype_bitfields example ===" << std::endl; +#ifdef __MINGW32__ + std::cout << "MinGW Detected, packing structs is broken in MinGW and this test may fail" << std::endl; +#endif + sol::state lua; + lua.open_libraries(); + + lua.new_usertype("flags_t", + "C", sol::property(itsy_bitsy::read, itsy_bitsy::write), + "N", sol::property(itsy_bitsy::read, itsy_bitsy::write), + "D", sol::property(itsy_bitsy::read, itsy_bitsy::write) + ); + + lua["f"] = std::ref(flags); + + lua.script(R"( + print(f.C) + f.C = true; + print(f.C) + + print(f.N) + f.N = true; + print(f.N) + + print(f.D) + f.D = 0xDF; + print(f.D) +)"); + + bool C = flags.C; + bool N = flags.N; + uint16_t D = flags.D; + + std::cout << std::hex; + std::cout << "sizeof(flags): " << sizeof(flags) << std::endl; + std::cout << "C: " << C << std::endl; + std::cout << "N: " << N << std::endl; + std::cout << "D: " << D << std::endl; + + assert(C); + assert(N); + assert(D == 0xDF); + + std::cout << std::endl; + + return 0; +} diff --git a/single/sol/sol.hpp b/single/sol/sol.hpp index f95963f6..f95f4777 100644 --- a/single/sol/sol.hpp +++ b/single/sol/sol.hpp @@ -20,8 +20,8 @@ // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // This file was generated with a script. -// Generated 2016-11-15 09:39:55.397287 UTC -// This header was generated with sol v2.15.1 (revision fe162b9) +// Generated 2016-11-16 03:43:41.504038 UTC +// This header was generated with sol v2.15.1 (revision 4aec055) // https://github.com/ThePhD/sol2 #ifndef SOL_SINGLE_INCLUDE_HPP @@ -6986,13 +6986,16 @@ namespace sol { return call_into_lua(returns_list(), args_list(), L, start, std::forward(fx), std::forward(fxargs)...); } - inline call_syntax get_call_syntax(lua_State* L, const std::string& key, int index = -2) { + inline call_syntax get_call_syntax(lua_State* L, const std::string& key, int index) { + if (lua_gettop(L) == 0) { + return call_syntax::dot; + } luaL_getmetatable(L, key.c_str()); auto pn = pop_n(L, 1); - if (lua_compare(L, -1, index, LUA_OPEQ) == 1) { - return call_syntax::colon; + if (lua_compare(L, -1, index, LUA_OPEQ) != 1) { + return call_syntax::dot; } - return call_syntax::dot; + return call_syntax::colon; } inline void script(lua_State* L, const std::string& code) { @@ -7882,7 +7885,7 @@ namespace sol { }; static int call(lua_State* L, F& f) { - call_syntax syntax = stack::get_call_syntax(L, &usertype_traits::user_metatable()[0]); + call_syntax syntax = stack::get_call_syntax(L, &usertype_traits::user_metatable()[0], 1); int syntaxval = static_cast(syntax); int argcount = lua_gettop(L) - syntaxval; return construct_match>...>(onmatch(), L, argcount, 1 + syntaxval, f); diff --git a/sol/call.hpp b/sol/call.hpp index ac5f67e7..abd9dd49 100644 --- a/sol/call.hpp +++ b/sol/call.hpp @@ -443,7 +443,7 @@ namespace sol { }; static int call(lua_State* L, F& f) { - call_syntax syntax = stack::get_call_syntax(L, &usertype_traits::user_metatable()[0]); + call_syntax syntax = stack::get_call_syntax(L, &usertype_traits::user_metatable()[0], 1); int syntaxval = static_cast(syntax); int argcount = lua_gettop(L) - syntaxval; return construct_match>...>(onmatch(), L, argcount, 1 + syntaxval, f); diff --git a/sol/stack.hpp b/sol/stack.hpp index 9d1ab8d7..83cc59bc 100644 --- a/sol/stack.hpp +++ b/sol/stack.hpp @@ -181,13 +181,16 @@ namespace sol { return call_into_lua(returns_list(), args_list(), L, start, std::forward(fx), std::forward(fxargs)...); } - inline call_syntax get_call_syntax(lua_State* L, const std::string& key, int index = -2) { + inline call_syntax get_call_syntax(lua_State* L, const std::string& key, int index) { + if (lua_gettop(L) == 0) { + return call_syntax::dot; + } luaL_getmetatable(L, key.c_str()); auto pn = pop_n(L, 1); - if (lua_compare(L, -1, index, LUA_OPEQ) == 1) { - return call_syntax::colon; + if (lua_compare(L, -1, index, LUA_OPEQ) != 1) { + return call_syntax::dot; } - return call_syntax::dot; + return call_syntax::colon; } inline void script(lua_State* L, const std::string& code) { diff --git a/test_usertypes.cpp b/test_usertypes.cpp index 8d5d6c01..d23d45c1 100644 --- a/test_usertypes.cpp +++ b/test_usertypes.cpp @@ -1423,3 +1423,26 @@ TEST_CASE("usertype/destruction-test", "make sure usertypes are properly destruc lua["testCrash"](); } } + +TEST_CASE("usertype/call-initializers", "Ensure call constructors with initializers work well") { + struct A { + double f = 25.5; + + static void init(A& x, double f) { + x.f = f; + } + }; + + sol::state lua; + lua.open_libraries(); + + lua.new_usertype("A", + sol::call_constructor, sol::initializers(&A::init) + ); + + lua.script(R"( +a = A(24.3) +)"); + A& a = lua["a"]; + REQUIRE(a.f == 24.3); +}