根据模板类型生成lambdas主体(调用可调用对象并返回)

时间:2019-03-29 11:48:37

标签: c++ templates lambda c++14 variadic-templates

我有一个可调用对象,该对象可能返回boolvoid。该对象需要包装在lambda中。此lambda应始终返回bool。如果可调用对象返回bool,则lambda返回该对象返回的所有内容。否则(如果对象返回void),lambda只会调用它并返回true

我试图尽可能简化下面的代码。

template<class... Params>
struct SParamsPack {};

template <class T> struct SFuncInfo {};

// in the simplified version specialization only for member function
template <class T, class R, class... FuncParams>
struct SFuncInfo<R(T::*)(FuncParams...)> {
    using Signature = std::function<bool(FuncParams...)>;
    using Ret = R;
    using Params = SParamsPack<FuncParams...>;
};

template<class T, class Func, class... Params>
SFuncInfo<Func>::Signature GenerateVoidLambda(Func f, T* pObj, SParamsPack<Params...>)
{
    return [pObj, f](Params&&... params) -> bool
    {
        (pObj->*f)(std::forward<Params>(params)...);
        return true;
    };
}

template<class T, class Func, class... Params>
SFuncInfo<Func>::Signature GenerateBoolLambda(Func f, T* pObj, SParamsPack<Params...>)
{
    return [pObj, f](Params&&... params) -> bool
    {
        return (pObj->*f)(std::forward<Params>(params)...);
    };
}

// bodies of both WrapMemberFunction are almost identical
template<class T, class Func, std::enable_if_t<std::is_same<typename SFuncInfo<Func>::Ret, bool>::value, bool> = true>
SFuncInfo<Func>::Signature WrapMemberFunction(Func f, T* pObj)
{
    return GenerateBoolLambda(f, pObj, SFuncInfo<Func>::Params());
}

template<class T, class Func, class = std::enable_if_t<std::is_same<typename SFuncInfo<Func>::Ret, void>::value>>
SFuncInfo<Func>::Signature WrapMemberFunction(Func f, T* pObj)
{
    return GenerateVoidLambda(f, pObj, SFuncInfo<Func>::Params());
}

//! Registers a std::function that returns bool.
template<class... Params>
void RegisterCommand(const string& id, std::function<bool(Params...)> f)
{
    // Code for registration of command.
}

//! Registers a member function pointer as a command.
template<class T, class Func>
void RegisterCommand(const string& id, Func f, T* pObj)
{
    RegisterCommand(id, CommandRegistry::WrapMemberFunction(f, pObj));
}

用户的呼叫将如下所示:

RegisterCommand("general.create", &SomeObj::OnCreate, pSomeObject);

代码应符合C ++ 14标准。

那么有什么办法可以使这段代码看起来更好呢?是否有可能至少摆脱WrapMemberFunction()GenerateLambda()方法?

非常感谢您提供有关简化此代码的其他提示。

2 个答案:

答案 0 :(得分:2)

没有很好的解决方案...

我能想象的最好的是一个带有if constexpr的lambda来分隔两种情况(您标记了C ++ 11,但您使用的是std::invoke(),所以您使用的是C ++ 17,因此您也可以使用if constexpr

template <typename T, typename F, typename ... Args>
typename SFuncInfo<F>::Signature GenerateLambda(F f, T* pObj,
                                                SParamsPack<Args...>)
 {
   return [pObj, f](Args && ... as) -> bool
    {
      if constexpr ( std::is_same_v<void,
                                    decltype(std::function{f})::result_type> )
       {
         std::invoke(f, pObj, std::forward<Args>(as)...);

         return true;
       }
      else
         return std::invoke(f, pObj, std::forward<Args>(as)...);
    };
 }

答案 1 :(得分:2)

好吧...如果您不能使用C ++ 17(因此没有std::function的模板推导指南,也没有if constexpr的模板推论),我能想象的最好是定义两个重载函数来接收对象,该对象的方法指针和该方法的参数。

template <typename T, typename ... As1, typename ... As2>
bool callFunc (T * pObj, bool(T::*f)(As1...), As2 && ... args)
 { return (pObj->*f)(std::forward<As2>(args)...); }

template <typename T, typename ... As1, typename ... As2>
bool callFunc (T * pObj, void(T::*f)(As1...), As2 && ... args)
 { (pObj->*f)(std::forward<As2>(args)...); return true; }

如您所见,第一个返回方法返回的值(一个bool值);第二个调用方法(返回void)并返回true

鉴于这对callFunc(),您可以按照以下步骤创建一个GenerateLambda()

template <typename T, typename F, typename ... As>
typename SFuncInfo<F>::Signature GenerateLambda (F f, T * pObj,
                                                 SParamsPack<As...>)
 {
   return [pObj, f](As && ... args)
    { return callFunc(pObj, f, std::forward<As>(args)...); };
 }