Шаблоны из C ++ в C

Я пытаюсь воссоздать некоторые классы из стандартной библиотеки 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?

2

Решение

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

Мне не особенно нравится это решение, но оно здесь.

Один заголовок, чтобы управлять ими всеми (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" и использовать пары.

РЕДАКТИРОВАТЬ — Ответы на вопросы:

  1. «Не вызовет ли это проблемы, когда я использую это один раз в библиотеке или что-то в этом роде, а затем снова в программе, которая использует и эту библиотеку, и пару« шаблон »?»

«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 заголовок, который предоставляет библиотека.

Вы по-прежнему получаете одно объявление и одно определение для каждой пары. Единственным недостатком является то, что вы не можете (безопасно) использовать пары «на лету», их нужно централизовать.

  1. «То, как вы выбрали имена типов и функций, вызывает некоторые проблемы. Вы разбили пару с двумя значениями и одним с двумя указателями. Вам также понадобится специализация для пары с одним указателем и одним значением, и для пары с одним значением и одним указателем. Таким образом, у меня уже было бы 4 случая пар. Используя это для триплетов или даже более высоких кортежей, я должен был бы реализовать 2 ^ n случаев. «

Ну, для начала, вы попросили std::pair и не для тупло.

Обратите внимание, что std::pair является эквивалентом PAIR_NAME, здесь нет std::pair который выделяет динамическую память.

Вам не нужно никаких новых специализаций, если вам не нужно автоматически использовать malloc, Пример с pInt показывает, что вы можете создать с pair из (int, int*) или же (int*, int), Просто значение указатель должен прийти извне pair,

Если вы действительно хотите pair из (int, int*) который автоматически выделяет память для int* Вы должны будете добавить это самостоятельно если вам это действительно нужно. Я надеюсь, что вы не будете.

Для кортежей значений не очень оптимальным решением может быть использование pair из pair и еще один элемент. Это даст вам структуру, содержащую три элемента. Нечто подобное можно, вероятно, сделать с помощью макромагиков, сократив тот экспоненциальный рост, о котором вы беспокоитесь.

5

Другие решения

Объявление структуры несколько раз не является проблемой.

Определение функции несколько раз является проблемой. Если вы сделаете функцию статичной, то это станет проблемой для каждого файла. Даже в C ++ иногда люди явно создают экземпляры шаблонов. Вы можете иметь один файл, который имеет все новые функции.

Если вам нужно автоматическое решение, вам нужно посмотреть, как компиляторы C ++ создают экземпляры шаблонов.
Вот один метод, который использовался давным-давно.

  • Скомпилируйте и свяжите ваш код без использования PAIR_NEW
  • Вы получите неопределенные функции _new ().
  • Запустите сценарий, который генерирует файл C с соответствующими вызовами макросов PAIR_NEW (), чтобы определить неопределенные символы
  • скомпилируйте этот новый файл и перекомпоновайте ваш проект, включая новый файл.
2

Абстрагируясь от вопроса, если это хорошая идея, у вас есть несколько ошибок в вашей реализации.

  • никогда не скрывать ; внутри макроса это полностью противоречит визуальным ожиданиям в месте, где это будет использоваться.
  • кроме C ++, функция, которая не получает параметр, должна
    иметь void внутри (),
  • имена, начинающиеся с подчеркивания и заглавной буквы, зарезервированы
    для реализации C во всех контекстах. Выбери лучшее название
    условность.
  • кроме как в C ++ ваш new функция не будет инициализировать данные,
    это тоже плохое соглашение об именах.
  • вы используете идентификатор new как локальная переменная, плохо, если вы
    хочу связать это с какой-то C ++ один день
  • Ваш макрос для функции должен быть разделен на три части: (1) для
    соглашение об именовании, которое объединяет имя функции,
    (2) inline определение и (3) внешний «экземпляр», который позволяет
    вы генерируете символ функции ровно в одной единице компиляции.
1
По вопросам рекламы [email protected]