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