Cody
Cody

Reputation: 639

How to have a function template get the parameter types of its lambda argument?

Assume we have a class template like this:

template <typename baseT, typename ...argTs>
class CCallable : public baseT
{
public:
    CCallable(std::function<bool(argTs...)> lambda)
    : m_lambda(lambda) {
    }
    bool Invoke(argTs ...args) override {
        return m_lambda(args...);
    }
private:
    std::function<bool(argTs...)> m_lambda;
};

And assume we have a function template callback implemented, probably similar to this pseudo code:

template <typename baseT, typename lambdaT>
CCallable<baseT, typename lambdaT::argTs...> callback(lambdaT lambda)
{
    return CCallable<baseT, typename lambdaT::argTs...>(lambda);
}

so that we are able to do this:

auto&& functor = callback<CBase>([](int x, int y, int *sum)->bool{
    *sum = x + y;
    return true;
});
// Start passing functor around and then...
int sum;
functor.Invoke(7, 42, &sum);

Please note that the parameter types of the lambda are not passed to callback as its template type arguments.

How can we implement a function template like this, to save the users from typing more codes like:

auto&& functor = callback<CBase, int, int, int*>([](int x, int y, int *sum)->bool{
    *sum = x + y;
    return true;
});

Thanks.

By the way, why I'm asking this is because Microsoft::WRL provides a similar template named Callback which is called many times in an opensource library I want to use. However, I prefer to build the library with GNU C++ instead of Visual C++. Therefore, it seems inevitable that I have to implement a Microsoft::WRL::Callback-like template or macro myself.

Upvotes: 1

Views: 266

Answers (1)

Evg
Evg

Reputation: 26292

With C++17 CTAD, you could harness deduction guides for std::function:

template<class T>
struct Identity { };

template<typename baseT, typename ...argTs>
class CCallable : public baseT {
public:
    CCallable(std::function<bool(argTs...)> lambda, Identity<baseT> = {})
        : m_lambda(lambda) {
    }

    bool Invoke(argTs ...args) override {
        return m_lambda(args...);
    }

private:
    std::function<bool(argTs...)> m_lambda;
};

template<typename baseT, typename lambdaT>
auto callback(lambdaT lambda) {
    return CCallable(std::function(lambda), Identity<baseT>{});
}

CTAD is all-or-nothing, so the type baseT is wrapped into Identity to make it deducible.

Demo

Upvotes: 3

Related Questions