web-dev-qa-db-ja.com

C ++クラスメンバー関数をCコールバック関数として使用する

処理をカスタマイズするためにコールバック関数を登録する必要があるCライブラリがあります。コールバック関数のタイプはint a(int *, int *)です。

次のようなC++コードを書いて、C++クラス関数をコールバック関数として登録しようとしています。

class A {
  public:
   A();
   ~A();
   int e(int *k, int *j);
};

A::A()
{
   register_with_library(e)
}

int
A::e(int *k, int *e)
{
  return 0;
}

A::~A() 
{

}

コンパイラーは次のエラーをスローします。

In constructor 'A::A()',
error:
 argument of type ‘int (A::)(int*, int*)’ does not match ‘int (*)(int*, int*)’.

私の質問:

  1. まず第一に、私がやろうとしているようにC++クラスのメンバー関数を登録することは可能ですか? (私は http://www.parashift.com/c++-faq-lite/mixing-c-and-cpp.html で32.8を読みました。しかし、私の意見ではそれは問題を解決しません)
  2. これに取り組むための代替/より良い方法はありますか?
43
Methos

メンバー関数が静的である場合、それを行うことができます。

クラスAの非静的メンバー関数には、thisポインターに対応するclass A*型の暗黙の最初のパラメーターがあります。そのため、コールバックの署名にもclass A*タイプの最初のパラメーターがある場合にのみ登録できます。

41
sharptooth

メンバー関数が静的ではない場合にもこれを行うことができますが、もう少し作業が必要です( C++関数ポインターをc関数ポインターに変換する も参照):

_#include <stdio.h>
#include <functional>

template <typename T>
struct Callback;

template <typename Ret, typename... Params>
struct Callback<Ret(Params...)> {
   template <typename... Args> 
   static Ret callback(Args... args) {                    
      func(args...);  
   }
   static std::function<Ret(Params...)> func; 
};

template <typename Ret, typename... Params>
std::function<Ret(Params...)> Callback<Ret(Params...)>::func;

void register_with_library(int (*func)(int *k, int *e)) {
   int x = 0, y = 1;
   int o = func(&x, &y);
   printf("Value: %i\n", o);
}

class A {
   public:
      A();
      ~A();
      int e(int *k, int *j);
};

typedef int (*callback_t)(int*,int*);

A::A() {
   Callback<int(int*,int*)>::func = std::bind(&A::e, this, std::placeholders::_1, std::placeholders::_2);
   callback_t func = static_cast<callback_t>(Callback<int(int*,int*)>::callback);      
   register_with_library(func);      
}

int A::e(int *k, int *j) {
   return *k - *j;
}

A::~A() { }

int main() {
   A a;
}
_

この例は、コンパイルするという意味で完全です。

_g++ test.cpp -std=c++11 -o test
_

_c++11_フラグが必要です。コードでは、register_with_library(func)が呼び出されていることがわかります。funcは、メンバー関数eに動的にバインドされた静的関数です。

17
Anne van Rossum

問題は、そのメソッド!=関数です。コンパイラはメソッドを次のようなものに変換します。

int e( A *this, int *k, int *j );

したがって、クラスインスタンスを引数として渡すことはできないため、それを渡すことはできません。この問題を回避する1つの方法は、メソッドを静的として作成することです。これにより、メソッドの型が適切になります。ただし、クラスインスタンスは存在せず、非静的クラスメンバーへのアクセスもありません。

もう1つの方法は、最初に初期化されたAへの静的ポインターを使用して関数を宣言することです。関数は呼び出しをクラスにリダイレクトするだけです:

int callback( int *j, int *k )
{
    static A  *obj = new A();
    a->(j, k);
}

その後、コールバック関数を登録できます。

6

まあ...あなたがwin32プラットフォームを使用している場合は、常に厄介なサンキング方法があります...

Win32でのサンキング:非静的メンバー関数へのコールバックの簡素化

これは解決策ですが、使用することはお勧めしません。
良い説明があり、それが存在することを知ってうれしいです。

5
TimW

メンバー関数を使用することの問題は、それが動作するオブジェクトを必要とすることです-そして、Cはオブジェクトについて知りません。

最も簡単な方法は次のことです。

//In a header file:
extern "C" int e(int * k, int * e);

//In your implementation: 
int e(int * k, int * e) { return 0; }
1
PaulJWilliams

このソリューションでは、コールバックとして「c関数」に渡される静的メソッドを持つテンプレートクラスがあります。このクラスは、「通常の」オブジェクト(最終的に呼び出されるcallback()という名前のメンバー関数を持つ)を保持します。

クラス(ここではA)を定義したら、簡単に使用できます。

int main() {

  Holder<A> o ( A(23, 23) );

  std::cout << o().getN() << "\n";

  callACFunctionPtr( fun );

  callACFunctionPtr( o.callback );

} // ()

完全な例:

#include <iostream>

// ----------------------------------------------------------
// library class: Holder
// ----------------------------------------------------------
template< typename HeldObjectType >
class Holder {
public:
  static inline HeldObjectType object;

  static void callback( ) {
    object.callback();
  } // ()

  HeldObjectType &  operator() ( ) {
    return object;
  }

  Holder( HeldObjectType && obj )
  {
    object = obj;
  }

  Holder() = delete;

}; // class

// ----------------------------------------------------------
// "old" C function receivin a ptr to function as a callback
// ----------------------------------------------------------
using Callback = void (*) (void);

// ..........................................................
// ..........................................................
void callACFunctionPtr( Callback f ) {
  f();
} // ()

// ----------------------------------------------------------
// ----------------------------------------------------------
void fun() {
  std::cout << "I'm fun\n";
} // 

// ----------------------------------------------------------
// 
// Common class where we want to write the
// callback to be called from callACFunctionPtr.
// Name this function: callback
// 
// ----------------------------------------------------------
class A {
private:
  int n;

public:

  A(  ) : n( 0 ) { }

  A( int a, int b ) : n( a+b ) { }

  void callback( ) {
    std::cout << "A's callback(): " << n << "\n";
  }

  int getN() {
    return n;
  }

}; // class

// ----------------------------------------------------------
// ----------------------------------------------------------
int main() {

  Holder<A> o ( A(23, 23) );

  std::cout << o().getN() << "\n";

  callACFunctionPtr( fun );

  callACFunctionPtr( o.callback );

} // ()
0
cibercitizen1