【问题标题】:null pointer when getting function pointer using boost::function::target使用 boost::function::target 获取函数指针时的空指针
【发布时间】:2009-09-04 19:13:44
【问题描述】:

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

【问题讨论】:

  • @Konrad 在另一个问题上也解释了您的代码失败的原因:stackoverflow.com/questions/282372/…
  • @HazyBlueDot - 这个问题是关于 C++,而不是 C。请尝试正确标记它。

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


【解决方案1】:

为此,您需要知道存储到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 以了解此代码 sn-p 的工作原理。

【讨论】:

  • 那么解决这个问题的合适方法是什么?我想使用一个函数作为ftw的回调函数,但我也想传递一个指向回调并放入结果的数据结构的指针。看起来Boost.Bind应该能够解决这种问题,所以我该怎么做?
  • 为什么不将ftw 设为模板?然后它可以通过boost::bind 接受从函数指针到boost::function 的任何内容。如果你不想让它成为一个模板,为什么不使用boos::function作为它的参数类型呢?这就是它的用途:)
  • 我应该提供更多细节。 ftw 是共享库中的一个函数(参见 linux.die.net/man/3/ftw 上的 man ftw),我无法控制它的参数列表。
  • 那你就不走运了。创建一个包装函数,该函数转发给实际函数并另外传递另一个参数。那时不能使用boost::bind。 :(
  • 库设计的规则之一是,对于回调函数,您始终添加一个参数,库用户可以使用该参数让您的库交回一些用户提供的数据。我只是想到了一个可能的解决方案,但它太难用了。
【解决方案2】:

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

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 函数,将全局变量的内容填充到对回调函数的调用中。

【讨论】:

    【解决方案3】:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-05-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多