проблема специализации шаблонной функции - PullRequest
3 голосов
/ 20 августа 2009

Я использую шаблоны для преобразования диапазона с проверкой диапазона из int в enum. Это выглядит так:

template<typename E>
E enum_cast(const int &source);

Функция шаблона размещена более или менее в корневой директории проекта. При определении нового перечисления, которому, как предполагается, будут присвоены значения из файла конфигурации, например:

enum ConfigEnum {
    ConfigEnumOption1 = 'A'
  , ConfigEnumOption2 = 'B'
  , ConfigEnumInvalid };

ConfigEnum option = XmlNode.iAttribute("option");

Я определяю специализацию шаблона для этого конкретного типа перечисления в .cpp-файле для модуля, в котором это перечисление используется.

template<>
ConfigEnum enum_cast(const int &source) {
   switch(source) {
   case ConfigEnumOption1 : return ConfigEnumOption1;
   case ConfigEnumOption2 : return ConfigEnumOption2;
   default return ConfigEnumInvalid;
}

Теперь присвоение int для перечисления становится:

ConfigEnum option = enum_cast<ConfigEnum>(XmlNode.iAttribute("option"));

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

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

Проблема связана с тем фактом, что было бы желательно использовать эту конструкцию "enum_cast" через кодовую базу всякий раз, когда для enum назначается in. После всего этого можно выполнить простую операцию поиска и замены. Конечно, я не хочу определять эти специализации для всех и каждого перечисления вокруг, но только для тех, которые нуждаются в проверке диапазона в данный момент. Я бы предпочел добавить специализации шаблонов для типов перечислений, когда возникнет такая необходимость, и использовать оператор присваивания, когда специализация не определена.

Таким образом:

InternalEnum internal = enum_cast<InternalEnum>(internal_integer);

будет эффективно вызывать internal = internal_integer. Я полагаю, что мне нужно сказать компилятору использовать определенную реализацию «по умолчанию» для всех перечисляемых типов, которые не имеют специализации.

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

template<typename E>
E enum_cast(const int &source) {
  E copy = source;
  return copy;
};

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

Есть мысли?

Заранее спасибо Arne

Ответы [ 3 ]

5 голосов
/ 20 августа 2009

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

#include "enum_cast.h"
enum Foo { Foo_A, Foo_B, Foo_C };
template<> Foo enum_cast<Foo>(int source);

и в соответствующем .cpp вы даете определение.

1 голос
/ 20 августа 2009

Не могли бы вы использовать класс черт для описания каждого перечисления:

const int LARGE = 65536;

template<typename>
struct EnumTrait
{
    enum {LOW = -LARGE};
    enum {HIGH = LARGE};
};

template<typename ENUM>
static ENUM enum_cast (int i)
{
    if (i < EnumTrait<ENUM>::LOW || i > EnumTrait<ENUM>::HIGH)
        throw std::runtime_error ("Out of bounds");
    return static_cast<ENUM> (i);
}

enum Colour {RED = 0, GREEN, BLUE};

template<>
struct EnumTrait<Colour>
{
    enum {LOW = RED};
    enum {HIGH = BLUE};
};

enum Location {HERE = 0, THERE, NOWHERE};
// No EnumTraits class.

int main (int argc, char* argv[])
{
    int i = 2;

    Colour c = enum_cast<Colour> (i);
    std::cout << "c=" << c << std::endl;

    Location l = enum_cast<Location> (i);
    std::cout << "l=" << l << std::endl;

    return 0;
}

Обычно определение enum сопровождается специализацией EnumTraits. Для любых перечислений вне вашего контроля проверка границ использует только черты по умолчанию.

0 голосов
/ 20 августа 2009

Это

#include <iostream>

enum e1 { a, b, c, e1_invalid };
enum e2 { x, y, z, e2_invalid };

template<typename E>
E enum_cast(int source)
{
    std::cout << "default implementation\n";
    return static_cast<E>(source);
}

template<>
e2 enum_cast<e2>(int source)
{
    std::cout << "specialization\n";
    switch(source) {
        case x: return x;
        case y: return y;
        case z: return z;
    }
    return e2_invalid;
}

int main(int /*argc*/, char* /*argv*/[])
{
    std::cout << static_cast<int>(enum_cast<e1>(1)) << '\n';
    std::cout << static_cast<int>(enum_cast<e2>(1)) << '\n';
    return 1;
}

Работает на моей машине (ТМ). Это печатает

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