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.22k stars 517 forks source link

Add Constructors Separately #1361

Open dhkatz opened 2 years ago

dhkatz commented 2 years ago

Is it possible to add constructors at different times instead of all at once when registering the user type?

I was playing around with RTTR, since I plan to use the metadata for an editor later on, and it would be nice if I could also register classes in Lua using that same information.

Everything seems to work using a visitor pattern I found in an example, but RTTR visits each constructor separately.

For example, this is how I've been handling the constructors with Sol2 at the moment. This works with one constructor, but when there are multiple, they start overriding each other:

        template <typename T, typename... Args>
        void visit_constructor(const constructor_info<T>& info) {
            using Class = typename constructor_info<T>::declaring_type;

            auto table = info.ctor_item.get_declaring_type().get_name().to_string();

            sol::usertype<Class> usertype = lua.get<sol::usertype<Class>>(table);

            auto ctor = sol::constructors<T(Args...)>();

            usertype[sol::meta_function::construct] = ctor;
        }

My C++ knowledge might be a bit limited in terms of templates so I'm not really sure what I could do here.

One of my first thoughts was to start storing constructors in visit_type_begin because that's where I first register the type:

        template <typename T, typename... Bases>
        void visit_type_begin(const type_info<T>& info) {
            using Class = typename type_info<T>::declaring_type;

            auto name = info.type_item.get_name().to_string();

            lua.new_usertype<Class>(name);

            // Can I create a way to store constructors for later?
        }

and then maybe in visit_type_end to finally register them all at once?

        template <typename T, typename... Bases>
        void visit_type_end(const type_info<T>& info) {
            // Register constructors now that we know we've seen them all?
        }

Again, my knowledge of templates/generics in C++ is a bit limited so I'm not even sure this is possible because of type erasure when storing types and such.

I saw this project get around this by instead storing a type containing all the constructors on every class it expects to register with Sol2 but this seems like kind of a bandaid solution:

    struct position_2d : public math::vec2f
    {
        template <typename... Args>
        position_2d(Args&&... args) noexcept : math::vec2f(std::forward<Args>(args)...)
        {
        }

        position_2d() noexcept = default;

        position_2d(const position_2d& other) noexcept = default;

        position_2d& operator=(const position_2d& other) noexcept = default;

        position_2d(math::vec2f pos) noexcept : math::vec2f(pos)
        {
        }

        position_2d(float x, float y) noexcept : math::vec2f(x, y)
        {
        }

       // Storing the sol constructors here for later
        using constructors = sol::constructors<
            position_2d(),

            position_2d(math::vec2f pos), position_2d(float x, float y)

            >;
    };