I'm trying to write a function that will take a functor as an argument, invoke the functor and then return its return value wrapped in a boost::shared_ptr.
The following refuses to compile and I'm all out of ideas. I get "std::vector< std::string > does not provide a call operator" (roughly). I'm using Clang 3.1 on Mac OS X.
template< typename T >
boost::shared_ptr< T > ReturnValueAsShared(
boost::function< T() > func )
{
return boost::make_shared< T >( func() );
}
This is the context in which I'm trying to use it:
make_shared< packaged_task< boost::shared_ptr< std::vector< std::string > > > >(
bind( ReturnValueAsShared< std::vector< std::string > >,
bind( [a function that returns a std::vector< std::string >] ) ) );
EDIT: Here's a complete self-contained test case. This code fails to compile with the same error, and for the life of me I can't see what's wrong:
#include <boost/make_shared.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/function.hpp>
#include <boost/bind.hpp>
#include <string>
#include <vector>
std::vector< std::string > foo( std::string a )
{
std::vector< std::string > vec;
vec.push_back( a );
return vec;
}
template< typename T >
boost::shared_ptr< T > ReturnValueAsShared(
boost::function< T() > func )
{
return boost::make_shared< T >( func() );
}
int main()
{
auto f = boost::bind( ReturnValueAsShared< std::vector< std::string > >,
boost::bind( foo, std::string("a") ) );
f();
} // main
And here's the error output:
In file included from testcase.cpp:3:
In file included from /usr/local/include/boost/function.hpp:64:
In file included from /usr/local/include/boost/preprocessor/iteration/detail/iter/forward1.hpp:47:
In file included from /usr/local/include/boost/function/detail/function_iterate.hpp:14:
In file included from /usr/local/include/boost/function/detail/maybe_include.hpp:13:
/usr/local/include/boost/function/function_template.hpp:132:18: error: type 'std::vector<std::basic_string<char>, std::allocator<std::basic_string<char> > >' does not provide a call operator
return (*f)(BOOST_FUNCTION_ARGS);
^~~~
/usr/local/include/boost/function/function_template.hpp:907:53: note: in instantiation of member function 'boost::detail::function::function_obj_invoker0<std::vector<std::basic_string<char>, std::allocator<std::basic_string<char> > >, std::vector<std::basic_string<char>, std::allocator<std::basic_string<char> > > >::invoke' requested here
{ { &manager_type::manage }, &invoker_type::invoke };
^
/usr/local/include/boost/function/function_template.hpp:722:13: note: in instantiation of function template specialization 'boost::function0<std::vector<std::basic_string<char>, std::allocator<std::basic_string<char> > > >::assign_to<std::vector<std::basic_string<char>, std::allocator<std::basic_string<char> > > >' requested here
this->assign_to(f);
^
/usr/local/include/boost/function/function_template.hpp:1042:5: note: in instantiation of function template specialization 'boost::function0<std::vector<std::basic_string<char>, std::allocator<std::basic_string<char> > > >::function0<std::vector<std::basic_string<char>, std::allocator<std::basic_string<char> > > >' requested here
base_type(f)
^
/usr/local/include/boost/bind/bind.hpp:243:43: note: in instantiation of function template specialization 'boost::function<std::vector<std::basic_string<char>, std::allocator<std::basic_string<char> > > ()>::function<std::vector<std::basic_string<char>, std::allocator<std::basic_string<char> > > >' requested here
return unwrapper<F>::unwrap(f, 0)(a[base_type::a1_]);
^
/usr/local/include/boost/bind/bind_template.hpp:20:27: note: in instantiation of function template specialization 'boost::_bi::list1<boost::_bi::bind_t<std::vector<std::basic_string<char>, std::allocator<std::basic_string<char> > >, std::vector<std::basic_string<char>, std::allocator<std::basic_string<char> > > (*)(std::basic_string<char>), boost::_bi::list1<boost::_bi::value<std::basic_string<char> > > > >::operator()<boost::shared_ptr<std::vector<std::basic_string<char>, std::allocator<std::basic_string<char> > > >, boost::shared_ptr<std::vector<std::basic_string<char>, std::allocator<std::basic_string<char> > > > (*)(boost::function<std::vector<std::basic_string<char>, std::allocator<std::basic_string<char> > > ()>), boost::_bi::list0>' requested here
BOOST_BIND_RETURN l_(type<result_type>(), f_, a, 0);
^
testcase.cpp:27:4: note: in instantiation of member function 'boost::_bi::bind_t<boost::shared_ptr<std::vector<std::basic_string<char>, std::allocator<std::basic_string<char> > > >, boost::shared_ptr<std::vector<std::basic_string<char>, std::allocator<std::basic_string<char> > > > (*)(boost::function<std::vector<std::basic_string<char>, std::allocator<std::basic_string<char> > > ()>), boost::_bi::list1<boost::_bi::bind_t<std::vector<std::basic_string<char>, std::allocator<std::basic_string<char> > >, std::vector<std::basic_string<char>, std::allocator<std::basic_string<char> > > (*)(std::basic_string<char>), boost::_bi::list1<boost::_bi::value<std::basic_string<char> > > > > >::operator()' requested here
f();
^
1 error generated.
Here are some more clues. The following code compiles just fine, but that doesn't help me since this is not the code that I want :)
#include <boost/make_shared.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/function.hpp>
#include <boost/bind.hpp>
#include <string>
#include <vector>
std::vector< std::string > foo()
{
std::vector< std::string > vec;
return vec;
}
template< typename T >
boost::shared_ptr< T > ReturnValueAsShared(
boost::function< T() > func )
{
return boost::make_shared< T >( func() );
}
int main()
{
auto f = boost::bind( ReturnValueAsShared< std::vector< std::string > >,
foo );
f();
} // main
boost::protect is the way to go:
int main()
{
auto f = boost::bind( ReturnValueAsShared< std::vector< std::string > >,
boost::protect(boost::bind( foo, std::string("a") ) ) );
f();
} // main
This is as clean as it can get.
Some constructions (such as bind) return intermediate "expression" types which you don't actually want to capture on the nose. In that case, you mustn't capture the type via auto, and you may need to specify explicit conversions, since otherwise there isn't a unique, single user-defined conversion chain. In your case, add the explicit conversion from the bind expression to function:
typedef std::vector<std::string> G;
auto f = boost::bind(ReturnValueAsShared<G>,
static_cast<boost::function<G()>(boost::bind(foo, std::string("a")))
);
(This itself doesn't actually work for me, but it does work if you use the corresponding std constructions.)
Complete rewrite, original answer was incorrect.
As I didn't know at first what was going wrong here, I did some analysis. I'm keeping it for future reference; see the solution below for how to avoid the problem.
bind.hpp does this:
return unwrapper<F>::unwrap(f, 0)(a[base_type::a1_]);
which in my opinion translates like this:
unwrapper<F>::unwrap(f, 0) = ReturnValueAsShared< std::vector< std::string > >
base_type::a1_ = boost::bind( foo, std::string("a") )
So what you would expect this code to do is pass the argument to the function, just the way it is. But for this to work, the expression a[base_type::a1_] would have to be of type boots:_bi::value<T>, whereas it is of the unwrapped type boost::_bi::bind_t. So instead of the functor being passed as the argument, a special overloaded version gets called:
namespace boost { namespace _bi { class list0 {
…
template<class R, class F, class L>
typename result_traits<R, F>::type
operator[] (bind_t<R, F, L> & b) const {
return b.eval(*this);
}
…
} } }
This will evaluate the nullary function, instead of passing it on. So instead of an object returning a vector, the argument now is a vecotr. Subsequent steps will attempt to convert that to a boost::function and fail.
Edited yet again:
It looks like this special handling of nested binds is intended as a feature. Talking on #boost with users Zao and heller, I now know that there is a protect function to counter these effects. So the canonical solution to this problem appears to be the following:
…
#include <boost/bind/protect.hpp>
…
auto f = boost::bind( ReturnValueAsShared< std::vector< std::string > >,
boost::protect ( boost::bind( foo, std::string("a") ) ) );
…
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With