模板函数特化问题

发布于 2024-08-03 05:54:53 字数 1500 浏览 4 评论 0原文

我正在使用模板来实现从 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_cast”构造每当将 in 分配给枚举时。毕竟这可以通过简单的搜索和替换操作来强制执行。当然,我不想为所有枚举定义这些专业化,而只想为那些目前需要范围检查的枚举定义这些专业化。我更愿意在需要时为枚举类型添加模板专业化,并在未定义专业化时使用赋值运算符。

因此:

InternalEnum internal = enum_cast<InternalEnum>(internal_integer);

将有效地调用内部=内部_整数。我认为我需要告诉编译器对所有没有专门化的枚举类型使用某种“默认”实现。

我的第一个赌注是为原始模板函数提供这样的实现:

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

不幸的是,现在总是调用它,而不是在项目目录树更深处的 .cpp 文件中给出的专业化。

有什么想法吗?

提前致谢 阿内

I am using templates to implement a range checked conversion from int to enum. It looks like this:

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

The template function placed more or less in the root-directoy of the project. When defining a new enum that is foreseen to be assigned values from a config file like this:

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

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

I define a template specialization for this particular enum type in a .cpp-file for the module this enum is used in.

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

Now the assignment of an int to the enum becomes:

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

which makes sure that the enum is allways in valid range. Note that I have not allways control over these enums so this seems to a be reasonable and easily configureable solution.

Anyway, this works all very well (although I am not shure all code given here is correct because I just recall it from memory right now)

The problem stems from the fact that it might be desireable to use this "enum_cast" construct thorughout the code base whenever an in is assigned to a enum. After all this can be enforced by a simple search-and-replace operation. Of course I do not want to define these specializations for all and every enum around but only for those that need the range check at the moment. I would prefer to add template specializations for the enum types when the need arises and use the assignment operator when no specialization is defined.

Thus:

InternalEnum internal = enum_cast<InternalEnum>(internal_integer);

would effecively call internal = internal_integer. I figure that I need to tell the compiler to use a certain "default" implementation for all enum types that do not have a specialization.

My first bet was giving the original template function an implementation like this:

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

Unfortunately now this is allways called instead of the specialiazations given in the .cpp-files deeper into the project directory-tree.

Any thoughts?

Thanks in advance
Arne

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

ヤ经典坏疍 2024-08-10 05:54:53

显式专业化必须在使用它们的任何地方都可见。由于它们是定义,因此它们不能在每个编译单元中重复。因此,在定义枚举的头文件中,您要检查您所说的内容

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

,并在相应的 .cpp 中给出定义。

The explicit specializations have to be visible everywhere they are used. And as they are definitions, they can't be repeated in every compilation unit. So in the header file where you define an enum you want to check you 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.

前事休说 2024-08-10 05:54:53

您不能使用特征类来描述每个枚举吗:

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

通常枚举定义伴随着 EnumTraits 专业化。对于您无法控制的任何枚举,边界检查仅使用默认特征。

Couldn't you use a traits 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;
}

Normally the enum definition is accompanied by an EnumTraits specialisation. For any enums outside your control then the bounds checking just uses the default traits.

策马西风 2024-08-10 05:54:53

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

适用于我的机器(TM)。它打印

default implementation
1
specialization
1

This

#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). It prints

default implementation
1
specialization
1
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文