2012-07-15 14 views
8

Estoy intentando escribir una función que tomará un funtor como argumento, invocará el funtor y luego devolverá su valor devuelto en boost::shared_ptr ."<class name> no proporciona un operador de llamada" error al intentar ajustar el valor de retorno de la función

El siguiente se niega a compilar y estoy libre de ideas. Obtengo "std :: vector < std :: string> no proporciona un operador de llamada" (más o menos). Estoy usando Clang 3.1 en Mac OS X.

template< typename T > 
boost::shared_ptr<T> ReturnValueAsShared(
    boost::function< T() > func) 
{ 
    return boost::make_shared<T>(func()); 
} 

Este es el contexto en el que estoy tratando de usarlo:

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: He aquí un caso completo de prueba autónomo. Este código falla al compilar con el mismo error, y para la vida de mí no puedo ver lo que está mal:

#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 

Y aquí está la salida de error:

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. 

Aquí hay algunas pistas más. El código siguiente se compila bien, pero eso no me ayuda ya que este no es el código que quiero :)

#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 
+3

Simplifique el problema y proporcione un código de ejemplo mínimo (que no funcione). –

+0

¿Cuál es el error? –

+0

@KerrekSB El error es 'std :: vector no proporciona un operador de llamada' cuando intento usarlo como' ReturnValueAsShared > '. – Lucas

Respuesta

2

Algunas construcciones (como bind) regresan tipos intermedios "expresión", que en realidad no se desea capturar en la nariz. En ese caso, no debe capturar el tipo a través del auto, y es posible que deba especificar conversiones explícitas, ya que de lo contrario no existe una única cadena de conversión única definida por el usuario. En su caso, añadir la conversión explícita de la expresión se unen a function:

typedef std::vector<std::string> G; 
auto f = boost::bind(ReturnValueAsShared<G>, 
      static_cast<boost::function<G()>(boost::bind(foo, std::string("a"))) 
        ); 

(Esto en sí en realidad no funciona para mí, pero funciona si se utiliza el correspondiente std construc ­ ciones.)

+0

Envolviendo mi segunda llamada 'bind' con un molde' boost :: function ()> (...) 'hace que todo funcione bien. Gracias por la info. – Lucas

2

reescritura completa, respuesta original era incorrecta.

El análisis de errores

Como yo no sabía en un principio lo que estaba pasando mal aquí, he hecho un poco de análisis. Lo guardo para futuras referencias; vea la solución a continuación para saber cómo evitar el problema.

bind.hpp hace esto:

return unwrapper<F>::unwrap(f, 0)(a[base_type::a1_]); 

que en mi opinión se traduce así:

unwrapper<F>::unwrap(f, 0) = ReturnValueAsShared< std::vector<std::string> > 
base_type::a1_ = boost::bind(foo, std::string("a")) 

Entonces, ¿qué se puede esperar este código para hacer es pasar el argumento de la función, de la manera es. Pero para que esto funcione, la expresión a[base_type::a1_] debería ser del tipo boots:_bi::value<T>, mientras que es del tipo boost::_bi::bind_t sin empaquetar. Así que en lugar de la funtor que se pasa como argumento, una versión sobrecargada especial se llama:

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

Esto le evaluar la función nularia, en lugar de transmitirla. Entonces, en lugar de que un objeto devuelva un vector, el argumento ahora es un vecotr. Los pasos subsiguientes intentarán convertir eso a boost::function y fallarán.

solución canónica

Editado una vez más:
Parece que este manejo especial de nested binds pretende ser una característica. Hablando en #boost con los usuarios Zao y Heller, ahora sé que hay una función protect para contrarrestar estos efectos.Así que la solución canónica a este problema parece ser la siguiente:

… 
#include <boost/bind/protect.hpp> 
… 
    auto f = boost::bind(ReturnValueAsShared< std::vector<std::string> >, 
    boost::protect (boost::bind(foo, std::string("a")))); 
… 
+0

No, el valor devuelto es un funtor que cuando se ejecuta devuelve un 'shared_ptr'. Si reemplazo 'f()' con '(* f)()', con razón me sale un error del compilador que se queja de que 'f' no es de tipo puntero. – Lucas

+0

OK, eres ightight, el problema es un nivel más profundo. Me parece que 'bind 'no tiene sobrecarga para' shared_ptr', por lo que asume un objeto de función por defecto. Pero como 'shared_ptr' no proporciona un operador de llamada, las cosas fallan. – MvG

+0

Um, 'bind' no necesita una sobrecarga para' shared_ptr', es una plantilla. Además, el error se queja de que 'vector ' no tiene un operador de llamada, no un 'shared_ptr'. – Lucas

3

impulso :: proteger es el camino a seguir:

int main() 
{ 
    auto f = boost::bind(ReturnValueAsShared< std::vector<std::string> >, 
         boost::protect(boost::bind(foo, std::string("a")))); 
    f(); 

} // main 

Esto es tan limpia como se puede conseguir.

+1

Esta respuesta podría mejorarse mediante una explicación de qué es 'boost :: protect', qué hace y quizás un enlace a los documentos que lo describen con más detalle. – Lucas

Cuestiones relacionadas