c++ boost::bind 存储不同类型的函数指针

2024-03-08

我今天查了很多资料,却一无所获。有什么方法可以存储从 boost::bind 不同类型返回的仿函数吗?我找到了一个使用 boost::variants 的示例,但不确定是否需要这样做。 (为简单起见,Foo 和 Bar 已被简化)

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

编译器输出:

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.


您有多态函子(Foo 和 Bar)。

您想要为一组特定的操作数类型键入擦除它们。我建议为此目的定义一个类型擦除的函子类型:

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

现在您可以简单地将函数对象直接存储在映射中:

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

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

让我们使用at("foo")代替["foo"]以避免必须使ErasedFunctor默认可构造:

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 住在科里鲁 http://coliru.stacked-crooked.com/a/2771244b1fb13e24

更多背景请参见:

  • 生成没有虚拟功能的接口? https://stackoverflow.com/questions/18859699/generating-an-interface-without-virtual-functions/18859931#18859931

完整样本

#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;
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

c++ boost::bind 存储不同类型的函数指针 的相关文章

随机推荐