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

const correctness of C++ object passed to sol::protected_function #1607

Open neel opened 1 month ago

neel commented 1 month ago

C++ arguments of sol::protected_function looses const-ness. The non-const functions of the C++ class can be called from the lua function.

Minimal reproducible example


class MyClass {
public:
    void modify() {
        // Modify the object
        std::cout << "modify" << std::endl;
    }
    void inspect() const {
        // Inspect the object without modifying it
        std::cout << "inspect" << std::endl;
    }
};

template <typename Data>
void run(sol::protected_function& view_fnc, const Data& data){
    sol::protected_function_result result = view_fnc(data);
}

int main(){

    sol::state lua;
    lua.open_libraries(sol::lib::base, sol::lib::string, sol::lib::math, sol::lib::utf8);

    lua.new_usertype<MyClass>("MyClass",
        "modify", &MyClass::modify,
        "inspect", &MyClass::inspect
    );

    static char buffer[] = R"TEMPLATE(
    return function(obj)
        obj:inspect()
        print("inspect called")
        obj:modify()
        print("modify called")
        return "hello"
    end
    )TEMPLATE";

    sol::load_result load_result = lua.load_buffer(buffer, strlen(buffer));
    sol::protected_function view =  load_result.get<sol::protected_function>();
    sol::protected_function_result view_result = view();
    sol::protected_function view_fnc = view_result;
    MyClass data;
    run(view_fnc, data);
    return 0;
}

Compiler:

$ /usr/bin/c++ --version c++ (GCC) 14.1.1 20240522

Workaround

There can be many workarounds. The two I am interested in are

Workaround 1

Make two lua bindings for same C++ class MyClass lets call one LMyClass and the other CLMyClass. The CLMyClass will not expose bind any non-const member functions. It will bind the mutable member variables a const properties using sol::readonly.

But the problem with this approach is How will I decide when it should use CLMyClass instead of LMyClass?

Workaround 2

I am trying to workaround it in a different way.

template <typename Class, bool Const>
struct type: public Class{
    static constexpr bool is_const = Const;
};

Then provide make two lua user types for type<X, true> and type<X, false> . The problem with this approach is, if class X has a member variable of type std::vector it has to be transformed to std::vector<type<Y, Const>> which is very difficult to do automatically without any usercode.