从具有未知参数计数的函数创建仿函数

时间:2013-02-02 12:13:08

标签: c++ templates boost

我用C ++编写了一个程序&amp;促进。是否有可能编写一个模板类,从具有未知数量参数的函数生成仿函数,例如: my_call<func>(vector<variant>),有趣的可以是bool fun(string)bool fun(int, int, string)等?

1 个答案:

答案 0 :(得分:1)

首先,重要的是要认识到boost::variant<>是一个类模板,需要它可以容纳的所有可能类型的列表。因此,您不会只有vector<variant>,而是vector<variant<string, double>>vector<variant<int, double, string, my_class>>,您将无法将它们混合在一起。

这让我觉得你可能想要使用boost::any而不是boost::variant<>。因此,我在这里提供了一个与boost::variant一起使用的解决方案,可以稍加修改以使用boost::any,因此您可以选择您喜欢的版本。


首先,我必须承认解决方案易于使用不易理解,因此我将不得不介绍一些机器< / strong>第一个。对于基于变体的解决方案和基于任何解决方案,此机制常见

//=============================================================================
// META-FUNCTIONS FOR CREATING INDEX LISTS

// The structure that encapsulates index lists
template <size_t... Is>
struct index_list
{
};

// Collects internal details for generating index ranges [MIN, MAX)
namespace detail
{
    // Declare primary template for index range builder
    template <size_t MIN, size_t N, size_t... Is>
    struct range_builder;

    // Base step
    template <size_t MIN, size_t... Is>
    struct range_builder<MIN, MIN, Is...>
    {
        typedef index_list<Is...> type;
    };

    // Induction step
    template <size_t MIN, size_t N, size_t... Is>
    struct range_builder : public range_builder<MIN, N - 1, N - 1, Is...>
    {
    };
}

// Meta-function that returns a [MIN, MAX) index range
template<size_t MIN, size_t MAX>
using index_range = typename detail::range_builder<MIN, MAX>::type;

元类index_range允许定义整数编译时序列An interesting proposal Jonathan Wakely已经{{3}}标准化了这种结构,因此不需要整个机器。但是,目前我们必须像上面那样手工编写代码。


