mdsimmo
mdsimmo

Reputation: 619

c++: Use templates to wrap any lambda inside another lambda

I want to make a function that can wrap any lambda to log start/end calls on it.

The code below works except for:

  1. any lambda that has captures

  2. any lambda that returns void (although this can easily be fixed by writing a second function)

#include <iostream>
#include <functional>

template <class T, class... Inputs>
auto logLambda(T lambda) {
    return [&lambda](Inputs...inputs) {
        std::cout << "STARTING " << std::endl;
        auto result = lambda(inputs...);
        std::cout << "END " << std::endl;
        return result;
    };
}

int main() {
    int a = 1;
    int b = 2;

    // works
    auto simple = []() -> int {
        std::cout << "Hello" << std::endl; return 1;
    };
    logLambda(simple)();

    // works so long as explicit type is declared
    auto with_args = [](int a, int b) -> int {
        std::cout << "A: " << a << " B: " << b << std::endl;
        return 1;
    };
    logLambda<int(int, int), int, int>(with_args)(a, b);

    // Does not work
    // error: no matching function for call to ‘logLambda<int(int), int>(main()::<lambda(int)>&)’
    auto with_captures = [&a](int b) -> int {
        std::cout << "A: " << a << " B: " << b << std::endl;
        return 1;
    };
    logLambda<int(int), int>(with_captures)(b);

}

Is there any way to do this? Macros are also acceptable

Upvotes: 1

Views: 156

Answers (2)

Jarod42
Jarod42

Reputation: 218323

Use Raii to handle both void and non-void return type,
and capture functor by value to avoid dangling reference,
and use generic lambda to avoid to have to specify argument your self

It results something like:

template <class F>
auto logLambda(F f) {
    return [f](auto... args) -> decltype(f(args...)) {
        struct RAII {
            RAII()  { std::cout << "STARTING " << std::endl; }
            ~RAII() { std::cout << "END " << std::endl; }
        } raii;

        return f(args...);
    };
}

Call look like:

const char* hello = "Hello";
logLambda([=](const char* s){ std::cout << hello << " " << s << std::endl; })("world");

Demo

Upvotes: 5

SergeyA
SergeyA

Reputation: 62613

That code has undefined behavior.

auto logLambda(T lambda) {
    return [&lambda]

You are capturing local parameter by reference.

Upvotes: 3

Related Questions