Реализация шаблонной функции в С

Как известно в языке С нет шаблонов в отличие С++, но реализация шаблонной функции возможна средствами препроцессора стандартного С. Это делается с помощью макро ##. Или его еще называют оператором конкатенации.
Следующее макро определение позволяет два токена соединить вместе.

 
#define TOKENPASTE(x, y) x##y

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

 
#define define_get_minimum(T) \
T get_minimum_##T(T* nums, int len){ \
    T min = nums[0]; \
    for (int i = 1; i < len; i++) { \
        if (nums[i] < min) { \
            min = nums[i]; \
        } \
    } \
    return min; \
}

Теперь понадобится прототип функции. В С++ это встроено в компилятор

 define_get_minimum(int)

Далее нужна спецификация шаблона

 #define GetMinimum(T) get_minimum_##T

Теперь все готово для использования нашего шаблона

 

int arr[3] = { 1,2,3 };
int main(void)
{
  ...  
  int res = GetMinimum(int)(arr, 3);
  ...
}

Конечно если вы пишете в С++ вам это все не понадобится. Этот пример лишь демонстрирует как можно использовать макро конкатенации ##, кроме того возможно дает лучше понять что такое шаблоны.
Все что описано выше и многое другое вы можете найти в книге Энтони Портера «The best C/C++ tips ever»
Там приводится пример когда в те стародавние времена (1993г) не все компиляторы C++ имели поддержку шаблонов и для этого использовался оператор конкатенации препроцессора.


#define name(n1, n2)          n1 ## n2
#define name1(n1, n2, n3)     n1 ## n2 ## n3
#define name2(n1, n2, n3, n4) n1 ## n2 ## n3 ## n4

#define declare(a, type)             a##declara(type)
#define implement(a, type)           a##implement(type)
#define declare2(a, type1, type2)    a##declarе2(type1,tpe2)
#define implement2(a,type1, type2)   a##implement2(type1, type2)

макросы «name» просто объединяют несколько имен для формирования одного идентификатора.
Обратите внимание, что хотя между аргументами макроса и конкатенацией ## существует некоторое количество пробелов, препроцессор удаляет пространство перед контактирующими именами. Чтобы объявить шаблон, требуется только использовать «declare» макрос.

А теперь попробуем написать средствами препроцессора шаблон класса «List» который наследует от класса «VoidList». Класс «VoidList» содержит указатели на «void».

#define List(type) name2(List, type) 

#define ListDelare(type)  class List(type): VoidList\
{
  public:\
  List(type)():VoidList(){}\
  List(type)(type *a): VoidList(a){}\
  int insert(type *a) { return VoidList::insert(a);}\
  int append(type *a) { return VoidList::append(a);}\
  int unlink(type *a) { return VoidList::unlink(a);}\
  type* get() { return (type*)VoidList::get();}\
};

Как видно из кода там есть два макроса «List(type)» и «ListDelare(type)». «ListDelare(type)» будет использования программой для спецификации шаблона, например, для класса «Complex»

...
ListDeclare(Complex);
...

Теперь для получения экземпляра этого класса и его использования сделать следующее

...
ListDeclare(Complex) aComplexList;

aComplexList.insert( new Complex(3.5, -4.5));
...

Сегодня такая симуляция шаблона в C++ вряд ли кому-нибудь может понадобиться , но в С такой трюк полезная вещь о чем собственно и статья.

Оставьте первый комментарий

Оставить комментарий

Ваш электронный адрес не будет опубликован.


*