2

Is it possible to get the address of the lambda call operator for a lambda having captures ? And even more: to assign such a pointer to a common pointer which can point to different lambdas having the same captures and the same calling-parameters ? Strictly speaking, assigning a result (class1::*)( ... ) to another result (class2::*)( ... ) shouldn't be directly possible, but you might do dirty tricks and cast the pointers which could be possible since the lambda-classes are notionally interchangeable.

So what's the "proper" syntax for this ? Even as a dirty trick.

The last line of the following code unfortunately doesn't work:

int main()
{
    int i;
    auto l100 = [&]()
    {
        i += 100;
    };
    using l100_t = decltype(l100);
    using l100_fn = void (l100_t::*)();
    l100_fn fn100 = &l100_t::operator ();
}
cigien
  • 57,834
  • 11
  • 73
  • 112
Bonita Montero
  • 2,817
  • 9
  • 22
  • 4
    You mean other than using an `std::function`? – Stephen Newell Apr 03 '21 at 14:53
  • *"address of the lamda calling-operator"* You mean a pointer-to-member? Or a function pointer? – HolyBlackCat Apr 03 '21 at 14:54
  • @HolyBlackCat - but lambdas have anonymous (unnamable) types? – davidbak Apr 03 '21 at 14:55
  • 1
    Lambdas are *voldemort types*. But one can `using mylambda_t = decltype(mylambda);` in a pinch. – Eljay Apr 03 '21 at 14:57
  • `std::function` works as suggested by @StephenNewell - see [godbolt example](https://godbolt.org/z/4KvT93PYT) (although it doesn't answer the OP's question since that isn't the _address_ of the lambda operator). – davidbak Apr 03 '21 at 14:59
  • However, it looks like you _can't_ get the address of the lambda-with-capture inside of the std function [according to the comments on this answer](https://stackoverflow.com/a/18039824/751579). – davidbak Apr 03 '21 at 15:06
  • 3
    "*the lambda-classes are notionally interchangeable.*" What is "interchangeable" about completely unrelated classes? – Nicol Bolas Apr 03 '21 at 15:21
  • 1
    "*different lambdas having the same captures and the same calling-parameters ?*" It sounds like you want a derived class, not a lambda. Don't use lambdas as some quick-and-dirty way to write a type. – Nicol Bolas Apr 03 '21 at 15:23
  • 1
    The compiler errors I get at that `l100_fn fn100 = &l100_t::operator ();` from gcc, clang, and msvc all give an important hint about how to correct it. – aschepler Apr 03 '21 at 15:27
  • Ok, a function<...> is a solution if the time to create a function-object isn't relevant. It disassembled the construction with MSVC 2019 and gcc, and they resut in a dozen of instructions. – Bonita Montero Apr 03 '21 at 15:59

1 Answers1

1

which can point to different lambdas having the same captures and the same calling-parameters

If you have some interface which expects the user to provide a "lambda" that "captures" a specific set of values, and takes a specific set of parameters, then it's not a lambda anymore. It's just a function pointer that is given a const& to a struct containing the "captured" values as one of its parameters. After all, the receiving code needs to store the "captured" values, right? So the user needs to provide a "capture" struct and a function pointer that takes the "capture" struct and the other arguments.

That's how you should build your API: clearly and explicitly. It's not the user's job to make sure they're capturing the right parameters; it's the API's job to tell the user want values are being "captured", and the user provides a function pointer that interfaces with them.

Yes, the user doesn't get to automatically treat those "captured" values as accessible by name; they have to use the parameter name to get at them. But the API becomes a lot more coherent and a lot less fragile.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • Thanks, but that's not what I asked for. – Bonita Montero Apr 03 '21 at 15:58
  • @BonitaMontero: I know. What you asked for made no sense, isn't actually possible, and even if it were possible would be a bad solution for what you're trying to do. So here's a solution that actually works, better communicates to the user what the system's needs are, and is less prone to breakage. – Nicol Bolas Apr 03 '21 at 16:07
  • Others have shown a proper solution with a function<>-object. This almost fits every time if you aren't concerned about the costs of creating the object. Calling the object has almost the same performance than calling a pure function-pointer. I already had the idea you told myself. But having additional parameters makes the code more to write and sometimes it may be relevant that it is slower than having a lamda with pre-initialized captures. – Bonita Montero Apr 03 '21 at 16:20
  • @BonitaMontero: "*Others have shown a proper solution with a function<>-object.*" But you asked to get a *function pointer*, not a `std::function`, which has some overhead to it. It also doesn't stop the user from capturing different values, which your question made clear was a requirement of the system. – Nicol Bolas Apr 03 '21 at 16:35
  • Of course the function<>-object is somewhat different as it also includes an object-pointer. But what you advised isn't still what I asked for since calling lambda-objects with initialized captues is more efficient than calling a function-like lambda without any caputes but parameters instead. – Bonita Montero Apr 03 '21 at 17:00