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

Fix invalid usage of lambdas with Lua bindings. #74

Open
Possseidon opened this issue Feb 24, 2022 · 1 comment
Open

Fix invalid usage of lambdas with Lua bindings. #74

Possseidon opened this issue Feb 24, 2022 · 1 comment
Assignees
Labels
bug Something isn't working dang-lua refactor Changing stuff without introducing new features

Comments

@Possseidon
Copy link
Owner

Possseidon commented Feb 24, 2022

Pattern

The following pattern is heavily used in the current Lua bindings:

constexpr auto function = +[] {};
dlua::wrap<function>;

This is very convenient, as the full context of the class (for e.g. type aliases) is available inside the lambda.

Issue

This works fine on msvc and clang, but gcc gives an error, stating that the lambda cannot be used as it has no linkage - except it doesn't actually give this error at the moment. Everything compiles just fine with gcc... or does it?

Why it works (even though it shouldn't)

The reason it compiles is the (un)lucky coincidence of:

  1. Using gcc 9.x (any later version will likely break according to some testing on compiler-explorer)
  2. Having template stuff around the lambda.

Basically, upgrading gcc or moving the lambdas somewhere that isn't templated will (likely) cause the error.

Workarounds

There are several ways on how one can work around this issue.

1. Wrapping it in std::function

The probably easiest fix would be to simply type-erase the lambda with a std::function. This even has the benefit of having a nice signature when printing in Lua. However it comes with a decent performance hit.

2. Use free-standing functions

Simply replacing the lambdas with free-standing functions e.g. right in front of the function in which they are used is generally a good alternative. Context from the class is lost however.

3. Use static constexpr lambdas inside the class

This way, context of the class can be retained. Implementations have to be put in the header however, although that is hopefully not a big deal.

This also has the added benefit of allowing for re-use of wrappers between e.g. methods() and properties().

4. Proper static functions inside the class

Another option would be to use proper static functions, but implementing them is very verbose, as the entire template header has to be repeated for each and every function.

Conclusion

Use static constexpr lambdas (approach 3) like so:

Header

template <>
struct ClassInfo<Foo> {
    static std::array<luaL_Reg> methods();

private:
    static constexpr auto bar = +[] {};
};

Source

template <>
std::array<luaL_Reg> ClassInfo<Foo>::methods()
{
    return {reg<bar>("bar")};
}
@Possseidon Possseidon added bug Something isn't working dang-lua refactor Changing stuff without introducing new features labels Feb 24, 2022
@Possseidon Possseidon self-assigned this Feb 24, 2022
@Possseidon
Copy link
Owner Author

If I'm not mistaken, C++20 allows passing lambdas as template parameters, which could not only fix this issue, but also allows me to get rid of the + before each lambda. The big question is, if GCC allows this or if this is still not possible because of the same reasons as previously (no linkage, which GCC might technically be correct about, but not sure).

Of course, the lambdas still won't be allowed to capture anything, but the conversion to a function pointer can happen inside the wrap style functions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working dang-lua refactor Changing stuff without introducing new features
Projects
None yet
Development

No branches or pull requests

1 participant