Я хотел бы предоставить библиотеку, которая предоставляет шаблон кода. Но я также хотел бы сохранить максимально возможную принадлежность этого кода (сгенерированного кода), когда я могу предположить использование различных обычных типов моего шаблона. Вот пример того, что я пытаюсь сделать:
lib1.h
#include <iostream>
template<int N>
void print_me() {
std::cout << "I am function number " << N << std::endl;
}
lib1.cpp
#include "lib1.h"
/* Force symbols to be defined here. */
template void print_me<0>();
template void print_me<1>();
Я компилирую свою библиотеку, используя:
g ++ -shared -fPIC lib1.cpp -o lib1.so
И когда я использую свою библиотеку:
main.cpp
#include <lib1.h>
int main() {
print_me<0>();
print_me<1>();
print_me<2>();
}
Составлено с:
g ++ main.cpp -l1
Здесь я ожидаю, что символ print_me<0> () и print_me<1> () определены и используются из lib1.so и print_me<2> () определено и используется для моего исполняемого файла (проверяется с помощью nm —defined-only). Но похоже, что это не так! Символы для 0 и 1 хорошо определены в lib1.so, но как слабый символы. И снова переопределены в моем исполняемом файле (0, 1 и 2), снова слабый. Это означает, что код для 0 и 1 для моего исполняемого файла взят из main.cpp, а это не то, что я хочу (я проверил со спецификацией в main.cpp).
Есть ли способ (например, в lib1.h) сказать во время компиляции main.cpp, что символы где-то уже определены и что нет необходимости добавлять эти символы?
C ++ 11 решение: использовать внешние шаблоны. Просто добавьте эти строки в свой main.cpp
файл:
extern template void print_me<0>();
extern template void print_me<1>();
Таким образом вы говорите компилятору не создавать экземпляр print_me
шаблон функции в main.cpp
(для аргументов шаблона 0 и 1). Поэтому линкер должен искать определение void print_me<0>();
а также void print_me<1>();
в других переводческих подразделениях.
Я бы отделил реализацию и интерфейс, используя специализацию шаблонов:
lib1.h:
#include <iostream>
template <int T> void print_me(void);
template <> void print_me<0>(void);
template <> void print_me<1>(void);
lib1_internal.h (ПРИМЕЧАНИЕ: это не нужно раскрывать):
#include <iostream>
template<int N>
void print_me_internal() {
std::cout << "I am function number " << N << std::endl;
}
lib1.cpp:
#include "lib1.h"#include "lib1_internal.h"
template <> void print_me<0>() {
print_me_internal<0>();
}
template <> void print_me<1>() {
print_me_internal<1>();
}
ваш main.cpp будет правильно приводить к ошибке компоновщика:
$ g++ main.cpp -L. -l1
/tmp/ccZSDqkp.o: In function `main':
main.cpp:(.text+0xf): undefined reference to `void print_me<2>()'
collect2: ld returned 1 exit status
Просто добавьте определение template <int T> void print_me(void)
в lib1.h
вместо его объявления, и он будет использоваться всякий раз, когда не найден в специализированных версиях.
Вы должны скрыть реализацию в заголовочном файле.
//lib1.h
template<int N>
void print_me();
//lib1.cpp
#include <iostream>
#include "lib1.h"template <int printme>
void print_me()
{
std::cout << printme << std::endl;
}
template void print_me<0>();
template void print_me<1>();
Происходит то, как обычно используются шаблоны: они создаются только тогда, когда это необходимо (в противном случае вам нужно создать print_me для всех целых чисел), поэтому они выясняют реализацию при запуске программного обеспечения (именно поэтому оно замедляет время компиляции так много, он перестраивает класс для каждого модуля компиляции, который использует шаблон). Пока у вас есть определение шаблона, вы можете создать его для любого аргумента, который вам нужен. Это один из немногих случаев, когда скрытие определения в файле .cpp является полу-допустимым, но конечный пользователь обычно получает чертовски бесполезную ошибку для print_me<2> (). Было бы лучше (imho) использовать этот фреймворк (C ++ 11):
//lib1.h
#include <type_traits>
template<int N>
typename std::enable_if<N == 1 || N == 0>::type print_me();
//lib1.cpp
#include <iostream>
#include "lib1.h"
template<int N>
typename std::enable_if<N == 1 || N == 0>::type print_me()
{
std::cout << N << std::endl;
}
template void print_me<0>();
template void print_me<1>();
теперь ошибка «нет соответствующей функции для вызова print_me ()», что немного лучше … вы можете сделать вложенную функцию с static_assert, чтобы сделать ее еще лучше.