ThePhD / sol2

Sol3 (sol2 v3.0) - a C++ <-> Lua API wrapper with advanced features and top notch performance - is here, and it's great! Documentation:
http://sol2.rtfd.io/
MIT License
4.2k stars 516 forks source link

Overloading of the same C++ operator results in failure #1407

Closed rleigh-codelibre closed 2 years ago

rleigh-codelibre commented 2 years ago

I'm attempting to wrap the C++ GLM matrix and vector types to make them accessible within Lua. Compiler is from VS2022 via CLion, no special options used other than warnings.

#include <sol/sol.hpp>

#define GLM_FORCE_RADIANS
#include <glm/glm.hpp>
#include <glm/gtx/string_cast.hpp>
#include <glm/gtc/matrix_transform.hpp>

void init_lua()
{
  sol::state lua;

  lua.open_libraries(sol::lib::base,
                     sol::lib::package,
                     sol::lib::math,
                     sol::lib::debug,
                     sol::lib::string,
                     sol::lib::table);

  lua.new_usertype<glm::mat3>(
      "mat3",
      sol::constructors<glm::mat3(),
                        glm::mat3(float const&),
                        glm::mat3(float const&, float const&, float const&,
                                  float const&, float const&, float const&,
                                  float const&, float const&, float const&)>(),
      sol::meta_function::multiplication, sol::resolve<glm::mat3 (glm::mat3 const&, glm::mat3 const&)>(&glm::operator*),
      sol::meta_function::addition, sol::resolve<glm::mat3 (glm::mat3 const&, glm::mat3 const&)>(&glm::operator+),
      sol::meta_function::subtraction, sol::resolve<glm::mat3 (glm::mat3 const&, glm::mat3 const&)>(&glm::operator-)
  );

  lua.new_usertype<glm::vec1>(
      "vec1",
      sol::constructors<glm::vec1(),
                        glm::vec1(float const&)>(),
      sol::meta_function::multiplication, sol::resolve<glm::vec1 (glm::vec1 const&, glm::vec1 const&)>(&glm::operator*),
      sol::meta_function::multiplication, sol::resolve<glm::vec1 (glm::vec1 const&, float)>(&glm::operator*),
      sol::meta_function::division, sol::resolve<glm::vec1 (glm::vec1 const&, glm::vec1 const&)>(&glm::operator/),
      sol::meta_function::addition, sol::resolve<glm::vec1 (glm::vec1 const&, glm::vec1 const&)>(&glm::operator+),
      sol::meta_function::subtraction, sol::resolve<glm::vec1 (glm::vec1 const&, glm::vec1 const&)>(&glm::operator-)
  );

  lua["vec"] = glm::vec3{2.0, 3.1, 82.3};
  lua.script("print(vec)");

  lua.script("print(vec1.new(5.5))\n"
             "-- print(vec1.new(5.5) * vec1.new(2.1))\n"
             "print(vec1.new(5.5) * 4.1)");

}

The wrapping of all of the matrix types (mat2, mat3 and mat4) works fine, as does wrapping of the vector types (vec1, vec2, vec3 and vec4). I've hit a problem when I tried to overload the vec1 operators for both vector and scalar variants. It compiles fine, but fails at runtime. With the above:

vec3(2.000000, 3.100000, 82.300003)
vec1(5.500000)
vec1(22.549999)

but if I uncomment the print(vec1.new(5.5) * vec1.new(2.1)) line:

vec3(2.000000, 3.100000, 82.300003)
vec1(5.500000)
[sol2] An error occurred and has been passed to an error handler: sol: runtime error: stack index 2, expected number, re
ceived sol.glm::vec<1,float,0>: not a numeric type (bad argument into 'glm::vec<1,float,0>(const glm::vec<1,float,0>&, f
loat)')
stack traceback:
        [C]: in metamethod 'mul'
        [string "print(vec1.new(5.5))..."]:2: in main chunk
sol: runtime error: stack index 2, expected number, received sol.glm::vec<1,float,0>: not a numeric type (bad argument i
nto 'glm::vec<1,float,0>(const glm::vec<1,float,0>&, float)')
stack traceback:
        [C]: in metamethod 'mul'
        [string "print(vec1.new(5.5))..."]:2: in main chunk

So it's using the "float" variant rather than the "vec1" variant. If I reorder the meta-functions:

      sol::meta_function::multiplication, sol::resolve<glm::vec1 (glm::vec1 const&, float)>(&glm::operator*),
      sol::meta_function::multiplication, sol::resolve<glm::vec1 (glm::vec1 const&, glm::vec1 const&)>(&glm::operator*),

I see:

vec3(2.000000, 3.100000, 82.300003)
vec1(5.500000)
vec1(11.549999)
[sol2] An error occurred and has been passed to an error handler: sol: runtime error: stack index 2, expected userdata,
received number: value is not a valid userdata (bad argument into 'glm::vec<1,float,0>(const glm::vec<1,float,0>&, const
 glm::vec<1,float,0>&)')
stack traceback:
        [C]: in metamethod 'mul'
        [string "print(vec1.new(5.5))..."]:3: in main chunk
sol: runtime error: stack index 2, expected userdata, received number: value is not a valid userdata (bad argument into
'glm::vec<1,float,0>(const glm::vec<1,float,0>&, const glm::vec<1,float,0>&)')
stack traceback:
        [C]: in metamethod 'mul'
        [string "print(vec1.new(5.5))..."]:3: in main chunk

So it's working fine for the vec1 case but not the float case. So it looks like in both cases, only one of the overloaded operators is being used, rather than it selecting the correct variant.

Is this something which is expected to work, or am I using sol2 incorrectly to register these overloads?

One other question: when you use lua.new_usertype<Type>, is it possible to add these to a table other than the root table so I can collect all of the library functions in a specific table?

Many thanks, Roger

rleigh-codelibre commented 2 years ago

This was primarily a misunderstanding on my part, I was missing the need for sol::overload, and I have got it working with:

  lua.new_usertype<glm::vec1>(
      "vec1",
      sol::constructors<glm::vec1(),
                        glm::vec1(float const&)>(),
      sol::meta_function::multiplication, sol::overload(sol::resolve<glm::vec1 (glm::vec1 const&, glm::vec1 const&)>(&glm::operator*), sol::resolve<glm::vec1 (glm::vec1 const&, float)>(&glm::operator*)),
      sol::meta_function::division, sol::resolve<glm::vec1 (glm::vec1 const&, glm::vec1 const&)>(&glm::operator/),
      sol::meta_function::addition, sol::resolve<glm::vec1 (glm::vec1 const&, glm::vec1 const&)>(&glm::operator+),
      sol::meta_function::subtraction, sol::resolve<glm::vec1 (glm::vec1 const&, glm::vec1 const&)>(&glm::operator-)
  );

which does the job nicely.

  lua.script("print(vec1.new(5.5))\n"
             "print(vec1.new(5.5) * 4.0)\n"
             "print(vec1.new(5.5) * vec1.new(2.1))");

==>

vec1(5.500000)
vec1(22.000000)
vec1(11.549999)