Я пытаюсь воссоздать некоторые классы из стандартной библиотеки C ++ в C. Например, класс std :: pair.
Конечно, для эмуляции шаблонов я использовал макросы. Вот пример того, как это выглядит:
#define _TEMPLATE_PAIR_STRUCT(T1, T2, TNAME, STRNAME) \
typedef struct { \
T1* first; \
T2* second; \
} STRNAME;
#define _TEMPLATE_PAIR_NEW(T1, T2, TNAME, STRNAME) \
STRNAME* TNAME##_new() \
{ \
STRNAME *new = malloc(sizeof( STRNAME )); \
new->first = malloc(sizeof(T1)); \
new->second = malloc(sizeof(T2)); \
return new; \
}
Если я пытаюсь использовать эту структуру в нескольких исходных файлах, мне придется генерировать код несколько раз. Это, очевидно, приводит к ошибке.
Есть ли способ исправить это, чтобы я мог использовать эти «шаблоны» в C?
Как уже говорили другие, нужно помнить о нескольких вещах, в основном убедиться, что существует только одно определение функции.
Мне не особенно нравится это решение, но оно здесь.
Один заголовок, чтобы управлять ими всеми (pair.h)
#ifndef TEMPLATE_PAIR
#define TEMPLATE_PAIR
#include <stdlib.h>
#define PAIR_NAME( T1, T2 ) T1##T2##NAME
#define PAIR_PTR_NAME( T1, T2 ) T1##T2##PTR_NAME
#define PAIR_CREATE( T1, T2) MAKE##T1##T2
#define PAIR_PTR_CREATE( T1, T2) MAKE_PTR##T1##T2
#define PAIR_PTR_FREE(T1, T2) FREE##T1##T2
#define PAIR_DEFINITION( T1, T2) \
typedef struct { \
T1 first; \
T2 second ; \
} PAIR_NAME(T1, T2)
#define PAIR_PTR_DEFINITION( T1, T2) \
typedef struct { \
T1* first; \
T2* second ; \
} PAIR_PTR_NAME(T1, T2)
#define MAKE_PAIR_DECLARE(T1, T2) PAIR_NAME(T1, T2) PAIR_CREATE(T1, T2) ( const T1& V1, const T2& V2 )
#define MAKE_PAIR_PTR_DECLARE(T1, T2) PAIR_PTR_NAME(T1, T2) PAIR_PTR_CREATE(T1, T2) ( const T1& V1, const T2& V2 )
#define PAIR_PTR_FREE_DECLARE(T1, T2) void PAIR_PTR_FREE(T1, T2) ( PAIR_PTR_NAME(T1, T2) & Pair )
#define MAKE_PAIR_SIGNATURE(T1, T2) PAIR_NAME(T1, T2) PAIR_CREATE(T1, T2) ( const T1& V1, const T2& V2 )
#define MAKE_PAIR_PTR_SIGNATURE(T1, T2) PAIR_PTR_NAME(T1, T2) PAIR_PTR_CREATE(T1, T2) ( const T1& V1, const T2& V2 )
#define FREE_PAIR_PTR_SIGNATURE(T1, T2) void PAIR_PTR_FREE(T1, T2) ( PAIR_PTR_NAME(T1, T2) & Pair )
#define MAKE_PAIR_DEFINE( T1, T2 ) \
MAKE_PAIR_SIGNATURE(T1, T2) { \
PAIR_NAME(T1, T2) pair; \
pair.first = V1; \
pair.second = V2; \
return pair; \
}
#define MAKE_PAIR_PTR_DEFINE( T1, T2 ) \
MAKE_PAIR_PTR_SIGNATURE(T1, T2) { \
PAIR_PTR_NAME(T1, T2) pair; \
pair.first = malloc( sizeof(T1) ); \
if ( pair.first != 0 ) *(pair.first) = V1; \
pair.second = malloc( sizeof( T2) ) ; \
if ( pair.second != 0 ) *(pair.second) = V2; \
return pair; \
}
#define PAIR_PTR_FREE_DEFINE( T1, T2 ) \
FREE_PAIR_PTR_SIGNATURE(T1, T2) { \
free( Pair.first ); \
free( Pair.second ); \
}
#endif
Один заголовок, чтобы привести их всех (defs.h):
#ifndef DEFS_HEADER
#define DEFS_HEADER
#include "pair.h"
typedef int* pInt;
PAIR_DEFINITION( int, int );
PAIR_DEFINITION( int, double );
PAIR_DEFINITION( double, double );
PAIR_DEFINITION( pInt, pInt );
PAIR_DEFINITION( float, int );
PAIR_PTR_DEFINITION( int, int );
MAKE_PAIR_DECLARE( int, int );
MAKE_PAIR_DECLARE( int, double );
MAKE_PAIR_DECLARE( double, double );
MAKE_PAIR_DECLARE( pInt, pInt );
MAKE_PAIR_DECLARE( float, int );
MAKE_PAIR_PTR_DECLARE( int, int );
PAIR_PTR_FREE_DECLARE( int, int );
#endif
И во тьме свяжи их (импл.с)
#include "defs.h"
MAKE_PAIR_DEFINE( int, int );
MAKE_PAIR_DEFINE( int, double );
MAKE_PAIR_DEFINE( double, double );
MAKE_PAIR_DEFINE( pInt, pInt );
// manual "instantiation"MAKE_PAIR_SIGNATURE( float, int )
{
PAIR_NAME( float, int ) local;
local.first = V1;
local.second = V2;
return local;
}
MAKE_PAIR_PTR_DEFINE( int, int );
PAIR_PTR_FREE_DEFINE( int, int );
В земле главной, где лежат тени:
#include "defs.h"
int main(void)
{
PAIR_NAME(int, int) myPairInts;
PAIR_NAME( double, double) myPairDoubles;
PAIR_NAME( pInt, pInt) myPairPointers;
PAIR_NAME( float, int) myPairOther;
PAIR_PTR_NAME( int, int ) pairPtr;myPairInts = PAIR_CREATE( int, int ) (1, 2);
myPairDoubles = PAIR_CREATE( double, double ) (5, 6.5);
myPairPointers = PAIR_CREATE( pInt, pInt) ( 0, 0 );
myPairOther = PAIR_CREATE( float, int) (1, 1);
pairPtr = PAIR_PTR_CREATE(int, int) (1, 2 );
PAIR_PTR_FREE(int, int) (pairPtr );return 0;
}
PAIR_NAME
создает структуру, содержащую значения, PAIR_PTR_NAME
содержит указатели на значения. PAIR_CREATE
а также PAIR_PTR_CREATE
создать значения и заполнить данные внутри пары.
Вам нужно будет определить все необходимые комбинации в «impl.c». Любой модуль компиляции может #include "defs.h"
и использовать пары.
РЕДАКТИРОВАТЬ — Ответы на вопросы:
«pair.h» содержит только макросы, его можно безопасно использовать в любой библиотеке или программе.
Важно то, что вы не определяете одну и ту же функцию дважды. Я бы не стал определять одну и ту же структуру дважды.
Вы можете сделать следующее, хотя:
— возьмите pair.h, defs.h и impl.c, как указано выше, и соберите их в библиотеку (добавьте все необходимые __declspec(dllexport)
а также __declspec(dllimport
)
— вы можете тогда #include pair.h
а также defs.h
и использовать пары, определенные там в программе
— если вы хотите использовать новые пары, скажем, для (float, float)
вам нужно будет добавить новый defs_my_program.h
и новый impl_my_program.c
объявить и определить эти новые пары. defs_my_program.h
заголовок может быть включен вместе с defs.h
заголовок, который предоставляет библиотека.
Вы по-прежнему получаете одно объявление и одно определение для каждой пары. Единственным недостатком является то, что вы не можете (безопасно) использовать пары «на лету», их нужно централизовать.
Ну, для начала, вы попросили std::pair
и не для тупло.
Обратите внимание, что std::pair
является эквивалентом PAIR_NAME
, здесь нет std::pair
который выделяет динамическую память.
Вам не нужно никаких новых специализаций, если вам не нужно автоматически использовать malloc
, Пример с pInt
показывает, что вы можете создать с pair
из (int, int*)
или же (int*, int)
, Просто значение указатель должен прийти извне pair
,
Если вы действительно хотите pair
из (int, int*)
который автоматически выделяет память для int*
Вы должны будете добавить это самостоятельно если вам это действительно нужно. Я надеюсь, что вы не будете.
Для кортежей значений не очень оптимальным решением может быть использование pair
из pair
и еще один элемент. Это даст вам структуру, содержащую три элемента. Нечто подобное можно, вероятно, сделать с помощью макромагиков, сократив тот экспоненциальный рост, о котором вы беспокоитесь.
Объявление структуры несколько раз не является проблемой.
Определение функции несколько раз является проблемой. Если вы сделаете функцию статичной, то это станет проблемой для каждого файла. Даже в C ++ иногда люди явно создают экземпляры шаблонов. Вы можете иметь один файл, который имеет все новые функции.
Если вам нужно автоматическое решение, вам нужно посмотреть, как компиляторы C ++ создают экземпляры шаблонов.
Вот один метод, который использовался давным-давно.
Абстрагируясь от вопроса, если это хорошая идея, у вас есть несколько ошибок в вашей реализации.
;
внутри макроса это полностью противоречит визуальным ожиданиям в месте, где это будет использоваться.void
внутри ()
,new
функция не будет инициализировать данные,new
как локальная переменная, плохо, если выinline
определение и (3) внешний «экземпляр», который позволяет