luaaa icon indicating copy to clipboard operation
luaaa copied to clipboard

cannot convert argument 2 from 'T' to 'T *' in the Lua Stack

Open flok opened this issue 1 year ago • 3 comments

Hey,

i'm trying to build a lua interface around a c++ header only library. the library consists of a derived and virtual classes.

i stumbled on the error:

cannot convert argument 2 from 'T' to 'T *' in the Lua Stack. image

I feel like i'm doing something wrong? Other question would be if i need to implement the abstract class as a LuaClass aswell or only the derived class are okay?

System: C++ 17 on Windows with the latest MSVC compiler on VS 2022

Minimal example:


class Test
{
public:
    Test() : m_test(100) {}

    uint64_t getTest() { return m_test; }

protected:
    uint64_t m_test;
};


extern "C" __declspec(dllexport) int RegisterWithLua(lua_State* L) {
    printf("RegisterWithLua called!\n");
    
    LuaClass<Test> test(L, "Test");
    test.ctor<>();
    test.fun("getTest", &Test::getTest);
    return 0;
} 

flok avatar Oct 14 '24 20:10 flok

Lua only supports a single integer type, which is defined by the LUA_INTEGER macro in the luaconf.h header during the compilation of Lua. Due to this limitation, special handling is required when dealing with integers that exceed the defined size of Lua's integer type. This can be achieved in two ways:

  1. Store large integers in Lua userdata. For more information, see this link: lua-int64.
  2. Alternatively, implement a 64-bit integer algorithm within Lua. For an example, refer to the second answer in this Stack Overflow thread: Does Lua make use of 64-bit integers?

However, the best approach is to directly modify the LUA_INTEGER definition within Lua to the desired integer size, such as int64. In fact, Lua already does this by default; LUA_INTEGER is set to ptrdiff_t, which is int32 in a 32-bit environment and int64 in a 64-bit environment.

Due to type limitations, distinguishing between 32-bit and 64-bit integers, as well as signed and unsigned types, in Lua is challenging and generally unnecessary. Similarly, restricting the integer types that interact with Lua in C++ can lead to overflow issues when converting Lua integers back to C++ types if the C++ types have a smaller range.

Therefore, in practice, it is common to define integers in the C++ to Lua interface without specifying their size or sign, using the largest possible range instead.

Consequently, in luaaa, only the import/export of the int type is defined to prevent misuse.

Nevertheless, if you absolutely must export int64_t/uint64_t, define the following methods before doing so:

template<>
struct LuaStack<int64_t>
{
    inline static int get(lua_State* L, int idx)
    {
        if (lua_isnumber(L, idx) || lua_isstring(L, idx))
        {
            return static_cast<int>(lua_tointeger(L, idx));
        }
        else
        {
            luaL_checktype(L, idx, LUA_TNUMBER);
        }
        return 0;
    }

    inline static void put(lua_State* L, const int64_t& t)
    {
        // Note: int64_t may not fit within the range of Lua's integer type.
        lua_pushinteger(L, static_cast<lua_Integer>(t));
    }
};

template<>
struct LuaStack<uint64_t>
{
    inline static int get(lua_State* L, int idx)
    {
        if (lua_isnumber(L, idx) || lua_isstring(L, idx))
        {
            return static_cast<int>(lua_tointeger(L, idx));
        }
        else
        {
            luaL_checktype(L, idx, LUA_TNUMBER);
        }
        return 0;
    }

    inline static void put(lua_State* L, const uint64_t& t)
    {
        // Note: uint64_t may not fit within the range of Lua's integer type.
        lua_pushinteger(L, static_cast<lua_Integer>(t));
    }
};

Please note that the lua_pushinteger function may not be able to handle 64-bit integers if they exceed the range of Lua's integer type. In such cases, you might need to use Lua userdata or the lua_pushnumber function with appropriate conversions to handle these values correctly.

gengyong avatar Oct 15 '24 18:10 gengyong

Thanks for the fast answer and the detailed explanation. And also thanks for the code!

With the Lua Stack templates my code at least compile, but calling functions of the library still throws errors. Two quick question regarding this:

I saw that lua also supports unsigned integer with lua_Unsigned but it seems there is no lua_pushunsigned. Do you know why? I would have suspected that the LuaStack for uint64_t or uint32_t (i added that myself) would then use the lua_Unsigned and the fitting lua_pushXXXX function?

