Есть ли встроенный способ комбинирования прототипов c и С++?
Я хочу встроенный способ указать, какие прототипы должны быть включены в С++. Например:
void ArrayList_insert(ArrayList *arrlst, void *data, int i);
IS_CPP void ArrayList_insert(ArrayList *arrlst, char *data, int i);
IS_CPP void ArrayList_insert(ArrayList *arrlst, Buffer *data, int i);
в настоящее время я делаю:
#ifdef __cplusplus
extern "C" {
#endif
....C HEADERS..
#ifdef __cplusplus
}
....C++ HEADERS...
#endif
но его очень неудобно, потому что перегрузки одной и той же функции находятся в разных местах. Я мог бы просто иметь два разных файла заголовка, но это тоже боль. Поэтому Im ищет встроенное решение, подобное предложению выше. Кто-нибудь знает, как это сделать?
Ответы
Ответ 1
Это проще, чем вы думаете.
#ifdef __cplusplus
#define IS_C(x) extern "C" x ;
#define IS_CPP(x) x ;
#else
#define IS_C(x) x ;
#define IS_CPP(x)
#endif
С этим типом заголовка:
IS_C (void ArrayList_insert(ArrayList *arrlst, void *data, int i))
IS_CPP (void ArrayList_insert(ArrayList *arrlst, char *data, int i))
IS_CPP (void ArrayList_insert(ArrayList *arrlst, Buffer *data, int i))
Ответ 2
Конечно, вы можете сделать это почти так же, как ваш пример, используя макрос функции:
#ifdef __cplusplus
#define IS_CPP(x) x
#else
#define IS_CPP(x)
#endif
void ArrayList_insert(ArrayList *arrlst, void *data, int i);
IS_CPP(void ArrayList_insert(ArrayList *arrlst, char *data, int i));
IS_CPP(void ArrayList_insert(ArrayList *arrlst, Buffer *data, int i));
Теперь, если вы скомпилируете заголовок как С++, вы получите все три, но если вы скомпилируете C, вы получите только один. Если вы хотите разделить одну библиотеку между ними, вам нужно добавить некоторые extern "C"
квалификаторы к функции C при компиляции для С++. Ответ @MarkLakata показывает один из возможных способов.
Ответ 3
Обычный подход заключается в том, чтобы просто написать его наиболее очевидным образом:
void ArrayList_insert(ArrayList *arrlst, void *data, int i);
#ifdef __cplusplus
void ArrayList_insert(ArrayList *arrlst, char *data, int i);
void ArrayList_insert(ArrayList *arrlst, Buffer *data, int i);
#endif /* __cplusplus */
Как указывает @chacham15, вам также понадобится в заголовке проекта,
#ifdef __cplusplus
#define EXTERN_C extern "C"
#endif /* __cplusplus */
и вам нужно украсить C-вызываемую функцию EXTERN_C
.
Ответ 4
Очевидно, вы можете злоупотреблять препроцессором, чтобы взломать то, что вы просите, но зачем это делать? Лично я предпочитаю вводить mylst.insert(foop, 1);
вместо ArrayList_insert(mylst, foop, 1);
, если бы я использовал С++. Другими словами, я мало польжу от использования стиля C для вызова перегруженных функций, но смешивание стилей вызовов функций, которые вы, как создатель кода, тоже не совсем красивы.
Я бы сделал класс ArrayList, который имеет те же элементы, что и в структуре C, и в любое время вам нужно связать свой класс с функциями C, создать по возможности мелкую копию и передать ее функции C, затем скопировать информацию из этой структуры обратно в ваш класс.
Альтернативой было бы обернуть структуру в классе С++ и использовать ее для функций интерфейса C.
В противном случае вы можете попытаться сделать класс наследованием структуры C, предполагая, что структурный тег не называется ArrayList, а тип typedef для структуры не отображается на интерфейсе С++. Затем вы можете надеяться передать этот указатель непосредственно изнутри функции-члена, как если бы это была фактическая структура C. Я не уверен, что этот метод переносим во всех случаях, поэтому я бы использовал бывшую идею, если это было возможно, даже если это требует копирования данных взад и вперед.
Все идеи избегают дублирования кода, а интерфейс С++ больше похож на код С++ вместо плохой смеси функций C и перегрузки функций С++. Кроме того, интерфейсы несколько отличаются друг от друга. Никаких дополнительных файлов заголовков не требуется, так как функции C могут быть обернуты во внешнем блоке "C", как обычно:
#ifdef __cplusplus
extern "C" {
#endif
struct array_list_tag {
...
};
/* C functions here */
#ifdef __cplusplus
} /* extern "C" */
class ArrayList ...
#else /* !__cplusplus */
typedef struct array_list_tag ArrayList;
#endif
Ответ 5
Если вы действительно хотите избавиться от шаблона, и вы готовы использовать препроцессор, чтобы сделать это, тогда просто зайдите и напишите шаблон. Общий шаблон, который у вас похож на
extern "C" {
void C_accessible_declaration(); // this is all C sees
}
void Cxx_accessible_declaration_1( int );
void Cxx_accessible_declaration_1( long );
Итак, вы можете сделать макрос,
#ifdef __cplusplus
# define C_PORTABLE_FUNCTION_SET( C_DECLS, CXX_DECLS ) \
extern "C" { C_DECLS } \
CXX_DECLS
#else
# define C_PORTABLE_FUNCTION_SET( C_DECLS, CXX_DECLS ) \
C_DECLS
#endif
Это работает, потому что объявление обычной функции не может содержать запятую, не заключенную в круглые скобки. Если вы хотите, чтобы он работал с шаблонами (с параметрами шаблона с разделителями-запятыми), вы можете использовать переменные макросы, поддерживаемые в C99, С++ 11, и различные компиляторы, предшествующие этим стандартам, в качестве расширения.
#ifdef __cplusplus
# define C_PORTABLE_FUNCTION_SET( C_DECLS, ... ) \
extern "C" { C_DECLS } \
__VA_ARGS__
#else
# define C_PORTABLE_FUNCTION_SET( C_DECLS, ... ) \
C_DECLS
#endif
Теперь это работает до тех пор, пока объявления C не содержат голой запятой, что означает, что вы не должны объявлять несколько объектов в одном объявлении. Я назвал его C_PORTABLE_FUNCTION_SET
, чтобы подчеркнуть, что он в основном безопасен для использования с объявлениями функций, но обратите внимание, что вам нужно объявить C-доступные объекты в extern C
. Общие определения struct
не должны быть защищены вообще; они защищены концепцией С++ POD и не имеют языковой привязки.
Использование:
#ifdef __cplusplus
template< typename T, typename U >
class Buffer { // still use #ifdef for the general case
...
};
#endif
C_PORTABLE_FUNCTION_SET (
void ArrayList_insert(ArrayList *arrlst, void *data, int i);
, /* C++ */
void ArrayList_insert(ArrayList *arrlst, char *data, int i);
template< typename T, typename U >
void ArrayList_insert(ArrayList *arrlst, Buffer< T, U > &data, int i);
)
Я не думаю, что сделаю это сам, но кажется, что он достаточно безопасен, чтобы стать идиоматичным.