処理をカスタマイズするためにコールバック関数を登録する必要がある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*)’.
私の質問:
メンバー関数が静的である場合、それを行うことができます。
クラスAの非静的メンバー関数には、thisポインターに対応するclass A*
型の暗黙の最初のパラメーターがあります。そのため、コールバックの署名にもclass A*
タイプの最初のパラメーターがある場合にのみ登録できます。
メンバー関数が静的ではない場合にもこれを行うことができますが、もう少し作業が必要です( 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
に動的にバインドされた静的関数です。
問題は、そのメソッド!=関数です。コンパイラはメソッドを次のようなものに変換します。
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);
}
その後、コールバック関数を登録できます。
まあ...あなたがwin32プラットフォームを使用している場合は、常に厄介なサンキング方法があります...
Win32でのサンキング:非静的メンバー関数へのコールバックの簡素化
これは解決策ですが、使用することはお勧めしません。
良い説明があり、それが存在することを知ってうれしいです。
メンバー関数を使用することの問題は、それが動作するオブジェクトを必要とすることです-そして、Cはオブジェクトについて知りません。
最も簡単な方法は次のことです。
//In a header file:
extern "C" int e(int * k, int * e);
//In your implementation:
int e(int * k, int * e) { return 0; }
このソリューションでは、コールバックとして「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 );
} // ()