0

I would like to know if there is a way to store functional objects (functions, callbacks, ...) with different signatures in a standard container (std::map) with modern C++ only. The library that manages the container does not know which signatures will be used by its "clients".

My need is the same as exposed here : How to store functional objects with different signatures in a container?, and this solution https://stackoverflow.com/a/8304873/4042960 is about perfect for me: I would just like to do the same thing without boost. As far as I know, there is no std::any. The best solution for me would be to store std::function without specialized them, but I do not know how to do it, if it is possible.

Edit:

With the answers you give to me I wrote this example :

#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;
}

This works well, but I would like to improve it a bit:

1) I would like to be able to use std::bind : fm.store("square", std::bind(&multiplies, std::placeholders::_1, std::placeholders::_1)); but currently that does not compile ;

2) I would like to use fm.get<int (int, int)>("plus") instead of fm.get<int, int, int>("plus") but I do not know how to do it.

Many thanks for your help !

Community
  • 1
  • 1
merovingien
  • 319
  • 2
  • 9
  • Umm, `template` and `auto`? That's as close as you're going to get. – David Dec 05 '14 at 14:36
  • Why can't you use Boost.Any? – ecatmur Dec 05 '14 at 14:36
  • @David could you please give me an example or a link ? A solution based on auto and template should pleased me but I do not see how to develop it. – merovingien Dec 05 '14 at 16:58
  • @ecatmur you could ask why I need to store functions in a map... More seriously, not using boost is not my choice, and I hope that there is a better solution than using void* and old style cast, so my question. – merovingien Dec 05 '14 at 17:04
  • Boost.Any uses normal type erasure techniques, and that definitely includes `void` pointers and casting. If you can't use it for reasons of politics you can easily implement it yourself. – ecatmur Dec 05 '14 at 17:30
  • Also, since any is in the Library Fundamentals working draft for C++1z it might be worth checking with your vendor whether they have it available yet. – ecatmur Dec 05 '14 at 17:36

2 Answers2

1

You can write your own any. Without all the compiler workarounds and stuff, boost::any can be written in about 30 lines of code.

Sebastian Redl
  • 69,373
  • 8
  • 123
  • 157
  • Could you please give me a link you consider to be good practice to do that ? I am looking for a simple solution so 30 lines is ok, but if it is 200 lines... – merovingien Dec 05 '14 at 17:07
  • http://www.cplusplus.com/articles/oz18T05o/ looks quite good. `any` is a type erasure type with just a few operations: copy, move, query type, and return `void*` to contents. `any_cast` is implemented on top of that (check type and cast pointer if it matches). – Sebastian Redl Dec 05 '14 at 22:04
0

Function objects are in no way different from any other kind of objects, so anything applicable to objects in general is applicable to function objects.

So you want to store different kinds of (function) objects in a map. This is normally done by storing (smart) pointers to a base class, where each derived class holds its own kind of objects you want to store.

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

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

That's your basic caveman's boost::any. Your clients do something like this:

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

But with appropriate checks of course.

n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243