lambdas和std :: function的包装器

时间:2017-04-18 03:30:41

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

我想创建一个函数,它将lambda,std :: function或regular函数作为输入,并将其包装成类似下一个函数的东西:

void original_function(const int x, float y) { 
    // do smth ...
}
...
using WrapperType = std::function<void(const QVariantList &)>;
WrapperType original_function_wrapper = [original_function](const QVariantList &vl) { 
    original_function(vl.value(0).value<int>(), vl.value(1).value<float>());
};

所以我创建了一些助手来处理我的问题:

template<size_t index, typename T>
T unpackFromVariant(const QVariantList &v) {
    return qvariant_cast<T>(v.value(static_cast<int>(index)));
};


template<typename T>
struct function_traits;

template<typename Ret, typename... Args>
struct function_traits<Ret(Args...)> {
    enum { ArgumentCount = sizeof...(Args) };

    using ArgumentTypes = std::tuple<Args...>;
    using ReturnType = Ret;
};

template<typename Ret, typename... Args>
struct function_traits<std::function<Ret(Args...)>> {
    enum { ArgumentCount = sizeof...(Args) };

    using ArgumentTypes = std::tuple<Args...>;
    using ReturnType = Ret;
};

template<size_t index, typename T>
struct type_of {
    using Type = typename std::remove_reference<decltype(std::get<index>(T()))>::type;
};


using WrapperFunctionType = std::function<void(const QVariantList &)>;

template<typename FT, typename T, size_t... I>
WrapperFunctionType createWrapperImpl(T &&functor, std::index_sequence<I...>) {
    return [ff = std::forward<T>(functor)](const QVariantList &vl) {
        ff(unpackFromVariant<I, typename type_of<I, typename FT::ArgumentTypes>::Type>(vl) ...);
    };
};

template<typename T>
WrapperFunctionType createWrapper(T &&functor) {
    using TT = typename std::remove_reference<T>::type;
    using FunctionTraits = function_traits<TT>;
    using Indices = std::make_index_sequence<FunctionTraits::ArgumentCount>;

    return createWrapperImpl<FunctionTraits>(std::forward<T>(functor), Indices());
}

它适用于std::function和常规函数,但它对lambdas没用(这意味着每次我想传递lambda函数作为参数时,我必须首先用std::function(RET_VAL(ARGS))包装它)。有没有办法专门化function_traits模板来推导lambda参数类型?

1 个答案:

答案 0 :(得分:1)

我会从:

开始
template<class...Args, class F>
std::result_of_t<F(Args&&...)>
apply_variant( F&& f, QVariantList& list ){
  auto indexer=index_upto<sizeof...(Args)>();
  return indexer([&](auto...Is)->decltype(auto){
    return std::forward<F>(f)( list.value(Is).value<Args>()... );
  };
}

其中hereindex_upto

现在我们退后一步:

template<class...Args, class F>
WrapperFunctionType createWrapper(F&& f){
  return [f=std::forward<F>(f)](auto& list)mutable->decltype(auto){
    return apply_variant<Args...>( f, list );
  };
}
template<class R, class...Args>
WrapperFunctionType createWrapper(std::function<R(Args...)> f){
  return [f=std::move(f)](auto& list)mutable->decltype(auto){
    return apply_variant<Args...>( f, list );
  };
}
template<class R, class...Args>
WrapperFunctionType createWrapper(R(*f)(Args...)){
  return [f=std::move(f)](auto& list)mutable->decltype(auto){
    return apply_variant<Args...>( f, list );
  };
}

现在,这个版本你必须传递你期望用函数对象调用的类型。

我们可以做一个不明智的人:

namespace details{
  template<class T>struct tag_t{};
  template<class...Args, class R, class U, class F>
  WrapperFunctionType createWrapper(tag_t<R(U::*)(Args...)>, F&& f){
    return [f=std::forward<F>(f)](auto& list)mutable->decltype(auto){
      return apply_variant<std::remove_reference_t<Args>...>( f, list );
    };
  }
}
template<class F>
WrapperFunctionType createWrapper(F&& f){
  using dF=std::decay_t<F>;
  using sig_t = details::tag_t<decltype(&dF::operator())>;
  return details::createWrapper( sig_t{}, std::forward<F>(f) );
}

或某些。

代码未编译。