Оберните класс Objective- C в структуру C, чтобы отразить имя класса - PullRequest
2 голосов
/ 25 марта 2020

Есть ли способ обернуть класс Objective- C в C struct так, чтобы имя struct соответствовало названию класса? В идеале я хочу подражать поведению использования extern "C" в C ++, как показано здесь , вместо необходимости указывать на член структуры типа TestObject, продемонстрированной в простых программах ниже.

Объём обёртывания - C класс до C struct

testobject.m

#ifndef testobject_h
#define testobject_h

#ifdef __OBJC__
@interface TestObject : NSObject
- (void) printHello;
@end
#endif

typedef struct TestObjectC TestObjectC; 
TestObjectC* newTestObject(void);
void TestObject_printHello(TestObjectC *t);

#endif /* testobject_h */

testobject.m

#import <Foundation/Foundation.h>
#import "testobject.h"

@implementation TestObject

- (void)printHello
{
    printf("HELLO!\n");
}

@end

typedef struct TestObject_t
{
    TestObject *testObject;
}TestObjectC;

TestObjectC* newTestObject(void)
{
    TestObjectC *t = malloc(sizeof(TestObjectC));
    t->testObject = [TestObject new];
    return t;
}

void TestObject_printHello(TestObjectC* t)
{
    [t->testObject printHello];
}

main. c

#include "testobject.h"

int main(int argc, const char * argv[])
{
    TestObjectC *t = newTestObject();
    TestObject_printHello(t);
    return 0;
}

Заключение класса C ++ в C struct

TestClass.h

#ifndef __TESTCLASS_H
#define __TESTCLASS_H

class TestClass 
{
    public:
        void printHello(void);
};

#endif

TestClass. cc

#include "TestClass.h"
void TestClass::printHello(void) 
{
    printf("HELLO!\n");
}

MyWrapper.h

#ifndef __MYWRAPPER_H
#define __MYWRAPPER_H

#ifdef __cplusplus
extern "C" {
#endif

typedef struct TestClass TestClass;

TestClass* newTestClass();
void TestClass_printHello(TestClass* v);
void deleteTestClass(TestClass* v);

#ifdef __cplusplus
}
#endif
#endif

MyWrapper. cc

#include "TestClass.h"
#include "MyWrapper.h"

extern "C" 
{
  TestClass* newTestClass() 
  {
          return new TestClass();
  }

  void TestClass_int_set(TestClass* v, int i) 
  {
          v->printHello();
  }
  void deleteTestClass(TestClass* v) 
  {
          delete v;
  }
}

C Программа

#include "MyWrapper.h"
#include <stdio.h>

int main(int argc, char* argv[]) 
{
  struct TestClass* c = newTestClass();
  TestClass_printHello(c);        
  deleteTestClass(c);
}

1 Ответ

2 голосов
/ 25 марта 2020

Здесь нет необходимости в обертке. Объекты Obj C могут без проблем проходить через C и даже поддерживать AR C. Я собираюсь немного изменить стиль, чтобы соответствовать CoreFoundation. Я напишу это снова, чтобы показать, как это будет выглядеть, не подвергая вызывающую сторону CoreFoundation.

main. c

int main(int argc, const char * argv[])
{
    // "Ref" means pointer. ...Create() means you must Release it.
    TestObjectRef t = TestObjectCreate();

    // Call your function
    TestObjectPrintHello(t);

    // And release it.
    CFRelease(t);
    return 0;
}

testobject.h

#ifndef testobject_h
#define testobject_h

#import <CoreFoundation/CoreFoundation.h>

// Only define the ObjC part in ObjC
#ifdef __OBJC__
#import <Foundation/Foundation.h>

@interface TestObject : NSObject
- (void) printHello;
@end

#endif

// This is the "void *" of CoreFoundation, but it lets the compiler know this
// this is a bridged ObjC object. This is the standard CF way of naming pointer types.
typedef CFTypeRef TestObjectRef;

// This is the standard CF way of naming functions. "Create" means you need to release it.
TestObjectRef TestObjectCreate(void);

// CF functions always start with their class
void TestObjectPrintHello(TestObjectRef t);

#endif /* testobject_h */

testobject.m

#import "testobject.h"

@implementation TestObject

- (void)printHello
{
    printf("HELLO!\n");
}

@end

// ARC is no problem. We just need to tell ARC that we intend to keep an extra retain.
TestObjectRef TestObjectCreate(void) {
    return CFBridgingRetain([TestObject new]);
}

// And we can get back to ObjC using `__bridge`
void TestObjectPrintHello(TestObjectRef t) {
    [(__bridge TestObject *)t printHello];
}

Это решение пропускает CoreFoundation.h вызывающей стороне и навязывает имя в стиле CF. Мне это очень нравится в моих системах. Но иногда я работаю над существующей кроссплатформенной системой C, и вы хотите скрыть эти детали. Поэтому, если бы я пытался соответствовать стилю в вашем примере, я сделал бы это следующим образом:

main. c

int main(int argc, const char * argv[])
{
    TestObjectC *t = newTestObject();
    TestObject_printHello(t);
    TestObject_release(t); // Still need memory management. Can't avoid that.
    return 0;
}

testobject.h

#ifndef testobject_h
#define testobject_h

#ifdef __OBJC__
#import <Foundation/Foundation.h>
@interface TestObject : NSObject
- (void) printHello;
@end
#endif

typedef const void TestObjectC; // 'const void *' is more CF-like, but either is fine

TestObjectC* newTestObject(void);
void TestObject_printHello(TestObjectC *t);
void TestObject_release(TestObjectC *t);

#endif /* testobject_h */

testobject.m

#import "testobject.h"

@implementation TestObject

- (void)printHello
{
    printf("HELLO!\n");
}

@end

TestObjectC* newTestObject(void) {
    return CFBridgingRetain([TestObject new]);
}

void TestObject_printHello(TestObjectC *t) {
    [(__bridge TestObject *)t printHello];
}
void TestObject_release(TestObjectC *t) {
    CFRelease(t);
}
...