如何在现代C ++中存储具有不同签名的功能对象

时间:2014-12-05 14:25:28

标签: c++11 std-function

我想知道是否有一种方法可以在具有现代C ++的标准容器(std :: map)中存储具有不同签名的功能对象(函数,回调,...)。管理容器的库不知道其客户"将使用哪些签名。

我的需求与此处公开的内容相同:How to store functional objects with different signatures in a container?,此解决方案https://stackoverflow.com/a/8304873/4042960对我来说非常完美:我只想在没有提升的情况下做同样的事情。据我所知,没有std :: any。对我来说最好的解决方案是存储std :: function而不专门设置它们,但如果可能,我不知道该怎么做。

编辑:

通过你给我的答案,我写了这个例子:

#include <map>
#include <memory>
#include <functional>
#include <string>
#include <iostream>
#include <stdexcept>

class FunctionMap
{
    struct Base {
        virtual ~Base() {}
    };

    template<class R, class... Args>
    struct Func : Base
    {
        std::function<R(Args...)> f;
    };

    std::map<std::string, std::shared_ptr<Base> > _map;

public:

    template<class R, class... Args>
    void store(const std::string &key, const std::function<R(Args...)> &f) {
        auto pfunc = std::make_shared<Func<R, Args...> >();
        pfunc->f = f;
        _map.insert(std::make_pair(key, pfunc));
    }

    template<class R, class... Args>
    std::function<R(Args...)> get(const std::string &key) {
        auto pfunc = std::dynamic_pointer_cast<Func<R, Args...> >(_map[key]);
        if (pfunc)
            return pfunc->f;
        else
            throw std::runtime_error("Bad type for function's parameters");
    }
};

// test

int plus(int a, int b) { return a+b; }

double multiplies(double x, double y) { return x*y; }

int main()
{
    FunctionMap fm;

    fm.store("plus", std::function<int(int, int)>(&plus));
    fm.store("multiplies", std::function<double(double, double)>(&multiplies));
//    fm.store("square", std::bind(&multiplies, std::placeholders::_1, std::placeholders::_1));

    std::cout << "5 + 3 = " << fm.get<int, int, int>("plus")(5, 3) << std::endl;
    std::cout << "5 * 3 = " << fm.get<double, double, double>("multiplies")(5.0, 3.0) << std::endl;

    return 0;
}

这很好用,但我想稍微改进一下:

1)我希望能够使用std::bindfm.store("square", std::bind(&multiplies, std::placeholders::_1, std::placeholders::_1));但目前无法编译;

2)我想使用fm.get<int (int, int)>("plus")代替fm.get<int, int, int>("plus"),但我不知道该怎么做。

非常感谢你的帮助!

2 个答案:

答案 0 :(得分:1)

您可以自己编写any。如果没有所有编译器的解决方法和内容,boost::any可以用大约30行代码编写。

答案 1 :(得分:0)

函数对象与任何其他类型的对象没有任何区别,因此适用于对象的任何内容都适用于函数对象。

因此,您希望在地图中存储不同类型的(函数)对象。这通常通过将(智能)指针存储到基类来完成,其中每个派生类都拥有您想要存储的自己的对象类型。

struct Base {
  virtual ~Base(){}
};

template <typename A>
struct Object : Base {
  A value;
};

这是你的基本穴居人的提升::任何。您的客户做这样的事情:

Base* b = mymap["foo"];
dynamic_cast<Object<void(*)(int)>*>(b)->val(123);

但当然要进行适当的检查。