【问题标题】:Create a std::function type with limited arguments创建具有有限参数的 std::function 类型
【发布时间】:2016-05-31 09:25:02
【问题描述】:

鉴于可调用函数C 的类型,我想在编译时获得std::function;其中的类型:

  • 具有相同的函数返回类型C
  • 参数类型是函数C的第一个N参数类型

这意味着,对于给定类型void(int, char, double) 和给定N,函数的类型是:

  • N = 1 => 结果类型:std::function<void(int)>
  • N = 2 => 结果类型:std::function<void(int, char)>
  • N = 3 => 结果类型:std::function<void(int, char, double)>
  • N > 3 => 编译时错误

例子:

template<std::size_t N, typename R, typename... A>
constexpr auto get() {
    return /*(magically somehow)*/ std::function<R(FirstNFromA...)>
}

template<std::size_t N, typename R, typename... A>
struct S {
    using func = decltype(get<N, R, A...>());
};

【问题讨论】:

  • 很好的问答及其解决方案。似乎是一种编码乐趣。但是,我不明白,这将在哪里实际使用?在实际应用中,为什么有人要使用FuncType&lt;2, void(int, char, double, int)&gt;::Type 而不是简单的std::function&lt;void(int, char)&gt;!即使在模板内部,似乎也几乎不需要做这样的事情。要解决一个棘手的问题,有两种方法:要么得出同样困难的解决方案,要么使问题变得更简单。 :-)
  • @iammilind 我在尝试用一堆 mixin 设计东西时遇到了问题。其中一个 mixin 获得一个可变类型的类型列表,其中最后一个是要继承的类型。不用说,第一个 N-1 必须用作函数类型的参数。 :-)

标签: c++ templates c++14 variadic-templates std-function


【解决方案1】:

它遵循一个可能的解决方案:

#include <tuple>
#include <utility>
#include <functional>
#include <type_traits>

template<
    typename R,
    typename... A,
    std::size_t... I,
    std::enable_if_t<(sizeof...(I)<=sizeof...(A))>* = nullptr
> constexpr auto
get(std::integer_sequence<std::size_t, I...>) {
    return std::function<R(std::tuple_element_t<I, std::tuple<A...>>...)>{};
}

template<std::size_t, typename>
struct FuncType;

template<std::size_t N, typename R, typename... A>
struct FuncType<N, R(A...)> {
    using Type = decltype(get<R, A...>(std::make_index_sequence<N>{}));
};

int main() {
    static_assert(
        std::is_same<
            FuncType<2, void(int, char, double, int)>::Type,
            std::function<void(int, char)>
        >::value,
        "!"
    );
}

基本思想是使用tuple 和那些属于标准模板库的实用程序(例如,std::tuple_element),将它们与放置在正确位置的包扩展混合,仅此而已。 为此,我使用了一个 constexpr 函数,该函数返回一个给定类型的空 std::function 对象。然后通过decltype 获取函数的类型,并使用别名导出为FuncType 对象的类型。
一切都发生在编译时。
为了推断该函数的正确类型,我使用了一个众所周知的模式,其中涉及 std::integer_sequence,并通过扩展索引来解包元组的类型。

【讨论】:

  • 我们在 C++14 领域,只需使用 tuple_element_t
  • @T.C.这说得通。固定。
【解决方案2】:

另一种解决方案可能是:

#include <tuple>
#include <utility>
#include <functional>
#include <type_traits>

template <size_t N, class R, class Pack, class ResultPack, class Voider>
struct FuncTypeImpl;

template <size_t N, class R, template <class...> class Pack, class First, class... Args, class... ResultArgs>
struct FuncTypeImpl<N, R, Pack<First, Args...>, Pack<ResultArgs...>,  std::enable_if_t<(N > 0)>>: FuncTypeImpl<N-1, R, Pack<Args...>, Pack<ResultArgs..., First>, void> {
   using typename FuncTypeImpl<N-1, R, Pack<Args...>, Pack<ResultArgs..., First>, void>::Type;
};

template <size_t N, class R, template <class...> class Pack, class... Args, class... ResultArgs>
struct FuncTypeImpl<N, R, Pack<Args...>, Pack<ResultArgs...>, std::enable_if_t<(N == 0)>> {
   using Type = std::function<R(ResultArgs...)>;
};

template<std::size_t, typename>
struct FuncType;

template<std::size_t N, typename R, typename... A>
struct FuncType<N, R(A...)> {
    using Type = typename FuncTypeImpl<N, R, std::tuple<A...>, std::tuple<>, void>::Type;
};

int main() {
    static_assert(
        std::is_same<
            FuncType<3, void(int, char, double, int)>::Type,
            std::function<void(int, char, double)>
        >::value,
        "!"
    );
}

编辑: 还有一个可能更简单(不需要 std::tuple)的解决方案:

#include <utility>
#include <functional>
#include <type_traits>

template <class T>
struct ResultOf;

template <class R, class... Args>
struct ResultOf<R(Args...)> {
   using Type = R;
};

template<std::size_t N, class Foo, class ResultFoo = typename ResultOf<Foo>::Type() , class Voider = void>
struct FuncType;

template<std::size_t N, class R, class First, class... Args, class... ResultArgs >
struct FuncType<N, R(First, Args...), R(ResultArgs...), std::enable_if_t<(N > 0)>>: FuncType<N-1, R(Args...), R(ResultArgs..., First), void> {
};

