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

发布于 2024-08-04 02:42:00 字数 964 浏览 9 评论 0原文

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

这是我试图执行的

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

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

月寒剑心 2024-08-11 02:42:00

为此,您需要知道存储到 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: 的类型:绑定返回。您可以使用它来调用 target 调用并返回包装在 boost::function 内的对象。然后,我们在 main 中调用绑定表达式。请阅读 Eric Niebler 的文章 Conditional Love 以了解此代码片段的工作原理。

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.

慕巷 2024-08-11 02:42:00

另一个答案指出了为什么您的代码不起作用。对于某些有限的情况,这是一个非常丑陋的解决方案。

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 函数将全局变量放入对回调函数的调用中。

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.

凝望流年 2024-08-11 02:42:00

这虽然晚了几年,但也许对你将来会有帮助。我的问题略有不同,但你仍然可以从解决方案中得到你想要的答案;阅读此处:
> 消息系统:回调可以是任何内容

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文