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.06k stars 492 forks source link

Emscripten/Clang error on setting usertype member variable #1565

Closed RobbinMarcus closed 6 months ago

RobbinMarcus commented 6 months ago

Compiling this simple example for emscripten:

#include "sol/sol.hpp"

class TestClass
{
public:
    void TestFunction() { }
    int TestValue = 0;
};

int main(int, char**)
{
    auto lua = sol::state();
    auto test = lua.new_usertype<TestClass>("test");
    test["testfunction"] = &TestClass::TestFunction;
    // This introduces the error:
    test["testvalue"] = &TestClass::TestValue;
    // This compiles fine:
    //test["testvalue"] = sol::property([](TestClass& c) { return c.TestValue; }, [](TestClass& c, int value) { c.TestValue = value; });
    return 0;
}

Results in the following error:

E:\Projects\emsdk\upstream\emscripten\em++.bat  -IE:/Projects/SolTest/lua54/lua-5.4.6/include -isystem E:/Projects/SolTest/sol2/include -frelaxed-template-template-args -g -std=c++17 -MD -MT CMakeFiles/SolTest.dir/source/main.cpp.o -MF CMakeFiles\SolTest.dir\source\main.cpp.o.d -o CMakeFiles/SolTest.dir/source/main.cpp.o -c E:/Projects/SolTest/source/main.cpp
In file included from E:/Projects/SolTest/source/main.cpp:1:
In file included from E:/Projects/SolTest/sol2/include\sol/sol.hpp:54:
In file included from E:/Projects/SolTest/sol2/include\sol/function.hpp:28:
In file included from E:/Projects/SolTest/sol2/include\sol/unsafe_function.hpp:31:
E:/Projects/SolTest/sol2/include\sol/function_types.hpp:110:31: error: address of overloaded function 'call' does not match required type 'int (lua_State *)'
  110 |                                 lua_CFunction freefunc = &function_detail::upvalue_this_member_variable<C, Fx>::template call<is_yielding, no_trampoline>;
      |                                                           ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
E:/Projects/SolTest/sol2/include\sol/function_types.hpp:268:5: note: in instantiation of function template specialization 'sol::function_detail::select_member_variable<false, false, int TestClass::*>' requested here
  268 |                                 select_member_variable<is_yielding, no_trampoline>(L, std::forward<Fx>(fx), std::forward<Args>(args)...);
      |                                 ^
E:/Projects/SolTest/sol2/include\sol/function_types.hpp:392:22: note: in instantiation of function template specialization 'sol::function_detail::select<false, false, int TestClass::*>' requested here
  392 |                                 function_detail::select<false, false>(L, std::forward<Args>(args)...);
      |                                                  ^
E:/Projects/SolTest/sol2/include\sol/stack_core.hpp:878:14: note: in instantiation of function template specialization 'sol::stack::unqualified_pusher<int TestClass::*>::push<int TestClass::*>' requested here
  878 |                                 return p.push(L, std::forward<T>(t), std::forward<Args>(args)...);
      |                                          ^
E:/Projects/SolTest/sol2/include\sol/stack_field.hpp:222:7: note: in instantiation of function template specialization 'sol::stack::push<int TestClass::*>' requested here
  222 |                                                 push(L, std::forward<Value>(value));
      |                                                 ^
E:/Projects/SolTest/sol2/include\sol/stack_core.hpp:1250:59: note: in instantiation of function template specialization 'sol::stack::field_setter<const char *>::set<const char *, int TestClass::*>' requested here
 1250 |                         field_setter<meta::unqualified_t<Key>, global, raw> {}.set(L, std::forward<Key>(key), std::forward<Value>(value), tableindex);
      |                                                                                ^
E:/Projects/SolTest/sol2/include\sol/table_core.hpp:264:14: note: (skipping 3 contexts in backtrace; use -ftemplate-backtrace-limit=0 to see all)
  264 |                                                 stack::set_field<global, raw>(L, std::forward<Key>(key), std::forward<Keys>(keys)..., table_index);
      |                                                        ^
E:/Projects/SolTest/sol2/include\sol/usertype.hpp:100:20: note: in instantiation of function template specialization 'sol::basic_table_core<false, sol::basic_reference<false>>::set<const char *, int TestClass::*>' requested here
  100 |                                         table_base_t::set(std::forward<Key>(key), std::forward<Value>(value));
      |                                                       ^
E:/Projects/SolTest/sol2/include\sol/usertype_proxy.hpp:64:9: note: in instantiation of function template specialization 'sol::basic_usertype<TestClass, sol::basic_reference<false>>::set<const char *, int TestClass::*>' requested here
   64 |                                 tbl.set(std::get<I>(std::move(key))..., std::forward<T>(value));
      |                                     ^
E:/Projects/SolTest/sol2/include\sol/usertype_proxy.hpp:86:21: note: in instantiation of function template specialization 'sol::usertype_proxy<sol::basic_usertype<TestClass, sol::basic_reference<false>> &, const char *>::tuple_set<0UL, int TestClass::*>' requested here
   86 |                         std::move(*this).tuple_set(idx_seq(), std::forward<T>(item));
      |                                          ^
