2012-08-30 17 views
12

私はViewControllerをobjective-cで使用していますが、ほとんどのコードはC++(.mm)です。私はいくつかのコールバックをobj-c(C++で)からメンバー関数にセットアップし、C++からコールしたいと思います。このような何か(それは非常にsimplifyedです):C++から目的関数へのコールバック

@interface MyClass 
{ } 
-(void)my_callback; 
@end 

@implementation MyClass 

-(void)my_callback 
{ 
    printf("called!\n"); 
} 

-(void)viewDidLoad 
{ 
    // setup_callback("to my_callback ?"); 
} 
@end 

とは:

void setup_callback(void(*func)()) { func(); } 

これはもちろん正しくありません。どのようにアドバイスをすればいいですか?

答えて

15

あなたはいくつかの選択肢があります。

ブロックの使用

ブロックを使用してコールバックの仕事を伝えることができます。コールバック関数にパラメータを渡すことなくコードを呼び出すことができるので、これはおそらく最も簡単な解決策です。ブロックはCとそのすべてのスーパーセットでClangで動作し、Clang ++はブロックとラムダ間の暗黙のキャストを許可します。代わりに、関数ポインタのファンクタ(または単にブロック、それは簡単です場合)を受け入れるようにC++の端にいくつかの手直しが必要になる場合があります

#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++、Objective-CのApple拡張です。それらについての詳細はhereを参照してください。

はあなたの選択の関数ポインタにアクセスするためのObjective-Cランタイムを使用し

を呼び出したいメソッドに関数ポインタを取得するためのObjective-Cランタイムを使用してください。これはもっと退屈で、3つの変数(メソッドを呼び出すオブジェクト、使用するセレクタ、メソッドの実装)を追跡する必要がありますが、Objective-Cを使用できない場合でも実際に機能します構文。

Objective-Cのメソッドの実装は、このシグネチャを持つ関数ポインタです:selfは、あなたが期待するものである

typedef void (*IMP)(id self, SEL _cmd, ...); 

は、_cmdは、このメソッドの呼び出しを引き起こしたセレクタです(_cmd変数はすべてで実際に利用可能ですObjective-Cの方法を試してみてください)、残りはバリデーションとみなされます。 IMPの変数を適切な関数シグネチャにキャストする必要があります。これは、variadic C関数の呼び出し規約が、Objective-Cメソッド呼び出しの呼び出し規則と常に一致するわけではないからです(Objective-Cメソッド呼び出しは、おそらくcdeclまたはamd64呼び出し規約のいずれかであり、可変呼び出し規約はであり、必ずしもと同じではありません。 reinterpret_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)); 
} 

またnilチェックはObjective-Cランタイムによって処理されるので、あなたのインスタンスは、関数呼び出しを使用する前にnilないように注意してください。この場合、私たちはそれをバイパスしています。

あなたのコールバックを実行するために、オブジェクトとSEL

利用-[NSObject performSelector:]を追跡します。基本的にObjective-Cランタイムソリューションのより単純なバージョンです。 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)); 
} 

私はこの1つは本当にすべての例を必要としないと思います。最初のパラメータとしてオブジェクト型を受け入れ、それに必要なメソッドを呼び出す関数を作成します。 SELソリューションと同様に、呼び出す関数と呼び出すオブジェクトを個別に追跡する必要があります。

+2

実装する必要はありません。 – user102008

+0

の実装の代わりに 'objc_msgSend'を使うことができます。その場合、メソッドポインタの代わりにセレクタを持ちます。 – zneak

+0

いいえあなたのIMPで、あなたはとにかくセレクターを運ぶ必要があります。だから、同じことですが、今はIMPを取得する必要はありません。 – user102008

1

C++コード内でObjective-Cコールバックを呼び出すことはできません。他の方法でも可能です。私はこの問題は、Objective-Cで永遠に実行されているEVENT_LOOPを書き、およびC++ std::vectorインスタンスからイベントを取得することである持っていたとき、私が何をしたか

。このように、C++では、Objective-Cでコールバックを呼び出す場合は、対応するstd::vectorにイベントを追加するだけです。

1

Obj-CとC++を.mmファイルに自由に混在させることができます。これはObjective-C++と呼ばれています。

C++コードとObjective-Cコードの分離を維持するために、Objective-Cメソッドの呼び出しを(通常の方法で)C++コールバックから行う軽量ファサードを作成できます。

2

C++とObj-CはどちらもCのスーパーセットなので、C関数へのC++コードコールバックを持つことができます。コールバックをObj-Cオブジェクトに返信するには、Cコールバック引数に元のObj-Cオブジェクトへのポインタが含まれていることを確認してください。

// C++ calls this C function 
void exampleCallback(void* classInstance) 
{ 
    // callback arguments include a pointer to the Obj-C object 
    [((MYObjCClassType*)classInstance) exampleCallback]; 
} 
+0

あなたが見せているキャストをするには、フリーダイヤルでブリッジする必要があります。 '[((__bridge MYObjCClassType *)classInstance)exampleCallback];' – dadude999

関連する問題