Создание библиотеки шаблонных функций - PullRequest
2 голосов
/ 22 января 2010

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

// MyLib.h
class MyLib
{
  template<class T>
  static void Func1()
  {
  }

  template<class T>
  static void Func2()
  {
  }
};

И, очевидно, звонки будут сделаны так:

MyLib::Func1();

Как видите, это может быть довольно уродливо, так как добавляется больше функций. По крайней мере, я хотел бы разделить его на разные файлы!

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

Есть мысли?

PS: Поскольку большинство этих функций имеют разные цели, нет смысла группировать их по классу, из которого мы будем создавать объекты. Я использовал class здесь, поэтому мне не нужно беспокоиться о порядке, в котором я определил функции (также существует взаимозависимость между функциями внутри MyLib).

Ошибки компоновщика:

Итак, основная структура такая: у меня есть два класса (скажем, A & B), которые компилируются в статические библиотеки, и мастер-приложение, которое запускает экземпляры этих классов. Эти классы A & B используют функции в MyLib. Когда A & B компилируются, я получаю предупреждение LNK4006, в котором говорится, что символы, принадлежащие MyLib, уже определены в файле OBJ в проекте, и он игнорирует его.

Когда дело доходит до приложения, возникает ошибка LNK2005, которая гласит, что она уже определена в OBJ файлах A & B.

UPDATE: Спасибо Майк & Матье за идею inline - это была проблема!

За исключением одной проблемы: у меня есть несколько шаблонных функций, которые я специально специализировал, и они вызывают ошибку already defined (LNK2005):

template<class t> int Cvt(){}
template<> int Cvt<unsigned char>(){return 1;}
template<> int Cvt<char>(){return 2;}
template<> int Cvt<unsigned short>(){return 3;}

Есть идеи?

Conlusion:

Решил проблему явной специализации, определив функции шаблона в отдельном файле - спасибо за помощь!

Ответы [ 3 ]

5 голосов
/ 22 января 2010

Вы должны предпочесть пространство имен вашему классу статическими методами:

  • Пространство имен предлагает вам возможность совместного использования несколькими файлами, по одному на логическую группу методов
  • Пространство имен может быть опущено: либо из-за ADL, либо с using myNamespace::MyFunc; (примечание: писать using myNamespace; плохая практика, и вы должны избегать практики)

Теперь поговорим об организации:

  • это хорошая практика, чтобы ваша файловая иерархия скрывала иерархию пространства имен [1]
  • Хорошей практикой является разделение ваших методов по логическим группам, чтобы пользователю не приходилось включать весь мир только потому, что он хотел печатать Hello, World!, хотя товарные заголовки могут помочь (т. Е. Заголовки, которые делают кучу включений для использования ленивыми программистами)

[1] Вот что я имею в виду:

#include "lib/string/manip.hpp"    // Okay, this files come from "lib"

int main(int argc, char* argv[])
{
  std::string s;
  lib::string::manip(s);           // Same hierarchy, easy to remember the header
  return 0;
}

Мотивирующий пример? Boost делает это (с товарными заголовками).

И более того, это не стоит больших затрат: просто замените class на namespace и удалите ключевые слова static, вот и все.

Для проблемы с компоновщиком: все не шаблонизированные методы должны быть либо объявлены как inline (старайтесь избегать его, если они не являются однострочными), либо определены вне заголовка (в отдельном файле .cpp) ).

UPDATE:

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

Чтобы быть более конкретным в отношении этой странной ошибки: проблема C ++ заключается в том, что каждый исходный файл компилируется отдельно: препроцессор возьмет include и фактически создаст один текстовый файл, который будет содержать каждый отдельный включенный файл (в порядок), а затем ваш источник в конце. Компилятор берет этот файл и создает файл «.o» (для gcc). Затем включается компоновщик и пытается на самом деле создать библиотеку (или двоичную) из всех этих файлов «.o», и он проверяет, что каждый метод определен только один раз, потому что иначе как бы он выбрал между несколькими определениями (к сожалению, нет проверить, эквивалентны они или нет ...)?

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

// foo.h
template <class T> int foo(T) { return 10; }

// foo.cpp
#include "foo.h"

char a;
std::cout << foo(a) << std::endl;

// bar.cpp
#include "foo.h"

template <> int foo<char>(char) { return 20; }

char b;
std::cout << foo(b) << std::endl;

Обе строки выводят один и тот же вывод, хотя неизвестно, будет ли это 10 или 20, и может меняться между сборками !!!

4 голосов
/ 22 января 2010

Пространство имен MyLib - очевидный путь - в конце концов, именно так и поступает стандартная библиотека, которая, вероятно, значительно больше вашей. необычно получать много ошибок компоновщика с шаблонами, если у вас нет большого количества предварительных объявлений - чего вы должны избегать.

3 голосов
/ 22 января 2010

Использование пространства имен - правильный подход. Лично я бы не объединил их в один заголовок «включить весь мир», так как это приведет к увеличению времени компиляции. Другие могут предпочесть удобство одного заголовка.

Если есть какие-либо не шаблонные функции, то они должны быть либо объявлены inline, либо реализованы только в одном исходном файле. Шаблонные функции и функции-члены класса, реализованные внутри определения класса, неявно inline, но другие функции - нет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...