现在我们可以构建编译时整数序列了,我们可以利用可变参数模板参数解包来创建一个转换{{1}向量的调度机制参数到常规参数列表中。注意必须如何提供具体的variant类型作为模板参数。基于variant<>的解决方案

any

最后,简要演示如何使用它。假设我们有两个测试函数,如下面的那些:

// Headers needed for the implementation of the dispatcher
#include <vector>
#include <functional>
#include <boost/variant.hpp>

// Just for convenience
using namespace std;
using boost::variant;

//============================================================================
// DISPATCHER IMPLEMENTATION

// Call dispatching mechanism: notice how the underlying variant type
// must be provided as a template argument (the first one)
template<typename VT, typename R, typename... Args>
struct dispatcher
{

    template<typename F>
    dispatcher(F f) : _f(f) { }

    // The call operator which performs the variant dispatch
    R operator () (vector<VT> const& v)
    {
        if (v.size() != sizeof...(Args))
        {
            // Wrong number of arguments provided!
            return false;
        }

        // Delegates to internal function call: needed for deducing
        // a sequence of integers to be used for unpacking.
        index_range<0, sizeof...(Args)> indexes;
        return do_call(v, indexes);
    }

private:

    // The heart of the dispatching mechanism
    template<size_t... Is>
    R do_call(vector<VT> const& v, index_list<Is...> indexes)
    {
        return _f((get_ith<Args>(v, Is))...);
    }

    // Helper function that extracts a typed value from the variant.
    template<typename T>
    T get_ith(vector<VT> const& v, size_t i)
    {
        return boost::get<T>(v[i]);
    }

    // Wrapper that holds the function to be invoked.
    function<R(Args...)> _f;
};

// Helper function that allows deducing the input function signature
template<typename VT, typename R, typename... Args>
function<R (vector<VT> const&)> get_dispatcher(R (*f)(Args...))
{
    dispatcher<VT, R, Args...> d(f);
    return d;
}

我们想要的是通过构建变量向量来调用它们并将其分派到所需的函数。我必须再次强调,我们需要指定我们的变体可以容纳的所有类型的列表。在这里,我将假设这些类型为#include <iostream> bool test1(string s, double d) { cout << s << " " << d << endl; return true; } bool test2(int i1, int i2, string s1, string s2) { cout << i1 << " " << i2 << " " << s1 << " " << s2 << endl; return true; } stringdouble,但您的程序可能适用于不同的类型。

此外,该解决方案基于int来实现类型擦除,允许您创建不同类型的仿函数并统一调用它们。因此,还提供了此std::function<>的便捷类型定义(这又取决于我们使用的std::function<>类型):

variant<>

由于所有调度程序都具有int main() { // A helper type definition for the variant typedef variant<int, double, string> vt; // A helper type definition for the function wrapper typedef function<bool (vector<vt>)> dispatcher_type; // Get a caller for the first function dispatcher_type f1 = get_dispatcher<vt>(test1); // Prepare arguments for the first function vector<vt> v = {"hello", 3.14}; // Invoke the first function f1(v); // Get a caller for the second function dispatcher_type f2 = get_dispatcher<vt>(test2); // Prepare arguments for the second function v.assign({1, 42, "hello", "world"}); // Invoke the second function f2(v); } 类型,因此您可以轻松将它们放入容器。但是,您必须意识到,只有在运行时才能检测到使用错误数量的参数调用函数的尝试(不可能在编译时dispatcher_type包含多少个元素。因此,必须采取适当的谨慎措施。


正如所承诺的,我现在将稍微修改此解决方案以使用std::vector<>而不是boost::any。优点是,由于boost::variant可以包含任何值,因此指定可用作函数参数的可能类型的列表无需。< / p>

虽然辅助机制未更改,但核心调度程序类模板必须按如下方式进行修改:

boost::any

如您所见,#include <vector> #include <functional> #include <boost/any.hpp> using namespace std; using boost::any; //============================================================================= // DISPATCHER IMPLEMENTATION template<typename R, typename... Args> struct dispatcher { template<typename F> dispatcher(F f) : _f(f) { } // The call operator which performs the dispatch R operator () (vector<any> const& v) { if (v.size() != sizeof...(Args)) { // Wrong number of arguments provided! return false; } // Delegates to internal function call: needed for deducing // a sequence of integers to be used for unpacking. index_range<0, sizeof...(Args)> indexes; return do_call(v, indexes); } private: // The heart of the dispatching mechanism template<size_t... Is> R do_call(vector<any> const& v, index_list<Is...> indexes) { return _f((get_ith<Args>(v, Is))...); } // Helper function that extracts a typed value from the variant. template<typename T> T get_ith(vector<any> const& v, size_t i) { return boost::any_cast<T>(v[i]); } // Wrapper that holds the function to be invoked. function<R(Args...)> _f; }; // Helper function template<typename R, typename... Args> function<R (vector<any> const&)> get_dispatcher(R (*f)(Args...)) { dispatcher<R, Args...> d(f); return d; } 模板参数已消失。特别是,可以在不明确指定任何模板参数的情况下调用VT。使用我们为基于get_dispatcher的解决方案定义的相同测试函数,以下是如何调整variant例程:

main()

唯一的缺点是使用int main() { // Helper type definition typedef function<bool (vector<any>)> dispatcher_type; // Get a caller for the first function dispatcher_type f1 = get_dispatcher(test1); // Get a caller for the second function dispatcher_type f2 = get_dispatcher(test2); // Prepare arguments for the first function vector<any> v = {string("hello"), 3.14}; // Invoke the first function f1(v); // Prepare arguments for the second function v.assign({1, 42, string("hello"), string("world")}); // Invoke the second function f2(v); } 无法显式分配字符串文字,因为字符串文字的类型为boost::any,并且数组不能用于初始化char []类型的对象:< / p>

any

因此,您必须将它们包装到any a = "hello"; // ERROR! 个对象中,或者将它们显式转换为指向string的指针:

char const*

如果这对您来说不是一个大问题,那么最好选择第二个解决方案。