问题
After reading this answer I thought I had a solution. At least the answer there is what I would like to do but I'm having a problem with the implementation.
here is an outline of what I am 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 with no errors or warnings but I am getting a null pointer (fpt) returned from f.target(); at runtime. From references linked on the above stackoverflow question it seems a null pointer is returned if boost is unable to perform the runtime conversion. But I have no idea why Boost might not be able to perform the runtime conversion. Any ideas?
回答1:
For that to work, you would need to know the exact type of the bind expression that you store into the boost::function
object. The object boost::bind(....)
returns is some weird expression template, not a function pointer.
To understand why this is needed, consider how boost::function is implemented in principle
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's the most fundamental structure, without the operator()
bloat - much like boost::any
. The mechanism is called type-erasure: The constructor accepts objects of arbitrary types, and then stores an object encapsulated into an object that you may reach through virtual function calls (boost::function
is optimized like hell, using its own vtable and stack-allocation to avoid new
for small types and so on).
For function pointers, this works great, because you know the type of the function that you assign to the boost::function
object. But for complex callable objects, it doesn't quite work anymore.
To be able to see it working and to see that it's not just working with function pointers, but also with bind 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);
}
Within get_target
you know the type of what boost::bind
returns. You can use that to call the target
call and return the object that's wrapped inside the boost::function
. Within main
we then call the bind expression. Please read Eric Niebler's article Conditional Love to see how this code snippet works.
回答2:
The other answer points out why your code doesn't work. Here is a really ugly solution that sort of does, 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;
}
The rules for using pointers as template arguments require that the pointer passed in as an argument be a pointer to a global variable. That global variable can, of course, be declared in an anonymous namespace.
It's ugly, and if you wanted to have several possible instances of myMap possible called back with at the same time you'd need as many global MyFTWFunction variables as possible simultaneous instances of myMap. Mostly this automates the creation of a thunk function that uses the content of a global variable to fill in the missing parameter.
Here is a version that is a LOT less flexible that does approximately the same thing for this narrow case that may make it more obvious what's going on here:
#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 auto-generates thunk functions that stuff in the contents of a global variable into a call to your callback function.
回答3:
This is a few years late, but maybe it'll 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 anything
来源:https://stackoverflow.com/questions/1381042/null-pointer-when-getting-function-pointer-using-boostfunctiontarget