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

标签 c++ templates boost

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

最佳答案

首先,重要的是要认识到 boost::variant<>是一个类模板,它需要它可以包含的所有可能类型的列表。所以,你不会只有 vector<variant> , 而是一个 vector<variant<string, double>> , 或 vector<variant<int, double, string, my_class>> ,并且您将无法混合它们。

这让我觉得你可能想使用 boost::any而不是 boost::variant<> .因此,我在这里提出一个适用于 boost::variant 的解决方案。并且可以稍作修改以使用 boost::any ,因此您可以选择您喜欢的版本。

首先,我必须承认解决方案是使用简单但不是那么容易理解,所以我得介绍一些机械第一的。这台机器是常见 到基于变体和基于任意的解决方案。

//=============================================================================
// 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 proposalJonathan Wakely 制作使这种结构标准化,这样整个机器就不需要了。然而,就目前而言,我们必须像上面那样手动编写代码。

现在我们可以构建编译时整数序列,我们可以利用 可变参数模板 参数解包创建一个调度机制来转换 variant 的 vector 参数到常规参数列表中。注意具体 variant<> type 必须作为模板参数提供。基于 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;
}

我们想要的是通过构建一个变体 vector 来调用它们并将其分派(dispatch)到所需的函数。再一次,我必须强调一个事实,我们需要指定我们的变体可以包含的所有类型的列表。在这里,我假设这些类型是 string , double , 和 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<> 包含多少元素)。因此,必须采取适当的措施。

正如所 promise 的,我现在将稍微修改此解决方案以使用 boost::any而不是 boost::variant .优点是因为 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 [] , 并且数组不能用于初始化 any 类型的对象:
any a = "hello"; // ERROR!

因此,您必须将它们包装成 string对象,或将它们显式转换为指向 char const* 的指针:
any a = string("hello"); // OK
any b = (char const*)"hello"; // OK

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

关于c++ - 从具有未知参数计数的函数创建仿函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14661787/

相关文章:

c++ - 继承对Base类私有(private)数据成员的访问(在Derived类中继承Base类成员函数)

css - 网页的 slider 蓝图模板

c++ - 具有必要副作用的静态初始化被优化掉

c++ - 静态内存中的STL结构 'Losing' 跨线程数据

c++ - MFC 应用程序中 Delphi 7 和 Delphi XE4 之间的 ActiveX 差异

c++ - 在 C 中迭代 C++ 容器

c++ - 重载数组的输出运算符

C++——继承已经模板化的类?

c++ - mutex.timed_lock(duration) 和 boost::timed_mutex::scoped_lock 之间的区别 scoped_lock(mutex, duration)

c++ - CMake 不再查找静态 Boost 库