问题
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