Storing function pointers with different types c++ boost::bind

ⅰ亾dé卋堺 提交于 2019-12-24 02:42:37

问题


I have dug around quite a bit today and have come up empty. Is there any way to store a functor that is returned from a boost::bind with different types? I found an example that used boost::variants but not sure that this is needed. (Foo and Bar have been simplified for simplicity sake)

#include <boost/bind.hpp>
#include <boost/variant.hpp>
#include <boost/function.hpp>

#include <map>
#include <iostream>

template <typename FooType>
struct Foo {
    const FooType tmp_value;

    Foo(const FooType& tmp_) :
    tmp_value(tmp_)
    {
    }

    template<typename Object>
    void operator()(Object& operand)
    {
        std::cout << operand << std::endl;
        operand += tmp_value;
    }
};

template <typename BarType>
struct Bar {
    const BarType tmp_value;

    Bar(const BarType& tmp_) :
    tmp_value(tmp_)
    {
    }

    template<typename Object>
    void operator()(Object& operand)
    {
        std::cout << operand << std::endl;
        operand -= tmp_value;
    }
};

typedef boost::variant<
    boost::function<void(int32_t)>,
    boost::function<void(int64_t)>,
    boost::function<void(double)>,
    boost::function<void(float)>
> my_functions;

typedef std::map<std::string, my_functions> test_map;

enum test_t {
    FOO,
    BAR
};

test_map createFunMap() {
  test_map result;

    for(int i = 0; i < 2; i++) {
        switch(i) {
            case test_t::FOO: {
                std::cout << "In FOO" << std::endl;
                Foo<double> p(1.0);
                result.insert(std::pair<std::string, 
                                        boost::function<void(double)>>
                                        ("foo", boost::bind<void>(p, _1)));
              break;
            }
            case test_t::BAR: {
                std::cout << "In BAR" << std::endl;
                Bar<int32_t> p(1.0);
                result.insert(std::pair<std::string, 
                                        boost::function<void(int32_t)>>
                                        ("bar", boost::bind<void>(p, _1)));
              break;
            }
            default:
              std::cout << "just a default" << std::endl;
              break;
        }
    }

  return result;
}

int main() {
    test_map myMap;
    double t = 5.0;

    myMap = createFunMap();

    std::cout << t << std::endl;
    myMap["foo"](t);
    std::cout << t << std::endl;

    return 0;
}

compiler output:

g++ -Wall --std=c++0x -I. test_ptrs.cc -o test_ptrs
test_ptrs.cc:93:2: error: type 'mapped_type' (aka 'boost::variant<boost::function<void (int)>,         boost::function<void (long long)>, boost::function<void (double)>, boost::function<void (float)>,
  boost::detail::variant::void_, boost::detail::variant::void_, boost::detail::variant::void_, boost::detail::variant::void_, boost::detail::variant::void_, boost::detail::variant::void_,
  boost::detail::variant::void_, boost::detail::variant::void_, boost::detail::variant::void_, boost::detail::variant::void_, boost::detail::variant::void_, boost::detail::variant::void_,
  boost::detail::variant::void_, boost::detail::variant::void_, boost::detail::variant::void_, boost::detail::variant::void_>') does not provide a call operator
    myMap["foo"](t);
    ^~~~~~~~~~~~
1 error generated.

Thanks.


回答1:


You have polymorphic functors (Foo and Bar).

You want to type erase them for a certain set of operand types. I suggest defining a type-erased functor type for the purpose:

struct ErasedFunctor
{
    template<typename F> ErasedFunctor(F&& f) 
        : pimpl(new impl_<F>(std::forward<F>(f))) {}

    template <typename T>
    void operator()(T& oper) const {
        assert(pimpl);
        pimpl->call(oper);
    }

  private:
    typedef boost::variant<int32_t&, int64_t&, double&, float&> Operand;

    struct base_ { virtual void call(Operand oper) const = 0; };
    // struct impl_ : base_ ... 

    std::shared_ptr<base_> pimpl;
};

Now you can simply store the function objects directly in the map:

typedef std::map<std::string, ErasedFunctor> test_map;

test_map createFunMap() {
    return test_map { 
        { "foo", Foo<double>(1.0) },
        { "bar", Bar<int32_t>(1)  },
    };
}

Let's use at("foo") instead of ["foo"] to avoid having to make ErasedFunctor default-constructible:

int main() {
    test_map myMap = createFunMap();
    double t = 5.0;

    std::cout << t << std::endl;
    myMap.at("foo")(t);
    std::cout << t << std::endl;
    myMap.at("bar")(t);
    std::cout << t << std::endl;
}

Prints

5
void ErasedFunctor::apply::operator()(const F&, T&) const [with F = Foo<double>; T = double](5)
5
6
void ErasedFunctor::apply::operator()(const F&, T&) const [with F = Bar<int>; T = double](6)
6
5

See it Live On Coliru

For more background see:

  • Generating an interface without virtual functions?

Full Sample

#include <boost/bind.hpp>
#include <boost/variant.hpp>
#include <iostream>

template <typename FooType> struct Foo {
    const FooType tmp_value;

    Foo(const FooType &tmp_) : tmp_value(tmp_) {}

    template <typename Object> void operator()(Object &operand) const {
        std::cout << operand << std::endl;
        operand += tmp_value;
    }
};

template <typename BarType> struct Bar {
    const BarType tmp_value;

    Bar(const BarType &tmp_) : tmp_value(tmp_) {}

    template <typename Object> void operator()(Object &operand) const {
        std::cout << operand << std::endl;
        operand -= tmp_value;
    }
};

struct ErasedFunctor
{
    template<typename F> ErasedFunctor(F&& f) 
        : pimpl(new impl_<F>(std::forward<F>(f))) {}

    template <typename T>
    void operator()(T& oper) const {
        assert(pimpl);
        pimpl->call(oper);
    }

  private:
    typedef boost::variant<int32_t&, int64_t&, double&, float&> Operand;

    struct base_ { virtual void call(Operand oper) const = 0; };

    struct apply : boost::static_visitor<void> {
        template <typename F, typename T> void operator()(F const& f, T& v) const {
            std::cout << __PRETTY_FUNCTION__ << "(" << v << ")\n";
            f(v);
        }
    };

    template <typename F> struct impl_ : base_ {
        F f_;
        impl_(F&& f) : f_(std::forward<F>(f)) { }
        virtual void call(Operand oper) const override {
            boost::apply_visitor(boost::bind(apply(), boost::cref(f_), _1), oper);
        }
    };

    std::shared_ptr<base_> pimpl;
};

#include <map>
typedef std::map<std::string, ErasedFunctor> test_map;

test_map createFunMap() {
    return test_map { 
        { "foo", Foo<double>(1.0) },
        { "bar", Bar<int32_t>(1)  },
    };
}

int main() {
    test_map myMap = createFunMap();
    double t = 5.0;

    std::cout << t << std::endl;
    myMap.at("foo")(t);
    std::cout << t << std::endl;
    myMap.at("bar")(t);
    std::cout << t << std::endl;
}


来源:https://stackoverflow.com/questions/26495784/storing-function-pointers-with-different-types-c-boostbind

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!