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

generate lambdas body (that invokes a callable and returns) depending on template type

本文关键字:调用 对象 返回 主体 lambda 类型      更新时间:2023-10-16

我有一个可调用的对象,可能会返回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()的方法?

非常感谢有关如何简化此代码的任何其他提示。

不是一个很好的解决方案...

我能想象的最好的是单个 lambda 带有一个if constexpr来分隔两种情况(您标记为 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)...);
    };
 }

嗯...如果你不能使用 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)...); };
 }