いくつかのオプションがあります。
ブロックの使用
ブロックを使用して、コールバック作業を伝えることができます。これは、コールバック「関数」にパラメーターを渡すことなくコードを呼び出すことができるため、おそらく最も簡単なソリューションです。ブロックは C とそのすべてのスーパーセットで Clang で機能し、Clang++ ではブロックとラムダの間の暗黙的なキャストも可能です。
#include <dispatch/dispatch.h>
void setup_callback(dispatch_block_t block)
{
// required to copy the block to the heap, otherwise it's on the stack
dispatch_block_t copy = [block copy];
// setup stuff here
// when you want to call the callback, do as if it was a function pointer:
// block();
}
int main()
{
MyClass* instance = [[MyClass alloc] init];
setup_callback(^{
[instance callback_method];
});
}
関数ポインターの代わりにファンクター (または単純な場合はブロックのみ) を受け入れるようにするには、C++ 側でいくつかの再作業が必要になる場合があります。
ブロックはクロージャを作るので、そういう作業にはとても便利です。
ブロックは、C、C++、および Objective-C に対する Apple の拡張機能です。詳細については、こちらを参照してください。
Objective-C ランタイムを使用して、呼び出したいメソッドへの関数ポインタを取得します
Objective-C ランタイムを使用して、セレクターの関数ポインターにアクセスします。これはより面倒で、3 つの変数 (メソッドを呼び出すオブジェクト、使用するセレクター、メソッドの実装) を追跡する必要がありますが、Objective-C を使用できない場合でも実際には機能します。構文。
Objective-C メソッドの実装は、次のシグネチャを持つ関数ポインターです。
typedef void (*IMP)(id self, SEL _cmd, ...);
あなたself
が期待して_cmd
いるのは、このメソッド呼び出しを引き起こしたセレクターです (_cmd
変数は実際にはすべての Objective-C メソッドで使用できます。試してみてください)。残りは可変長と見なされます。変数を適切な関数シグネチャにキャストする必要があるIMP
のは、可変長 C 関数の呼び出し規則が Objective-C メソッド呼び出しの呼び出し規則と常に一致するとは限らないためです (Objective-C メソッド呼び出しは、コンパイラの標準関数呼び出し規則です。おそらくcdecl
または amd64 呼び出し規則のいずれかであり、可変長呼び出し規則は常に同じとは限りません)。Areinterpret_cast
できるようになります。
同様の目的でまとめたコードを次に示します。C++11 可変個引数テンプレートを使用して、適切な関数シグネチャを取得するのに役立ちます。
#include <objc/runtime.h>
template<typename TReturnType, typename... TArguments>
auto GetInstanceMethodPointer(Class class, SEL selector) -> TReturnType (*)(id, SEL, TArguments...)
{
Method m = class_getInstanceMethod(class, selector);
IMP imp = method_getImplementation(m);
return reinterpret_cast<TReturnType (*)(id, SEL, TArguments...)>(imp);
}
int main()
{
MyClass* instance = [[MyClass alloc] init];
auto foo = GetInstanceMethodPointer<void>(
[MyClass class],
@selector(my_callback));
// foo is a void (*)(id, SEL) function pointer
foo(instance, @selector(my_callback));
}
また、チェックは Objective-C ランタイムによって処理されるnil
ため、インスタンスが関数呼び出しを使用する前にないように注意してください。nil
この場合、それをバイパスしています。
オブジェクトとSEL
コールバックを実行するために使用-[NSObject performSelector:]
します。基本的に、Objective-C ランタイム ソリューションの単純なバージョンです。
void setup_callback(id object, SEL selector)
{
// do stuff
// to execute the callback:
// [object performSelector:selector];
}
int main()
{
MyClass* instance = [[MyClass alloc] init];
setup_callback(instance, @selector(my_callback));
}
呼び出しを C++ 関数内にラップする
これは本当に例を必要としないと思います。オブジェクト型を最初のパラメーターとして受け入れる関数を作成し、必要なメソッドを呼び出します。ソリューションと同様に、SEL
呼び出す関数とそれを呼び出すオブジェクトを個別に追跡する必要があります。