C ++: создание множества шаблонов в библиотеке - PullRequest
5 голосов
/ 27 июня 2019

У меня есть библиотека с классом по размеру:

foo.hpp:

template <size_t N>
struct Foo {
   void bar();
};

foo.cpp:

template <size_t N>
void Foo<N>::bar() {
...
};

Я хочу, чтобы скомпилированная версия была установлена ​​для определенного списка размеров, который будет определен в файле cpp. В псевдокоде что-то вроде этого

foo.cpp:

template <size_t N>
void Foo<N>::bar() {
...
};

for (const auto & size: {1,2,7,9})
    template struct Foo<size>;

Прямо сейчас я делаю это вручную (наглядно для каждого типа), но это подвержено ошибкам, так как у меня есть несколько файлов, которые должны быть определены для одного размера.

Мне известно, что я могу переместить все в файл заголовка и использовать некоторые enable_if для выбора допустимых размеров. Однако я хочу скрыть детали реализации. Отсюда и текущий подход.

РЕДАКТИРОВАТЬ:

FWIW, я надеялся избежать макросов. Я думал, что что-то вроде этого будет работать, если вы хотите диапазон переменных (например, 1,2,3,4 ...):

template<template<size_t> class E, size_t I = 1, std::enable_if_t<I >= 5> *unused = nullptr>
bool instatiator() {
  return true;
}

template<template<size_t> class E, size_t I = 1, std::enable_if_t<I < 5> *unused = nullptr>
bool instatiator() {
  E<I> e;
  return instatiator<E, I + 1>();
}

bool unused = instatiator<Encoder>();

но я не могу заставить это работать.

Ответы [ 3 ]

8 голосов
/ 27 июня 2019

Вы можете использовать для этого подход X macro :

valid_sizes.hpp

// Note: no include guards!!

PROCESS_SIZE(1)
PROCESS_SIZE(2)
PROCESS_SIZE(7)
PROCESS_SIZE(9)

#undef PROCESS_SIZE

foo.cpp

// ...

#define PROCESS_SIZE(n) template struct Foo<n>;
#include "valid_sizes.hpp"

bar.cpp

// ...

#define PROCESS_SIZE(n) some_other_use_of<n>;
#include "valid_sizes.hpp"

В качестве альтернативы, вместо списка вручную, вы можете использовать Boost.Preprocessor:

process_sizes.hpp

#pragma once

#define PROCESS_SIZES(macro, data) BOOST_PP_SEQ_FOR_EACH(macro, data, (1)(2)(7)(9))

некоторый заголовочный файл

#define INSTANTIATE_CLASS(r, Name, size) template class Name<size>;

foo.cpp

#include "process_sizes.hpp"

// ...

PROCESS_SIZES(INSTANTIATE_CLASS, Foo)

bar.cpp

#include "process_sizes.hpp"

// ...

PROCESS_SIZES(INSTANTIATE_CLASS, Bar)

#define OTHER_SIZE_BASED_WORK(r, data, size) other_use_of<size>;
PROCESS_SIZES(OTHER_SIZE_BASED_WORK, whatever)

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

А также обратите внимание, что неявное создание экземпляров (например, вызванное ссылкой на класс в контексте, который требует его определения) не является "видимым" вне единицы перевода, поэтому его нельзя использовать в качестве замены для явная реализация.

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

1 голос
/ 27 июня 2019

Вы можете рекурсивно создать экземпляр класса Foo с помощью вспомогательного класса.К сожалению, вам нужно обеспечить вызов каждого метода Foo в конструкторе (хотя методы не будут вызваны), чтобы предоставить определения компоновщику:

// foo.cpp: 

template <size_t S>
struct recursive_helper {
  Foo<S> foo;
  recursive_helper<S-1> r_helper;

  recursive_helper() {
    foo.bar();
    foo.baz();
  }
};

template <>
struct recursive_helper<0> {
  Foo<0> foo;
  recursive_helper() {
    foo.bar();
    foo.baz();
  };
};

И затем вы создаете экземпляркласс помощника: template struct recursive_helper<6>;

1 голос
/ 27 июня 2019

В дополнение к ответу @Angew: xmacros может быть другим способом, позволяя включать охранников, а также избегать наличия #include в середине кода:

valid_sizes.hpp

#pragma once

#define SIZES_TABLE(OP)\
OP(1) \
OP(2) \
OP(12)

foo.cpp

#include "valid_sizes.hpp"
// ...
#define INSTANTIATE(Size) template struct Foo<Size>;
SIZES_TABLE(INSTANTIATE)
#undef INSTANTIATE
...