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(); から null ポインター (fpt) が返されます。実行時に。上記のスタックオーバーフローの質問にリンクされている参照から、ブーストがランタイム変換を実行できない場合、null ポインターが返されるようです。しかし、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。その中でmainbind 式を呼び出します。このコード スニペットがどのように機能するかについては、Eric Niebler の記事Conditional Loveをお読みください。

于 2009-09-04T19:33:00.420 に答える
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のいくつかの可能なインスタンスを同時にコールバックしたい場合は、myMapの可能な限り多くのグローバルMyFTWFunction変数が必要になります。ほとんどの場合、これにより、グローバル変数の内容を使用して欠落しているパラメーターを埋めるサンク関数の作成が自動化されます。

これは、柔軟性が非常に低いバージョンであり、この狭いケースでもほぼ同じことを行い、ここで何が起こっているのかをより明確にする可能性があります。

#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は、グローバル変数の内容をコールバック関数の呼び出しに詰め込むサンク関数を自動生成するテンプレートです。

于 2009-09-05T01:54:09.763 に答える
0

これは数年遅れていますが、将来的に役立つかもしれません。私の問題は少し異なりましたが、解決策から必要な答えを得ることができます。こちらをお読みください:
>メッセージング システム: コールバックは何でもかまいません

于 2011-07-31T16:15:57.770 に答える