2009-09-04 33 views
1

读完this answer后,我想我有一个解决方案。至少答案是我想要做的,但是我在执行时遇到问题。当使用boost :: function :: target获取函数指针时的空指针

这里是什么,我试图做

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

程序没有错误或警告编译提纲,但我得到一个空指针(FPT)从f.target返回();在运行时。从上面的stackoverflow问题链接的引用看来,如果boost无法执行运行时转换,则返回空指针。但我不知道为什么Boost可能无法执行运行时转换。有任何想法吗?

+0

@Konrad对其他问题也解释了为什么你的代码失败:http://stackoverflow.com/questions/282372/demote-boostfunction-to-a-plain-function-pointer/512233 #512233 – 2009-09-04 20:01:45

+0

@HazyBlueDot - 这个问题是关于C++,而不是C.请尝试正确标记它。 – 2009-09-05 02:03:56

回答

2

为此,您需要知道存储到boost::function对象中的绑定表达式的确切类型。对象boost::bind(....)返回的是一些奇怪的表达式模板,而不是函数指针。

要理解为什么这是需要考虑的功能是如何提高::原则

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

这是最根本的结构来实现,而不operator()膨胀 - 很像boost::any。该机制称为type-erasure:构造函数接受任意类型的对象,然后将一个对象封装到一个对象中,您可以通过虚函数调用(boost::function被优化为地狱,使用自己的vtable和堆栈分配来避免小型的new等)。

对于函数指针,这很好用,因为您知道分配给boost::function对象的函数的类型。但对于复杂的可调用对象,它不再适用。

为了能够看到它的工作,并看到它只是函数指针的工作没有,但也与绑定表达式考虑下面的代码

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

get_target你知道什么boost::bind回报的类型。您可以使用它调用target调用并返回包装在boost::function内的对象。在main之内,我们然后调用绑定表达式。请阅读Eric Niebler的文章Conditional Love以了解此代码片段的工作原理。

+0

那么什么是适当的方式去做这件事?我想使用一个函数作为ftw的回调函数,但我也想传递一个指向数据结构的指针,这个数据结构就是回调函数的结果。看起来Boost.Bind应该能够解决这种问题,所以我该如何去做呢? – HazyBlueDot 2009-09-04 19:48:58

+0

为什么不制作'ftw'模板?然后它可以通过'boost :: bind'上的'boost :: function'函数指针接受任何东西。如果你不想使它成为一个模板,为什么不使用'boos :: function'作为它的参数类型?这就是它的目的:) – 2009-09-04 19:51:10

+0

我应该提供更多细节。 ftw是共享库中的一个函数(请参阅linux.die.net/man/3/ftw上的man ftw),我无法控制其参数列表。 – HazyBlueDot 2009-09-04 22:53:28

1

另一个答案指出为什么你的代码不起作用。这是一种非常难看的解决方案,适用于某些有限的情况。

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

使用指针作为模板参数的规则要求作为参数传入的指针是指向全局变量的指针。当然,该全局变量可以在匿名名称空间中声明。

这很丑陋,如果你想有几个可能的myMap实例可能会被调用,同时你需要尽可能多的全局MyFTWFunction变量同时发生myMap的实例。大多数情况下,这会自动创建使用全局变量内容填充缺失参数的thunk函数。

这里是一个版本,是少了很多灵活的,做大致相同的事情这条狭窄的情况下,可能使之更明显这是怎么回事上:

#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; 
} 

正如你可以看到myMap_binder是一个模板它会自动生成填充全局变量内容的thunk函数,以调用您的回调函数。