Template specialization problem

I am using templates to implement the tested range of converting from int to enum. It looks like this:

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

      

A template function located more or less in the root directory of the project. When defining a new enumeration, it is intended to assign values ​​from the configuration file as follows:

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

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

      

I define a template specialization for this particular type of enum in a .cpp file for the module that uses the enum.

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

      

Now assigning int to enum will be:

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

      

which ensures that the enum is always in the valid range. Please note that I don't always have control over these enums, so this seems like a sane and easily customizable solution.

Anyway, this works pretty well (although I'm not sure if all the codes given here are correct, because I'm just recalling it from memory right now)

The problem is that it would be desirable to use this "enum_cast" construct, which stores the code base whenever in is assigned an enumeration. Ultimately, this can be accomplished with a simple find and replace operation. Of course, I don't want to define these specializations for each and every enum, but only for those who need range checking at the moment. I would prefer to add specialized template types for enum types when the need arises and use the assignment operator if no specialization is defined.

Thus:

InternalEnum internal = enum_cast<InternalEnum>(internal_integer);

      

will effectively call internal = internal_integer. I suppose I need to tell the compiler to use a specific default implementation for all enum types that have no specialization.

My first bet was that the original template function ran like this:

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

      

Unfortunately, this is now always called instead of specialized jobs in .cpp files deeper in the project directory tree.

Any thoughts?

Thanks in advance Arne

+2


source to share


3 answers


Explicit specializations should be visible wherever they are used. And since they are definitions, they cannot be repeated in every compilation unit. So, in the header file where you define the enum you want to check, say

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

      



and in the corresponding .cpp you give the definition.

+4


source


It is not possible to use a trait class to describe each enum:

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;
}

      



Typically, an enumeration definition is accompanied by the EnumTraits specialization. For any enums out of your control, then bounds checking only uses the default values.

+1


source


it

#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;
}

      

Works on my machine (TM). He prints

default implementation
1
specialization
1

      

0


source







All Articles