你可以通过一些技巧来做到这一点,但我不确定它到底有多大用处。
您将不得不面对的主要问题是,通常您无法轻松遍历enum 的所有值。由于普通enum 的基础数据类型是整数开头,您可以使用a simple loop for a plain enum,但这对于手动设置值的任何enum 都失败,例如
enum class AudioEffect {
Chorus = 3,
Filter = 6,
Gain = 5,
Oscillator = 2,
Phaser = 1
};
通常,您必须创建一个额外的数据结构,如 std::tuple 或 std::vector<AudioEffect>,添加所有可能的值并使用可变参数模板之类的东西循环(只要您使用模板参数,就会赢得正常循环已经不够用了)。此外,在 C++ 中,没有 virtual static 函数之类的东西。
专门的模板类
我能想到的最好的是:
-
声明一个类template <AudioEffect E> class Processor 并为所有可能的模板参数专门化它。我添加了两个static constexpr 函数来获取min 和max。这些都必须在所有专业中声明。我使用了两个函数min 和max,它们返回一个最小值和一个最大值,但你可以返回一个包含更多信息的std::string_view(或者在C++20 中你将能够返回一个constexpr std::string)关于不同的限制。
template <AudioEffect E>
class Processor: ProcessorBase {
};
template <>
class Processor<AudioEffect::Chorus>: ProcessorBase {
public:
// Or a single function for printing all the settings of this class
static constexpr int min() {
return 1;
}
static constexpr int max() {
return 99;
}
};
-
然后我可以使用 可变参数模板函数和 C++17 折叠表达式打印所有限制(假设每个实例template class Preprocessor 有一个 static constexpr 函数 min 和另一个 max
template <AudioEffect... E>
std::string print() {
std::stringstream ss;
((ss << E << "\t min: " << Processor<E>::min() << ",\t max: " << Processor<E>::max() << std::endl), ...);
return ss.str();
}
-
这个打印函数可以在主列表中调用,列出所有可能的参数
std::cout << print<AudioEffect::Chorus,AudioEffect::Filter,AudioEffect::Gain,AudioEffect::Oscillator,AudioEffect::Phaser>() << std::endl;
Try it here!
地图
另一种类似的方法是将enum 映射到classes(不再必须是专门的模板)与traits 类似here by
-
定义你的类
class ChorusProcessor: ProcessorBase {
public:
static constexpr int min() {
return 1;
}
static constexpr int max() {
return 99;
}
};
-
然后特化一个template struct Map,它映射在enum 值和classes 之间
template<AudioEffect> struct Map;
template<> struct Map<AudioEffect::Chorus> {
using type = ChorusProcessor;
};
-
并再次使用可变参数模板引入打印功能,例如
template <AudioEffect... E>
std::string print() {
std::stringstream ss;
((ss << E << "\t min: " << Map<E>::type::min() << ",\t max: " << Map<E>::type::max() << std::endl), ...);
return ss.str();
}
Try it here!
这与使用std::tuple 的solution proposed in the comments 非常相似
using Processors = std::tuple<
ChorusProcessor,
FilterProcessor,
GainProcessor,
OscilatorProcessor,
PhaserProcessor
>;
std::tuple_element_t<AudioEffect::Filter, Processors>::value
简短但肮脏
如果您使用 enum 而不明确指定值,您也可以使用仅提供 enum (C++17) 的第一个和最后一个值的肮脏解决方案
template <AudioEffect Begin, AudioEffect End>
std::string print() {
std::stringstream ss;
ss << Begin << "\t min: " << Processor<Begin>::min() << ",\t max: " << Processor<Begin>::max() << std::endl;
if constexpr (Begin != End) {
ss << print<static_cast<AudioEffect>(static_cast<int>(Begin)+1), End>();
}
return ss.str();
}
这样你可以简单地用enum的第一个和最后一个值来调用它
std::cout << print<AudioEffect::Chorus,AudioEffect::Phaser>() << std::endl;
Try it here!
枚举反射
最后,对于 GCC、Clang 和 MSVC 编译器,有一种hacky 方式可以使用 __PRETTY_FUNCTION__ 标识符 获取一些枚举属性,该标识符描述为 here。 (原代码有一些错别字,不是constexpr,所以最好试试here。)
有了它,您可以完全摆脱Begin 和End,方法是从0 循环到检测到的enum 值数。这有点复杂,所以我将留下代码here for you to try。有了它,您可以输出所有类,而无需明确指定所有 enum 值:
std::cout << print<AudioEffect>() << std::endl;