I don't plan that my data will be bigger than lua's integer type. The library is just using 64 bit unsigned / signed data. Changing is sadly not an option as it's used in several places. So this should not be a problem but thanks for the reminder!

Last question: Are abstract classes supported and derivation for the c++ -> lua binding and if yes how would i go about implementing this? Also what about giving over a pointer to data (e.g. uint8* array that comes from lua (would this be defined as a normal table in lua like?

local raw_data = {0x01, 0x02, 0x03, 0x04}

Thanks agains for answering my question. Haven't had that much experience with lua and especially not with c++ -> lua

flok avatar Oct 15 '24 19:10 flok

Can we expand on this issue for general other types of data? I'm currently working with Raylib and trying to generate bindings for it, but am getting weird errors that I'm not sure how to work around.

LoadTexture for example returns a Texture object, but dispite having Texture class mapped in Luaaa, it throws the same type conversion error...

I have to work around this by wrapping the Raylib LoadTexture function into a class that holds the Texture2D object, and then map it as a class with Luaaa. This in turn means I have to wrap each function that Raylib uses the texture with in a function that dereferences the custom wrapping object and then finally call the native Raylib function.

example error:

In file included from main.cpp:17:
./lib/lua/luaaa.hpp: In instantiation of ‘void luaaa::LuaStackReturn(lua_State*, T) [with T = Texture; lua_State = lua_State]’:
./lib/lua/luaaa.hpp:797:1:   required from ‘static int luaaa::NonMemberFunctionCaller(TRET (*)(ARGS ...))::HelperClass::Invoke(lua_State*) [with TRET = Texture; ARGS = {const char*}; lua_State = lua_State]’
  734 |           LuaStackReturn<TRET>(state, LuaInvoke<TRET, FTYPE, ARGS...>(         \
      |           ~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  735 |                                           state, calleePtr, SKIPPARAM));       \
      |                                           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
./lib/lua/luaaa.hpp:797:1:   required from ‘int (* luaaa::NonMemberFunctionCaller(TRET (*)(ARGS ...)))(lua_State*) [with TRET = Texture; ARGS = {const char*}; lua_CFunction = int (*)(lua_State*)]’
  740 |     };                                                                         \
      |     ^
./lib/lua/luaaa.hpp:2176:65:   required from ‘luaaa::LuaModule& luaaa::LuaModule::_funImpl(const char*, F) [with F = Texture (*)(const char*)]’
 2176 |     return _registerModuleFunction(name, NonMemberFunctionCaller(f), f);
      |                                          ~~~~~~~~~~~~~~~~~~~~~~~^~~
./lib/lua/luaaa.hpp:2182:20:   required from ‘luaaa::LuaModule& luaaa::LuaModule::fun(const char*, FRET (*)(FARGS ...)) [with FRET = Texture; FARGS = {const char*}]’
 2182 |     return _funImpl(name, f);
      |            ~~~~~~~~^~~~~~~~~
main.cpp:61:13:   required from here
   52 |     LuaModule(L)
      |     ~~~~~~~~~~~~
   53 |         .fun("WindowShouldClose", WindowShouldClose)
      |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   54 |         .fun("ClearBackground", ClearBackground)
      |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   55 |         .fun("BeginDrawing", BeginDrawing)
      |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   56 |         .fun("EndDrawing", EndDrawing)
      |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   57 |         .fun("DrawTexture",
      |         ~~~~~~~~~~~~~~~~~~~
   58 |              [](lTexture2D &texture, int posX, int posY, Color tint) {
      |              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   59 |                DrawTexture(*texture, posX, posY, tint);
      |                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   60 |              })
      |              ~~
   61 |         .fun("LoadTexture", LoadTexture)
      |         ~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~
./lib/lua/luaaa.hpp:552:23: error: cannot convert ‘Texture’ to ‘Texture*’
  552 |   LuaStack<T>::put(L, t);
      |                       ^
      |                       |
      |                       Texture
./lib/lua/luaaa.hpp:400:43: note:   initializing argument 2 of ‘static void luaaa::LuaStack<T>::put(lua_State*, T*) [with T = Texture; lua_State = lua_State]’
  400 |   inline static void put(lua_State *L, T *t) { lua_pushlightuserdata(L, t); }
      |       

bit-garden avatar Jul 04 '25 18:07 bit-garden