template<std::size_t N, class R, class First, class... Args, class... ResultArgs >
struct FuncType<N, R(First, Args...), R(ResultArgs...), std::enable_if_t<(N == 0)>> {
   using Type = std::function<R(ResultArgs...)>;
};

int main() {
    static_assert(
        std::is_same<
            FuncType<3, void(int, char, double*, int)>::Type,
            std::function<void(int, char, double*)>
        >::value,
        "!"
    );
}

【讨论】:

    【解决方案3】:

    另一个基于元组的解决方案。

    也应该适用于 C++11。

    我想它可以简化,但我不知道如何避免使用bool模板参数(我正在学习C++11)。

    #include <tuple>
    #include <utility>
    #include <functional>
    #include <type_traits>
    
    
    template<std::size_t N, bool Z, typename R, typename...>
    struct FTH1;
    
    template <typename R, typename... A, typename... B>
    struct FTH1<0U, true, R, std::tuple<A...>, B...>
     { using type = decltype(std::function<R(A...)>{}); };
    
    template <std::size_t N, typename R, typename... A, typename B0, typename... B>
    struct FTH1<N, false, R, std::tuple<A...>, B0, B...>
     { using type = typename FTH1<N-1U, (N-1U == 0U), R, std::tuple<A..., B0>, B...>::type; };
    
    
    template <std::size_t N, typename>
    struct FuncType;
    
    template<std::size_t N, typename R, typename... A>
    struct FuncType<N, R(A...)>
     { using Type = typename FTH1<N, (N == 0), R, std::tuple<>, A...>::type; };
    
    
    
    int main() {
        static_assert(
            std::is_same<
                FuncType<2, void(int, char, double, int)>::Type,
                std::function<void(int, char)>
            >::value,
            "!"
        );
    }
    

    ps:对不起,我的英语不好。

    【讨论】:

      【解决方案4】:

      正如我在 cmets 中提到的,如果出现这样的问题,那么我的第一个方法是改变问题本身!有时,与其为“棘手的问题”找到“棘手的解决方案”,不如让问题本身“更简单”!
      永远不需要写FuncType&lt;2, R(X,Y,Z)&gt;::type 而不是简单的std::function&lt;R(X,Y)&gt;

      以上是我的真实答案。为了解决您的问题作为编码的乐趣,我提出了一个简单的基于宏的答案。它会在预处理时为您提供所需的类型,而不是编译时。

      #define F(R, ...) std::function<R(__VA_ARGS__)>  // shorthand for std::function...
      #define FuncType(N, R, ...) FUNC_##N(R, __VA_ARGS__)  // Main type
      #define FUNC_0(R, ...) F(R)  // overload for 0 arg
      #define FUNC_1(R, _1, ...) F(R, _1)  // overload for 1 arg
      #define FUNC_2(R, _1, _2, ...) F(R, _1, _2)  // overload for 2 args
      #define FUNC_3(R, _1, _2, _3, ...) F(R, _1, _2, _3)  // overload for 3 args
      

      用法:

      int main() {
        static_assert(std::is_same<
          FuncType(2, void, int, char, double, int),  // <--- see usage
          std::function<void(int, char)>
          >::value, "!");  
      }
      

      如您所见,用法略有变化。我使用的是FuncType(2, void, int, char, double, int),而不是FuncType&lt;2, void(int, char, double, int)&gt;::type。这是demo


      截至目前,FUNC_N 宏最多可重载 3 个参数。对于更多的参数,如果我们想避免复制粘贴,那么我们可以用一个简单的程序生成一个头文件:

        std::ofstream funcN("FUNC_N.h"); // TODO: error check for argv & full path for file
        for(size_t i = 0, N = stoi(argv[1]); i < N; ++i)
        {
          funcN << "#define FUNC_" << i << "(R";  // FUNC_N
          for(size_t j = 1; j <= i; ++j)
            funcN << ", _" << j;  // picking up required args
          funcN << ", ...) ";  // remaining args
      
          funcN << "F(R";  // std::function
          for(size_t j = 1; j <= i; ++j)
            funcN << ", _" << j;  // passing only relevant args
          funcN <<")\n";
        }
      

      只需#include它:

      #define F(R, ...) std::function<R(__VA_ARGS__)>
      #define FuncType(N, R, ...) FUNC_##N(R, __VA_ARGS__)
      #include"FUNC_N.h"
      

      这里是demo,了解如何生成头文件。

      【讨论】:

      • 正如在 cmets 中已经提到的,当你有 R(Args...) 时,你不知道哪个是参数包的大小而不知道哪个是实际类型。你唯一知道的是你不想要所有这些,仅此而已。 :-) ...无论如何,谢谢你的回答。作为一种解决方案没有太大的可扩展性,但它确实有效。
      • @skypjack,"neither which is the size of the parameter pack not which are the actual types. The only thing you know is that you don't want all of them"。我的观点是,您可能希望以这样的方式设计您的项目,这样的要求本身就不会出现。但是,我并不是在判断您在这里需要的真实性。只是一个意见。您的解决方案当然比宏观风格优雅,因为它不依赖于其他任何东西。但是,上述宏解决方案也是可扩展的。你只需要使用那个涉及所有宏的小代码来创建头文件。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-05-21
      • 1970-01-01
      • 1970-01-01
      • 2015-12-25
      相关资源
      最近更新 更多