1. ホーム
  2. c++

[解決済み] C++のメンバ関数からObjective-Cのメソッドを呼び出すには?

2022-05-16 16:54:50

質問

クラス( EAGLView のメンバー関数を呼び出すクラスがあります。 C++ クラスのメンバー関数を問題なく呼び出すことができます。さて、問題なのはその C++ クラスで objective-C function [context renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer*)self.layer]; でできないことを C++ の構文ではできません。

これをラップして Objective-C の呼び出しを同じ Objective-C クラスのメソッドを呼び出します。 C++ からそのメソッドを呼び出す必要がありますが、それを行う方法がわかりません。

へのポインタを与えようとしたのですが EAGLView オブジェクトを C++ のメンバ関数に渡して、" をインクルードしてみました。 EAGLView.h を私の C++ クラスヘッダを作成しましたが、3999エラーが発生しました。

それで...私はこれをどのように行うべきですか?例があればいいのですが...。私が見つけたのは純粋な C の例しか見つけられませんでした。

どのように解決するのですか?

C++とObjective-Cは、注意深く行えば混ぜることができます。いくつかの注意点がありますが、一般的には混ぜることができます。これらを分離しておきたい場合は、Objective-C オブジェクトに非 Objective-C コードから使用可能な C スタイルのインターフェイスを与える標準 C ラッパー関数を設定できます (ファイルの名前をより良いものにします。私は冗長性のためにこれらの名前を選びました)。

MyObject-C-Interface.h

#ifndef __MYOBJECT_C_INTERFACE_H__
#define __MYOBJECT_C_INTERFACE_H__

// This is the C "trampoline" function that will be used
// to invoke a specific Objective-C method FROM C++
int MyObjectDoSomethingWith (void *myObjectInstance, void *parameter);
#endif

MyObject.h

#import "MyObject-C-Interface.h"

// An Objective-C class that needs to be accessed from C++
@interface MyObject : NSObject
{
    int someVar;
}

// The Objective-C member function you want to call from C++
- (int) doSomethingWith:(void *) aParameter;
@end

MyObject.mm

#import "MyObject.h"

@implementation MyObject

// C "trampoline" function to invoke Objective-C method
int MyObjectDoSomethingWith (void *self, void *aParameter)
{
    // Call the Objective-C method using Objective-C syntax
    return [(id) self doSomethingWith:aParameter];
}

- (int) doSomethingWith:(void *) aParameter
{
    // The Objective-C function you wanted to call from C++.
    // do work here..
    return 21 ; // half of 42
}
@end

MyCPPClass.cpp

#include "MyCPPClass.h"
#include "MyObject-C-Interface.h"

int MyCPPClass::someMethod (void *objectiveCObject, void *aParameter)
{
    // To invoke an Objective-C method from C++, use
    // the C trampoline function
    return MyObjectDoSomethingWith (objectiveCObject, aParameter);
}

ラッパー関数 は必要ありません。 が同じ .m ファイルにあるはずですが、Objective-C のクラスが存在するファイルは は Objective-C コードとしてコンパイルされる必要があります。 . ラッパー関数を宣言するヘッダはCPPとObjective-Cの両方のコードに含まれる必要があります.

(注意: Objective-Cの実装ファイルに拡張子 ".m" が与えられると、それはXcodeの下でリンクされないでしょう。 拡張子 ".mm" は、Objective-C と C++ の組み合わせ、つまり Objective-C++ を期待するように Xcode に伝えます)。


上記をオブジェクト指向で実装するためには、以下のように PIMPLイディオム . 実装はわずかに異なるだけです。 要するに、MyClass のインスタンスへの (private) void ポインタを持つクラスの中に、 ("MyObject-C-Interface.h" で宣言されている) ラッパー関数を配置するのです。

MyObject-C-Interface.h。 (PIMPL)

#ifndef __MYOBJECT_C_INTERFACE_H__
#define __MYOBJECT_C_INTERFACE_H__

class MyClassImpl
{
public:
    MyClassImpl ( void );
    ~MyClassImpl( void );

    void init( void );
    int  doSomethingWith( void * aParameter );
    void logMyMessage( char * aCStr );

private:
    void * self;
};

#endif

ラッパーメソッドはもはや MyClass のインスタンスへの void ポインタを必要としないことに注意してください; それは今 MyClassImpl のプライベートメンバです。 init メソッドは MyClass インスタンスを生成するために使用されます.

MyObject.h (PIMPL)

#import "MyObject-C-Interface.h"

@interface MyObject : NSObject
{
    int someVar;
}

- (int)  doSomethingWith:(void *) aParameter;
- (void) logMyMessage:(char *) aCStr;

@end

MyObject.mm (PIMPL)

#import "MyObject.h"

@implementation MyObject

MyClassImpl::MyClassImpl( void )
    : self( NULL )
{   }

MyClassImpl::~MyClassImpl( void )
{
    [(id)self dealloc];
}

void MyClassImpl::init( void )
{    
    self = [[MyObject alloc] init];
}

int MyClassImpl::doSomethingWith( void *aParameter )
{
    return [(id)self doSomethingWith:aParameter];
}

void MyClassImpl::logMyMessage( char *aCStr )
{
    [(id)self doLogMessage:aCStr];
}

- (int) doSomethingWith:(void *) aParameter
{
    int result;

    // ... some code to calculate the result

    return result;
}

- (void) logMyMessage:(char *) aCStr
{
    NSLog( aCStr );
}

@end

MyClass は MyClassImpl::init への呼び出しでインスタンス化されることに注意してください。 MyClassImpl のコンストラクタで MyClass をインスタンス化することもできますが、それは一般的に良いアイデアではありません。 MyClass のインスタンスは MyClassImpl のデストラクタから破壊されます。 C言語での実装と同様に,ラッパーメソッドはMyClassの各メソッドに委ねるだけです.

MyCPPClass.h (PIMPL)

#ifndef __MYCPP_CLASS_H__
#define __MYCPP_CLASS_H__

class MyClassImpl;

class MyCPPClass
{
    enum { cANSWER_TO_LIFE_THE_UNIVERSE_AND_EVERYTHING = 42 };
public:
    MyCPPClass ( void );
    ~MyCPPClass( void );

    void init( void );
    void doSomethingWithMyClass( void );

private:
    MyClassImpl * _impl;
    int           _myValue;
};

#endif

MyCPPClass.cpp (PIMPL)

#include "MyCPPClass.h"
#include "MyObject-C-Interface.h"

MyCPPClass::MyCPPClass( void )
    : _impl ( NULL )
{   }

void MyCPPClass::init( void )
{
    _impl = new MyClassImpl();
}

MyCPPClass::~MyCPPClass( void )
{
    if ( _impl ) { delete _impl; _impl = NULL; }
}

void MyCPPClass::doSomethingWithMyClass( void )
{
    int result = _impl->doSomethingWith( _myValue );
    if ( result == cANSWER_TO_LIFE_THE_UNIVERSE_AND_EVERYTHING )
    {
        _impl->logMyMessage( "Hello, Arthur!" );
    }
    else
    {
        _impl->logMyMessage( "Don't worry." );
    }
}

これで、MyClassImpl のプライベートな実装を通して MyClass への呼び出しにアクセスできるようになりました。このアプローチはポータブルなアプリケーションを開発している場合、有利になります; MyClass の実装を他のプラットフォームに固有のものと単純に交換することができます ... しかし正直なところ、これがより良い実装であるかどうかは、より好みやニーズの問題です。