E:/Projects/SolTest/sol2/include\sol/usertype_proxy.hpp:97:28: note: in instantiation of function template specialization 'sol::usertype_proxy<sol::basic_usertype<TestClass, sol::basic_reference<false>> &, const char *>::set<int TestClass::*>' requested here
   97 |                         return std::move(*this).set(std::forward<T>(other));
      |                                                 ^
E:/Projects/SolTest/source/main.cpp:15:23: note: in instantiation of function template specialization 'sol::usertype_proxy<sol::basic_usertype<TestClass, sol::basic_reference<false>> &, const char *>::operator=<int TestClass::*>' requested here
   15 |     test["testvalue"] = &TestClass::TestValue;
      |                       ^
E:/Projects/SolTest/sol2/include\sol/function_types_stateless.hpp:323:14: note: candidate template ignored: substitution failure [with is_yielding = false, no_trampoline = false]
  323 |                 static int call(lua_State* L) noexcept(std::is_nothrow_copy_assignable_v<T>) {
      |                            ^
1 error generated.
em++: error: 'E:/Projects/emsdk/upstream/bin\clang++.exe -target wasm32-unknown-emscripten -fignore-exceptions -fvisibility=default -mllvm -combiner-global-alias-analysis=false -mllvm -enable-emscripten-sjlj -mllvm -disable-lsr -DEMSCRIPTEN --sysroot=E:\Projects\emsdk\upstream\emscripten\cache\sysroot -Xclang -iwithsysroot/include\fakesdl -Xclang -iwithsysroot/include\compat -IE:/Projects/SolTest/lua54/lua-5.4.6/include -isystem E:/Projects/SolTest/sol2/include -frelaxed-template-template-args -g3 -std=c++17 -MD -MT CMakeFiles/SolTest.dir/source/main.cpp.o -MF CMakeFiles\SolTest.dir\source\main.cpp.o.d -c E:/Projects/SolTest/source/main.cpp -o CMakeFiles/SolTest.dir/source/main.cpp.o' failed (returned 1)
ninja: build stopped: subcommand failed.

I'm hoping you could help me out with some suggestions. I've tried the following:

Reproducing:

Combine them using this cmake file:

cmake_minimum_required(VERSION 3.12...3.25)
project(
    SolTest
    VERSION 0.1.0
    LANGUAGES CXX C
)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

add_subdirectory(lua54)
add_subdirectory(sol2)

add_executable(SolTest main.cpp)
target_link_libraries(SolTest PRIVATE lua_static sol2)

if (EMSCRIPTEN)
    # I tried setting relaxed template args compiler flag with no success
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -frelaxed-template-template-args")
endif()

Then build for emscripten (using their sdk for emcmake) using:

emcmake cmake . -B build-web
cmake --build build-web

EMCC version:

E:\Projects\SolTest>emcc -v
emcc (Emscripten gcc/clang-like replacement + linker emulating GNU ld) 3.1.48 (e967e20b4727956a30592165a3c1cde5c67fa0a8)
clang version 18.0.0 (https://github.com/llvm/llvm-project a54545ba6514802178cf7cf1c1dd9f7efbf3cde7)
Target: wasm32-unknown-emscripten
Thread model: posix
InstalledDir: E:\Projects\emsdk\upstream\bin

Edit: for anyone trying to use properties like above: the example compiles, but it's not functional. I have found no workaround other than polymorphism to add property functions to the type.

Rochet2 commented 6 months ago

@RobbinMarcus Have you had success with lua itself only (no sol)?

RobbinMarcus commented 6 months ago

Yes, running Emscripten with various distributions of Lua has worked out great so far! Specifically for this issue: Passing values back and forth using the global table (or tables on there) works. I also tested similar functionality using LuaBridge3 which was able to register members using the .addProperty("x", &class:x).

Rochet2 commented 6 months ago

Hmm, my guess is that the noexcept here https://github.com/ThePhD/sol2/blob/9c882a28fdb6f4ad79a53a4191b43ce48a661175/include/sol/function_types_stateless.hpp#L363C33-L363C41 is the problem.

However, if I understand correctly, it should not be a problem in Cpp17 (link). Emphasis mine.

Pointers (including pointers to member function) to non-throwing functions can be assigned to or used to initialize(until C++17)are implicitly convertible to(since C++17) pointers to potentially-throwing functions, but not the other way around.

So maybe it is a problem in the compiler toolchain not supporting this? Maybe you can just try casting or removing the noexcept to see how it works?

RobbinMarcus commented 6 months ago

Thank you so much for that! Removing the noexcept on lines 363 and 323 in function_types_stateless (both call functions) allowed it to return the correct function call for emcc, thus compiling once again on my side.

This solves my problem so I'll close this issue. I haven't run into any issues using any other Sol2 functionality so far on Emscripten (after some brief test runs), so if anyone is thinking about this combo be sure to give it a try with the changes mentioned above!