¿Puedo separar la función principal de C ++ y las clases de las rutinas Objective-C y / o C en comstackción y enlace?

Tengo una pequeña aplicación C ++ que importé clases Objective-C. Funciona como archivos Objective-C ++, .mm, pero cualquier archivo C ++ que incluya un encabezado que pueda incluir un encabezado Objective-C debe renombrarse a una extensión .mm para los controladores GCC adecuados.

¿Hay alguna manera de escribir un contenedor puramente C ++ para las clases Objective-C o puedo separar los objetos Objective-C de alguna manera y simplemente vincularlos por separado? Quizás incluso si las clases de Objective-C se convirtieran en una pequeña biblioteca, podría volver a enlazar estáticamente en tiempo de comstackción.

El problema es que este código es multiplataforma y es más difícil de comstackr en sistemas que normalmente no usan Objective-C (es decir, no Mac). Aunque los comandos del preprocesador restringen cualquier implementación del código Objective-C en Windows o Linux, el código original aún tiene extensiones .mm y GCC aún trata el código como Objective-C ++.

Por lo general, simplemente ajusta sus clases de Objective-C con clases de C ++, por ejemplo, utilizando punteros opacos y reenviando llamadas a métodos C ++ a métodos Objective-C.

De esta forma, sus fonts portátiles de C ++ nunca deben ver ningún Objective-C, y lo ideal es que solo tengan que intercambiar los archivos de implementación para los contenedores en diferentes plataformas.

Ejemplo:

// c++ header: class Wrapper { struct Opaque; Opaque* opaque; // ... public: void f(); }; // Objective-C++ source on Mac: struct Wrapper::Opaque { id contained; // ... }; void Wrapper::f() { [opaque->contained f]; } // ... 

Sí, eso es fácilmente posible, en ambos sentidos, si conoces algunos trucos:

1) El tipo “id” se define realmente en un encabezado C simple. Entonces puedes hacer lo siguiente:

En tu encabezado:

 #include  class MyWindow { public: MyWindow(); ~MyWindow(); protected: id mCocoaWindow; }; 

En su implementación (.mm):

 #include "MyWindow.h" #include  MyWindow::MyWindow() { mCocoaWindow = [[NSWindow alloc] init]; } MyWindow::~MyWindow() { [mCocoaWindow release]; mCocoaWindow = nil; } 

2) Hay dos constantes de preprocesador que puede usar para excluir el código específico de C ++ / ObjC cuando un archivo fuente las incluye que es uno de los dos, pero no ObjC ++:

 #if __OBJC__ // ObjC code goes here. #endif /* __OBJC__*/ #if __cplusplus // C++ code goes here. #endif 

Solo tenga cuidado, no puede simplemente agregar / eliminar ivars o métodos virtuales con un #ifdef, que creará dos clases con diferentes diseños de memoria y hará que su aplicación falle de maneras muy extrañas.

3) Puede usar un puntero a una estructura sin declarar su contenido:

En tu encabezado:

 @interface MyCppObjectWrapper : NSObject { struct MyCppObjectWrapperIVars *ivars; // This is straight ObjC, no ++. } @end 

En su archivo de implementación (.mm):

 struct MyCppObjectWrapperIVars { std::string myCppString1; std::string myCppString2; std::string myCppString3; }; @implementation MyCppObjectWrapper -(id) init { if(( self = [super init] )) { ivars = new MyCppObjectWrapperIVars; } return self; } -(void) dealloc { delete ivars; ivars = NULL; [super dealloc]; } @end 

Esto hará que su encabezado sea simple C u ObjC, mientras que su archivo de implementación obtiene constructores / destructores de todos los ivars llamados sin que tenga que crear / eliminar cada uno como un objeto en el montón.

Esto es solo el lado Mac de las cosas, pero esto significaría que podría mantener las cosas ObjC fuera de sus encabezados, o al menos hacer que se compile cuando se utiliza desde archivos de cliente multiplataforma de la implementación de Mac de su capa portabilidad C ++.