如何概括调用C ++中的函数列表?

时间:2017-08-18 08:37:05

标签: c++ templates chain-of-responsibility

我有以下代码,允许我实例化然后调用void()函数列表。

(如果您希望编译并运行此代码,我使用https://github.com/philsquared/Catch进行单元测试。)

#include "catch.hpp"

#include <functional>
#include <vector>

class ChainOfResponsibility : public std::vector<std::function<void()> >, public std::function<void()>
{
public:
    void operator()() const
    {
        for(std::vector<std::function<void()> >::const_iterator it = begin(); it != end(); ++it) {
            (*it)();
        }
    }
};

TEST_CASE("ChainOfResponsibility calls its members when invoked")
{
    bool test_function_called = false;
    std::function<void()> test_function = [&]()
    {
        test_function_called = true;
    };

    ChainOfResponsibility object_under_test;

    object_under_test.push_back(test_function);
    object_under_test();

    REQUIRE(test_function_called);
}

我的问题是如何模拟ChainOfResponsibility类接受具有不同(但一致)签名的函数?

例如,考虑ChainOfResponsibility<void(int)>ChainOfResponsibility<ReturnClass(Argument1Class, Argument2Class)>

为了参数,我们假设第二个示例返回链中最后一个成员返回的值,如果链为空,则返回ReturnClass的默认值。

另外,如果STL已经包含了实现此目的的模板类,那么我更愿意在我的本土课程中使用它。

2 个答案:

答案 0 :(得分:7)

您的具体“丢弃所有中间结果”也相当简单,但我认为这是一个坏主意。

template<typename Ret, typename ... Args>
class ChainOfResponsibility
{
    std::vector<std::function<Ret(Args...)> > chain;
public:
    Ret operator()(Args ... args) const
    {
        Ret value;
        for(auto & func : chain) {
            value = func(args...);
        }
        return value;
    }
};

void必须自行处理

template<typename ... Args>
class ChainOfResponsibility<void, Args...>
{
    std::vector<std::function<void(Args...)> > chain;
public:
    void operator()(Args ... args) const
    {
        for(auto & func : chain) {
            func(args...);
        }
    }
};

请注意,从std::类型派生是一个坏主意,尤其是std::function,这是一种类型擦除可调用,而不是“所有可调用的基础”。您只需提供operator()

即可

改进非空洞案例的选项:

    // fold the results
    template <typename BinaryFunction>
    Ret operator()(Args ... args, BinaryFunction add, Ret init) const
    {
        for(auto & func : chain) {
            init = add(init, func(args...));
        }
        return init;
    }

    // return a vector
    template <typename BinaryFunction>
    std::vector<Ret> operator()(Args ... args) const
    {
        std::vector<Ret> results(chain.size());
        for(auto & func : chain) {
            results.push_back(func(args...));
        }
        return results;
    }

答案 1 :(得分:1)

您不需要使用std::function作为基类,使用std::vector就足够了。模板ChainOfResponsibility可以使用与std::function相同的模板参数列表,如下所示:

#include <iostream>
#include <string>
#include <functional>
#include <vector>

template<typename>
class ChainOfResponsibility;

template<typename R, typename... Args>
class ChainOfResponsibility<R(Args...)> :
        public std::vector<std::function<R(Args...)>> {
public:
    R operator()(const Args&... args) {
        R result {};

        for(auto it = this->begin(); it != this->end(); ++it)
            result = (*it)(args...);

        return result;
    }
};

int main() {
    ChainOfResponsibility<std::string(int, int)> tester;

    tester.push_back([](int a, int b)->std::string {
        return std::to_string(a + b);
    });

    std::cout << tester(4, 2) << std::endl;
}

无论如何,仅使用std::vector对于您描述的问题是很好的。如果重载operator()的内容没有什么特别之处,您可以按如下方式更改我的示例:

int main() {
    std::vector<std::function<std::string(int, int)>> tester;

    tester.push_back([](int a, int b)->std::string {
        return std::to_string(a + b);
    });

    std::string result;

    for(auto& test_fn : tester)
        result = test_fn(4, 2);

    std::cout << result << std::endl;
}

您还可以编写函数模板,而不是重载operator()

template<typename R, typename... Args>
R perform(const std::vector<std::function<R(Args...)>>& functions,
        const Args&... args) {
    R result {};
    for(auto& test_fn : functions)
        result = test_fn(4, 2);
    return result;
}