Null pointer when getting function pointer using boost :: function :: target

After reading this answer, I thought I had a solution. At least the answer is what I would like to do, but I had a problem with the implementation.

here is the outline of what I'm trying to do

typedef map<string, double*> myMap; typedef int (*ftwpt)(const char*, const struct stat*, int); typedef boost::function<int(const char*, const struct stat*, int)> MyFTWFunction; int myFunction(const char*, const struct stat*, int, myMap*); int main() { myMap m_map; char tmpdir[] = "/tmp/mytmp"; MyFTWFunction f = boost::bind(myFunction,_1,_2,_3, &m_map); ftwpt* fpt = f.target<ftwpt>(); if (fpt) status = ftw(tmpdir, *fpt, 50); else { cout << "Boost could not perform runtime conversion on function pointer" << endl; return (EXIT_FAILURE); } } 

the program compiles without errors or warnings, but I get a null pointer (fpt) returned from f.target (); at runtime. From the links related to the stackoverflow question above, it seems like a null pointer is returned if boost cannot perform the conversion at runtime. But I have no idea why Boost might not be able to perform the conversion at runtime. Any ideas?

+1
c ++ boost function-pointers boost-bind
source share
3 answers

To do this, you will need to find out the exact type of binding expression that you store in the boost::function object. The boost::bind(....) object returns some strange expression pattern, not a function pointer.

To understand why this is necessary, consider how, in principle, the boost :: function function is implemented

 struct base { virtual ~base() { } }; template<typename T> struct derived : base { derived(T t):t(t) { } T t; }; struct function { template<typename T> function(T t) { base *b = new derived<T>(t); } template<typename T> T *target() { if(typeid(*b) == typeid(derived<T>)) return &static_cast< derived<T>* >(b)->t; return 0; } base *b; }; 

That the most fundamental structure, without bloating operator() , is very similar to boost::any . The mechanism is called type erasure: the constructor accepts objects of arbitrary types, and then saves the object encapsulated in the object, which you can perform through calls to virtual functions ( boost::function optimized like hell, using its own vtable and stack distribution to avoid new for small types etc.).

For function pointers, this works great because you know the type of function that you assign to the boost::function object. But for complex callable objects, it no longer works.

To see how it works, and to make sure that it not only works with function pointers, but also with binding expressions, consider the following code

 template<typename T> struct id { typedef T type; }; template<typename T> id<T> make_id(T) { return id<T>(); } struct any_type { template<typename T> operator id<T>() const { return id<T>(); } }; template<typename T, typename Fn> T *get_target(boost::function<Fn> &f, id<T>) { return f.template target<T>(); } void f(int a, int b) { std::cout << a << " " << b << std::endl; } int main() { boost::function<void(int)> g = boost::bind(&f, _1, 10); (*get_target(g, true ? any_type() : make_id(boost::bind(&f, _1, 10))))(2); } 

Inside get_target you know the type of boost::bind returned. You can use this to call the target call and return the object that was enclosed inside boost::function . Inside main we invoke the bind expression. Please read Eric Nibler's article โ€œConditional Loveโ€ to see how this piece of code works.

+2
source share

Another answer indicates why your code is not working. Here is a really ugly solution, kind of, for certain limited situations.

 typedef int (*ftwpt)(const char*, const struct stat*, int); typedef boost::function<int(const char*, const struct stat*, int)> MyFTWFunction; template <MyFTWFunction *callback> class callback_binder { public: static int callbackThunk(const char *s, const struct stat *st, int i) { return (*callback)(s, i); } }; extern void register_callback(callback_t f); int random_func(const char *s, const struct stat *st, int i) { if (s && *s) { return i; } else { return -1; } } MyFTWFunction myfunc; int main(int argc, const char *argv[]) { myfunc = random_func; register_callback(&callback_binder<&myfunc>::callbackThunk); return 0; } 

Rules for using pointers as template arguments require that the pointer passed as an argument be a pointer to a global variable. Of course, this global variable can be declared in an anonymous namespace.

This is ugly, and if you wanted to have several possible instances of myMap that could be called back, at the same time you would need as many global MyFTWFunction variables at the same time as myMap instances. It basically automates the creation of a thunk function that uses the contents of a global variable to populate the missing parameter.

Here is a version that LOT is less flexible, which does roughly the same for this narrow case, which may make what happens here more obvious:

 #include <map> #include <string> using ::std::map; using ::std::string; typedef map<string, double*> myMap; typedef int (*callback_t)(const char *, struct stat *st, int); int myFunction(const char*, struct stat *st, int, myMap*); template <myMap **map_ptr> class myMap_binder { public: static int call_my_function(const char *s, struct stat *st, int i) { return myFunction(s, st, i, *map_ptr); } }; extern void register_callback(callback_t f); myMap *mainmap; myMap *othermap; int main(int argc, const char *argv[]) { myMap m_map; myMap m_map2; mainmap = &m_map; othermap = &m_map2; register_callback(&myMap_binder<&mainmap>::call_my_function); register_callback(&myMap_binder<&othermap>::call_my_function); return 0; } 

As you can see, myMap_binder is a template that automatically generates thunk functions that contain the contents of a global variable into a call to your callback function.

+1
source share

This is a few years later, but perhaps it will help you in the future. My problem was slightly different, but you can still get the answer you want from the solution; read here:
> Messaging system: callbacks can be any

0
source share

All Articles