2016-03-28 4 views
2

Я обертываю библиотеку C++, написав интерфейс C. По этой причине я создал файл заголовка C, где большинство функций возвращают/принимают void* вместо указателей класса C++.Кастинг для void * и typedefs в C++

Я знаю, что это опасно отлиты из/к мочеиспусканию * в C++, см Casting to void* and Back to Original_Data_Type*

В настоящее время при наследовании я всегда литейным базового класса объекта перед назначением к мочеиспусканию *

void* temp = static_cast<BaseClass*>(ptr) 

и обратно

BaseClass* base = static_cast<BaseClass*>(voidPtr) 
DerivedClass* derived = dynamic_cast<DerivedClass*>(base) 

Однако использование void* в заголовке файлов удалить семантику и сделать гр Функции omplex трудно читать. Например:

void myComplexFunc(void* index, void* data, void* somethingElse) 

Для смягчения этого я хотел использовать определений типов, что даже если они не дают каких-либо типобезопасность по крайней мере, они дают читателю некоторое представление. Сравните предыдущий фрагмент с этим

extern "C" 
{ 
... 
typedef void Index; 
typedef void DBRecord; 
typedef void DBResult; 
void myComplexFunc(Index* index, DBRecord* data, DBResult* somethingElse) 
... 
} 

В основном эти определения типов действуют как своего рода для документирования когда несколько void* используются в качестве параметров функции (Имейте в виду, что я буду оборачивать несколько классов каждый C++ с 10 способами , поэтому есть множество функций, которые вставляют void * в качестве первого параметра).

По этой причине я хотел использовать для определения типов

extern "C" // In.h file (declaration) 
{ 
typedef void Index; 
... 
Index* getIndex(); 
void modifyIndex(Index* index); 
... 
// In .cpp file (definition) 
Index* getIndex() { 
    DerivedCppIndex* derived = ... 
    BaseCppIndex* base = static_cast<BaseCppIndex*>(derived) 
    return base; 
} 
... 
void modifyIndex(Index* index) { 
    BaseCppIndex* base = static_cast<BaseCppIndex*>(index); 
    DerivedCppIndex* derived = dynamic_cast<DerivedCppIndex*>(base); 
    ... 
} 

ли использование ЬурейеГо вместо пустоты * вызывает какие-либо проблемы в отношении вопросов присвоения недействительных *?

+0

Есть ли причина, по которой вы это делаете? Кажется, это супер плохая идея. – tadman

+0

Я думаю (после написания моего ответа), что он не хочет раскрывать свою иерархию типов пользователю; поэтому, хотя из его кода 'DerivedIndex' конвертируется в' Index', он не будет работать, если все, что видно, это 'class Index; класса DerivedIndex'. –

+1

звучит как проблема x-y, пожалуйста, предоставьте пример использования и фон. – user3528438

ответ

2

Это не должно вызывать никаких проблем, но это только даст пользователям API ложное чувство безопасности (как и любой произвольный указатель также будет работать). Там нет ничего, чтобы предотвратить компилятор жаловаться, если вы делаете что-то вроде:

int i; 
// .... 
modifyIndex(&i); 

Так что пока у вас не будет никаких проблем, вы не будете иметь никакого время компиляции проверки и подкрепления либо.

Обратите внимание, что у вас есть выбор просто объявить класс, фактически не определяя его.

Правильный путь решения пустоты указатель «проблемы» (и это на самом деле не проблема, это только один из аспектов C (и, следовательно, C++), который может сделать код трудно поддерживать без надлежащего ухода/документации) это как-то разоблачить Index как непрозрачный тип; например

// index.h 
class Index; 
// ... 
void modifyIndex(Index *i); 
+0

Я согласен с вами, но мой вариант использования - это упаковка библиотеки C, поэтому я не могу использовать класс в моем общедоступном заголовочном файле (и это причина использования void *) –

+0

Вы все равно можете сделать dummy struct при запуске под C, например: '#ifndef __cplusplus typedef struct IndexCDummy Index'. Я использовал этот подход в прошлом - он обеспечивает безопасность пользовательского типа. «IndexCDummy» в этом случае нигде не определяется, но это делает компилятор осведомленным об этом набранном непрозрачном указателе. Я бы поставил это в свой ответ, но это существенно повлияло бы на ответ –

0

Создание typedef для void немного неудобно. Более распространенной практикой является определение void* как IndexHandle и принуждение клиентов вашего кода читать или передавать в качестве параметра значение непрозрачного типа дескриптора. Все литье должно происходить внутри вашего кода.

Смежные вопросы