Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Interoperability between std::any and sol::object #1035

Closed
cschreib opened this issue Oct 3, 2020 · 2 comments
Closed

Interoperability between std::any and sol::object #1035

cschreib opened this issue Oct 3, 2020 · 2 comments
Assignees
Milestone

Comments

@cschreib
Copy link
Contributor

cschreib commented Oct 3, 2020

I am currently porting over a project of mine from a custom Lua C++ wrapper to sol2. This custom wrapper used std::any to allow passing arbitrary values to and from Lua without knowing their explicit types. This was convenient, because my event dispatching system also uses std::any, so I could directly send the std::any I got form an event straight to Lua, and the wrapper would take care of dispatching the content of the std::any to the most appropriate Lua type. The implicit contract was that, in this case, the std::any was expected to only ever contain basic numeric types, booleans, and strings [*].

I think the closest equivalent in sol2 is sol::object, which fulfils about the same need, however there are some important differences. As far as I can tell, the biggest one is that the ownership model is different: sol::object has reference semantic, and shared ownership of the underlying value (since the value is actually stored in the Lua memory space, and the object just keeps a reference to it). In contrast, std::any has value semantic and single ownership. This means I cannot use sol::object where I would normally use std::any (i.e., in my event dispatcher system).

There's also no built-in way to convert one to another, that I can see. I would need to add a function like this:

sol::object to_sol_object(sol::state& lua, const std::any& a) {
    if (!a.has_value()) return sol::make_object(lua.lua_state(), sol::lua_nil);

    const auto& type = a.type();
#define TRY_TYPE(T) if (type == typeid(T)) return sol::make_object(lua.lua_state(), std::any_cast<T>(a))
    TRY_TYPE(bool);
    TRY_TYPE(char);
    TRY_TYPE(unsigned char);
    TRY_TYPE(short);
    TRY_TYPE(unsigned short);
    TRY_TYPE(int);
    TRY_TYPE(unsigned int);
    TRY_TYPE(long);
    TRY_TYPE(unsigned long);
    TRY_TYPE(long long);
    TRY_TYPE(unsigned long long);
    TRY_TYPE(float);
    TRY_TYPE(double);
    TRY_TYPE(std::string);
    // etc, possibly adding support for more basic types and other sol types
#undef TRY_TYPE

    throw std::runtime_error("unsupported type"); // or your favorite error-reporting mechanism
}

And then the other way:

std::any from_sol_object(const sol::object& o) {
    if (o == sol::lua_nil) return std::any();

#define TRY_TYPE(T) if (o.is<T>()) return o.as<T>()
    TRY_TYPE(bool);
    TRY_TYPE(char);
    TRY_TYPE(unsigned char);
    TRY_TYPE(short);
    TRY_TYPE(unsigned short);
    TRY_TYPE(int);
    TRY_TYPE(unsigned int);
    TRY_TYPE(long);
    TRY_TYPE(unsigned long);
    TRY_TYPE(long long);
    TRY_TYPE(unsigned long long);
    TRY_TYPE(float);
    TRY_TYPE(double);
    TRY_TYPE(std::string);
    // etc, possibly adding support for more basic types and other sol types
#undef TRY_TYPE

    throw std::runtime_error("unsupported type"); // or your favorite error-reporting mechanism
}

I understand the scope of std::any and sol::object isn't the same, so I'm not suggesting replacing one with the other. But I was wondering if there was a plan, or interest, in adding first-class support for std::any in sol2? Maybe you know of a more clever way to do this than the naive functions above.

[*] : This type of contract could also be fulfiled by std::variant, which perhaps makes the task easier. I see that sol2 already supports std::variant, so perhaps it's just that std::any is too unconstrained a type to be supported generically by sol2...

@ThePhD ThePhD self-assigned this Oct 3, 2020
@ThePhD
Copy link
Owner

ThePhD commented Oct 3, 2020

so perhaps it's just that std::any is too unconstrained a type to be supported generically by sol2...

This is, in fact, exactly why I can't support it. I'd just throw a bunch of types at the std::any, hoping one sticks. This is only something your code can do: the best I can do is -- as is already happening -- transport the value (std::any) exactly as-is.

For your purposes, it might make sense to unravel to the type and just push the actual value from C++ into Lua. Then, you can retrieve it using a std::variant<Types, You, Care, About>, which is -- as you noted -- already supported in sol2!

Let me know if you need any hints or pointers.

@cschreib
Copy link
Contributor Author

cschreib commented Oct 3, 2020

Thank you! I will drop std::any and use std::variant, since that makes the contract more explicit. Really glad to see it supported out of the box :) sol2 is a really nice library, thanks for your efforts!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants