Search code examples
c++lambdadecltype

How can I use decltype on a variable in a lambda without capturing it?


I'm trying to design a C++ macro that needs to look something like this:

#define MY_MACRO(OBJECT, METHOD)                            \
    [](BaseClass* obj)                                      \
    {                                                       \
        return static_cast<decltype(OBJECT)>(obj)->METHOD();\
    }

Basically, a macro that translates into a lambda that calls a given method on a given object. But the lambda needs to take a base class of the object as a parameter (My use case guarantees that the cast will always work). Furthermore, the method to be called might not be on the base class.

The usage for this macro is that I have another method which I cannot modify declared as:

void Foo(std::function<int(BaseClass*)>);

and I need to be able to call it using my macro as a parameter like so:

T x;
Foo(MY_MACRO(x, method));  // match std::function<int(T*)>

However, the macro code doesn't work because I'm not capturing OBJECT, so it's not in scope when I need to pass it to decltype. Conceptually though, all the information the compiler needs is there... How can I do this? Is it possible?

A few constraints:

  1. The lambda's parameter needs to be BaseClass. I can't make it decltype(OBJECT).
  2. My situation does not allow me to capture OBJECT.
  3. I don't have access to the C++14 feature of generalized lambda captures.

Solution

  • You can add an optional parameter to the lambda with the type that you want, and use decltype on that parameter. Here's an example of the pattern, minus the macro:

    int main() {
        int foo = 4;
        auto lambda = [](double* bar, decltype(foo)* TP = nullptr) {
            return static_cast<std::remove_pointer<decltype(TP)>::type>(*bar);
        };
    
        double x = 5;
        return lambda(&x);
    }
    

    I get a pointer to decltype(foo) here because pointer types can easily be defaulted to nullptr to ensure that the parameter is optional. If decltype(foo) already resolves to a pointer type, as in your case if I got it right, you wouldn't need it (and the remove_pointer).