1

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.

5
  • Remember that the lambda might be callable with multiple types... (Auto parameter im c++14 for example) Commented Jan 6, 2021 at 7:07
  • The doc makes it look like that it is necessary to specify the template parameter, no? Commented Jan 6, 2021 at 7:10
  • 1
    You could make Invoke() itself a template. Commented Jan 6, 2021 at 7:16
  • @user202729 It does need to have a template type argument, but the only mandatory type parameter is the base class, not any types in the parameter list of the lambda that is passed to Callback. Maybe the Callbak gets those types from the base class, idk. Commented Jan 6, 2021 at 7:18
  • 1
    @G.Sliepen Nope, a method template isn't allowed to be virtual. See the keyword override there after the parameter list of Invoke? Commented Jan 6, 2021 at 7:20

1 Answer 1

3

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

Sign up to request clarification or add additional context in comments.

4 Comments

In non-C++17 land, I think this basically does template<typename B, typename F, typename G, typename R, typename... As> CCallback<B, As...> callback_impl(F &f, R (G::*)(As...)) { return CCallable<B, As...>(f); } template<typename B, typename F> auto callback(F f) /* -> decltype(callback_impl<B>(f, &F::operator())) */ { return callback_impl<B>(f, &F::operator()); } (no need for Identity). I actually didn't realize the standard library sanctioned this! I always thought it was a bit of a hack,
@HTNW Thanks. Your code works perfectly, even when the argument of the callback is a lambda instead of a std::function<> object. AFAIK, compilers don't do implicit conversion for the arguments of function templates, but in this case it looks like the the lambda is implicitly converted into a std::function<>. Why? What happened? I'm confused.
@Cody Implicit conversion is only suppressed during template argument deduction, because we might not know the type we'd need to convert to (since we haven't deduced the template parameters yet). If we've given enough template arguments to know the parameter type implicit conversion can proceed as usual.
@HTNW Allow me to make it clearer for the people who might be looking for the solution to the same problem. If use nm or objdump to check the symbols generated by the 2 function templates mentioned above, there is no evidence that implicit conversion happened when passing arguments into the function templates. The lambda is still a lambda. The std::function<> object is created in the callback_impl after callback_impl received the argument types of the lambda from callback.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.