2016-10-13 3 views
9

Мне нужно обернуть некоторые API-интерфейсы в библиотеке C++ в C. Я делал это в прошлом, используя непрозрачные указатели на объекты класса, extern «C» и т. Д., Как описано here. Однако эта новая библиотека, с которой я имею дело, широко использует ссылки на интеллектуальные указатели. Я не уверен, как сделать обертку в присутствии умных указателей. Например, предположим, что библиотека C++ имеет следующую функцию:Как обернуть функцию C++, которая возвращает интеллектуальный указатель в C?

SmartPointer<MyClass> foo() { 
    SmartPointer<MyClass> ret(new MyClass); // Create smart pointer ret 
    ret->DoSomething(); // Do something with ret 
    return ret; 
} 

Как я могу обернуть foo() в C? Очевидно, мне нужен непрозрачный указатель (например, void* или пустой указатель struct), который я могу использовать в функции C для ссылки на объект MyClass. Первый вариант, о котором я думал, состоял в том, чтобы извлечь объект MyClass из ret и бросить его в void*. Тем не менее, этот void* станет свисающим, как только ret выходит за пределы области действия из-за автоматического удаления, сделанного с помощью умного указателя (исправьте меня, если я ошибаюсь).

Другой вариант заключается в выделении указатель на смарт-указатель (скажем retPtr), делать *retPtr=ret, а затем создать непрозрачный указатель на retPtr. Я думаю, что этот вариант может работать, но лучший ли это?

Любая помощь приветствуется.

+3

Я согласен с вашим последним предложением. –

+3

Какой умный указатель, я не знаю класса SmartPointer в stdlib. Это сильно зависит от семантики рассматриваемого умного указателя ... – Vality

+0

действительно ли библиотеке C требуется доступ к элементам 'ObjectType'? или он просто удерживает указатель на некоторое время и освобождает его позже? Также какова связь между «ObjectType» и «MyClass»? –

ответ

1

Замечания показывают, что код C должен содержать интеллектуальный указатель, но ему ничего не нужно делать, кроме передачи его дословной другой функции C++. Код, который может быть:

// Shared header 
#ifdef __cplusplus 
extern "C" { 
#endif 

void * foo_acquire(void); 
int foo_bar(void *); 
void foo_release(void *); 

#ifdef __cplusplus 
} 
#endif 

// C++ implementation 
extern "C" void *foo_acquire() 
{ 
    return new SmartPointer<MyClass>(foo()); 
} 

extern "C" int foo_bar(void *s) 
{ 
    auto& sp = *static_cast< SmartPointer<MyClass> * >(s); 
    return bar(sp); // bar represents some function expecting the smart pointer 
} 

extern "C" void foo_release(void *s) 
{ 
    delete static_cast<SmartPointer<MyClass> *>(s); 
} 

Это использует перемещение-конструктор SmartPointer (или скопировать конструктор, если это не было двигаться-конструктор), операцию, которая должна опираться на смарт-указатели.

Если вы хотите предотвратить неявные преобразования в коде C, вы можете использовать непрозрачный дескриптор вместо void *. (Например, структура с элементом void *).

5

Вы в значительной степени должны вернуть непрозрачный указатель на код C. Поэтому вам нужно будет использовать new, чтобы выделить новый интеллектуальный указатель, на который вы можете вернуть указатель.

О вашей единственной альтернативе - использовать коллекцию общих указателей, чтобы сохранить объект в живых. Когда код C указывает, что он закончен с объектом, вы удаляете общий указатель из коллекции. Это позволяет вам возвращать любой идентификатор, который вы хотите использовать в коде C, - он просто используется в качестве дескриптора для поиска объекта в коллекции.

+1

Идея дескриптора хорошая - ведь так работает 'FILE' в C. –

2

В дополнение к ответу Дэвида Шварца вы можете рассмотреть подход, похожий на IUnknown COM. Вы можете определить структуру, содержащую указатели на функции (имитирующий C++-интерфейс в чистом C), и выставить несколько таких методов, как AddRef и Release, для увеличения и выпуска ref ref.

Звонящий получает указатель на эту структуру, поэтому он/она может контролировать правильное время жизни возвращаемого объекта с помощью AddRef и Release.

Кроме того, в структуре можно добавить другие методы (указатели функций) для раскрытия других функций возвращаемого объекта.

This article on COM in plain C подробно объясняет ситуацию.

0

Экспортировать структуру в код C, а не только указатель.В указанной структуре есть указатель и некоторый индикатор его состояния. Добавьте поведение к умному указателю, чтобы он знал структуру. Структура будет содержать как указатель, так и состояние его выделенного объекта. Таким образом, дополнительное поведение умного указателя должно было бы обновить состояние выделенного объекта в структуре (например, установив его на некоторое значение, когда интеллектуальный указатель сделает освобождение).

0

В комментариях указано, что код C должен что-то удерживать и передать SmartPointer в C++ API, но ему не нужно ничего делать, кроме передачи его дословной другой функции C++.

Я думаю, что вам нужно создать некоторые думают, как std::enable_shared_from_this, скажем EnableSharedFromThis:

Сделайте MyClass унаследовать от EnableSharedFromThis:

struct MyClass : public EnableSharedFromThis, public AnotherBaseClass { 
//... 
}; 

Общий заголовок:

реализация
#ifdef __cplusplus 
extern "C" { 
#endif 

struct MyClass; 
MyClass * foo_acquire(void); 
int foo_bar(MyClass *); 
void foo_release(MyClass *); 

#ifdef __cplusplus 
} 
#endif 

C++:

List<SmartPointer<MyClass> > listToEnsureLifeTime; 
extern "C" MyClass * foo_acquire() 
{ 
    SmartPointer<MyClass> ptr = foo(); 
    listToEnsureLifeTime.Add(ptr); 
    return ptr.get(); 
} 

extern "C" int foo_bar(MyClass *s) 
{ 
    // bar represents some function expecting the smart pointer 
    return bar(s->SharedFromThis()); 
} 

extern "C" void foo_release(MyClass *s) 
{ 
    // I suppose this list can take difference SmartPointer with same 
    // inner have the same hash or something like that 
    listToEnsureLifeTime.erase(s->SharedFromThis()); 
} 
Смежные вопросы