【问题标题】:Storing function pointers with different types c++ boost::bindc++ boost::bind 存储不同类型的函数指针
【发布时间】:2014-10-21 20:32:50
【问题描述】:

我今天挖了很多东西,结果还是空的。有没有办法存储从不同类型的 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.

谢谢。

【问题讨论】:

  • 我不这么认为,但我不确定是否可以作为答案发布。
  • 问题是您从未真正从变体中检索值。您需要使用apply_visitor 或使用get()。通过documentation 再看一眼。您正在尝试将 () 运算符应用于变体本身,而不是它包含的内容。

标签: c++11 function-pointers boost-bind boost-variant


【解决方案1】:

你有多态函子(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;
}

打印

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

Live On Coliru

更多背景见:


完整样本

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

【讨论】:

  • 帮助很大。我有一个问题。有没有办法扩展 ErasedFuntor 以包含对在初始化时采用 2 个元素而不是 1 个元素的结构的调用(如 Foo 和 Bar 或 Baz(2,1.2))?跨度>
  • 已经支持了。 ErasedFunction 只关心可调用对象的数量:Live On Coliru
  • 我想我应该准确地包含我传入的内容。Baz&lt;std::normal_distribution&lt;double&gt;, std::std::mt19937&gt;( std::normal_distribution&lt;double&gt;(0.0, 3.0), std::mt19937(10)) 这似乎引起了一些问题。我尝试扩展Operand,但这并没有解决任何问题。再次感谢@sehe 的帮助。
  • @PeterHubner 我不确定是什么阻止您修改 Coliru 上的示例:working due to mutable state
  • 咳咳,再次感谢。我隔开,甚至没有想过要更新样本。你的答案是正确的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多