Избегайте повторяющегося кода в шаблонных функциях C ++ - PullRequest
1 голос
/ 05 августа 2020

У меня очень ограниченный набор типов действительных чисел (в настоящее время float и double), на которые я использую sh все функции моей математической библиотеки, которые нужно использовать в качестве шаблона. Я ожидаю, что они будут объявлены в файлах заголовков:

template<typename R> void doA(task_t<R> t, ...);
template<typename R> void doB(task_t<R> t, ...);
template<typename R> void doC(task_t<R> t, ...);

реализовано в исходных файлах:

template<typename R> void doA(task_t<R> t, ...) { ... }
template<typename R> void doB(task_t<R> t, ...) { ... }
template<typename R> void doC(task_t<R> t, ...) { ... }

и явно инициализировано в этих исходных файлах:

template void doA(task_t<float> t, ...);
template void doB(task_t<float> t, ...);
template void doC(task_t<float> t, ...);

template void doA(task_t<double> t, ...);
template void doB(task_t<double> t, ...);
template void doC(task_t<double> t, ...);

Кроме того, подмножество из них должно быть предложено через C -совместимый заголовок:

#ifdef __cplusplus
using taskr32_t = task_t<float>;
using taskr64_t = task_t<double>;
#else
typedef struct taskr32t *taskr32_t;
typedef struct taskr64t *taskr64_t;
#endif

#ifdef __cplusplus
extern "C" {
#endif

void doAr32(taskr32_t t, ...);
void doBr32(taskr32_t t, ...);

void doAr64(taskr64_t t, ...);
void doBr64(taskr64_t t, ...);

#ifdef __cplusplus
}
#endif

, который я даю простейшую возможную реализацию пересылки C ++:

void doAr32(taskr32_t t, ...) { doA<float>(t, ...) };
void doBr32(taskr32_t t, ...) { doB<float>(t, ...) };

void doAr64(taskr64_t t, ...) { doA<double>(t, ...) };
void doBr64(taskr64_t t, ...) { doB<double>(t, ...) };

То есть уже 8 повторений полного doA объявления функции. Мой вопрос в том, как уменьшить количество повторений и получить более чистый код с меньшим набором текста? Например, возможно ли выполнить явную инициализацию без повторения полного объявления?

Я знаю, что многие предпочитают помещать реализации шаблонных функций непосредственно в файлы заголовков, чтобы избежать как прямого объявления, так и явной инициализации, но есть аргументы против :

  • Я точно знаю, какие типы поддерживаю, и набор очень ограничен.
  • Время компиляции увеличивается (особенно инкрементальные компиляции мелких изменений).
  • Зависимости ( doA зависит от doB и т. д.) становится труднее поддерживать без предварительных объявлений.

Моя лучшая идея до сих пор - просто сгенерировать код, касающийся явного создания экземпляра и C -совместимого уровня, который находится там, где все надоедливые повторы случаются. Это обычное / хорошо известное решение этой проблемы?

1 Ответ

0 голосов
/ 05 августа 2020

Вы можете написать макрос, который будет создавать вашу функцию для всех типов, которые вы хотите.

См. BOOST_PP_SEQ_FOR_EACH . Пример приложения к вашему коду может быть:

#include <boost/preprocessor/seq/for_each.hpp>

template <class T> class task_t {};

template<typename R> void doA(task_t<R> t) {}
template<typename R> void doB(task_t<R> t) {}
template<typename R> void doC(task_t<R> t) {}

#define MY_TYPES (int)(float)(double)(bool)

#define INSTANTIATE_FOR_TYPE(r, data, my_type) \
template void doA(task_t<my_type> t); \
template void doB(task_t<my_type> t); \
template void doC(task_t<my_type> t);


BOOST_PP_SEQ_FOR_EACH(INSTANTIATE_FOR_TYPE, _, MY_TYPES)                                                                                                     

int main() {}

После компиляции можно увидеть doAB C, созданный для всех типов

nm a.out | c++filt | grep \ do
000000000040053c W void doA<bool>(task_t<bool>)
0000000000400527 W void doA<double>(task_t<double>)
0000000000400512 W void doA<float>(task_t<float>)
00000000004004fd W void doA<int>(task_t<int>)
0000000000400543 W void doB<bool>(task_t<bool>)
000000000040052e W void doB<double>(task_t<double>)
0000000000400519 W void doB<float>(task_t<float>)
0000000000400504 W void doB<int>(task_t<int>)
000000000040054a W void doC<bool>(task_t<bool>)
0000000000400535 W void doC<double>(task_t<double>)
0000000000400520 W void doC<float>(task_t<float>)
000000000040050b W void doC<int>(task_t<int>)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...