使用boost :: function :: target获取函数指针时的空指针

时间:2009-09-04 19:13:45

标签: c++ boost function-pointers boost-bind

在阅读this answer之后,我认为我有一个解决方案。至少答案就是我想做的事情,但我遇到了实施问题。

这里是我想要做的概述

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

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

3 个答案:

答案 0 :(得分:2)

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

要理解为什么需要这样做,请考虑原则上如何实现boost :: function

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。该机制称为类型擦除:构造函数接受任意类型的对象,然后将封装到对象中的对象存储到您可以通过虚函数调用到达的对象(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,了解此代码段的工作原理。

答案 1 :(得分: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函数,这些函数将全局变量的内容填充到对回调函数的调用中。

答案 2 :(得分:0)

这已经晚了几年,但也许它将来会对你有所帮助。我的问题略有不同,但你仍然可以从解决方案中得到你想要的答案;在这里阅读:
&GT; Messaging system: Callbacks can be anything