1

阅读此答案后,我认为我有一个解决方案。至少答案是我想做的,但我在实施时遇到了问题。

这是我正在尝试做的事情的大纲

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 可能无法执行运行时转换。有任何想法吗?

4

3 回答 3

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以了解此代码片段是如何工作的。

于 2009-09-04T19:33:00.420 回答
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 函数,这些函数将全局变量的内容填充到对回调函数的调用中。

于 2009-09-05T01:54:09.763 回答
0

这已经晚了几年,但也许它会在未来帮助你。我的问题略有不同,但你仍然可以从解决方案中得到你想要的答案;在这里阅读:
>消息系统:回调可以是任何东西

于 2011-07-31T16:15:57.770 回答