Есть ли встроенный способ комбинирования прототипов 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);
)

Я не думаю, что сделаю это сам, но кажется, что он достаточно безопасен, чтобы стать идиоматичным.