【问题标题】:Associate each enum member to a class and get generic info from that class将每个枚举成员关联到一个类并从该类获取通用信息
【发布时间】:2021-05-20 20:50:18
【问题描述】:

假设我在一个枚举中有很多音频效果:

enum AudioEffect {
    CHORUS,
    FILTER,
    GAIN,
    OSCILATOR,
    PHASER
};

对于每一个,都有一个类指定如何处理音频字节:

class ChorusProcessor  : public ProcessorBase
{
public:
    ChorusProcessor()
    {
    }
}

class FilterProcessor  : public ProcessorBase
{
public:
    FilterProcessor()
    {
    }
}

每个处理器类都有可以更改的参数,这些是音频控制器(静音/取消静音、滑块等)。

在不创建处理器实例的情况下,是否可以遍历枚举,并为每个枚举获取可控值列表及其最小值和最大值?

我认为 C++ 足够聪明,可以让我们做这些事情。我会开始考虑如何将ChorusChorusProcessorFilterFilterProcessor 等关联起来。我该怎么做?

然后,我会让所有处理器类派生自 AudioEffectDescription 之类的类,该类有一些描述每个参数的 constexprs。

【问题讨论】:

  • 相当粗糙,但可能类似于 this?

标签: c++ enums


【解决方案1】:

你可以通过一些技巧来做到这一点,但我不确定它到底有多大用处。 您将不得不面对的主要问题是,通常您无法轻松遍历enum 的所有值。由于普通enum 的基础数据类型是整数开头,您可以使用a simple loop for a plain enum,但这对于手动设置值的任何enum 都失败,例如

enum class AudioEffect {
  Chorus = 3,
  Filter = 6,
  Gain = 5,
  Oscillator = 2,
  Phaser = 1
};

通常,您必须创建一个额外的数据结构,如 std::tuplestd::vector<AudioEffect>,添加所有可能的值并使用可变参数模板之类的东西循环(只要您使用模板参数,就会赢得正常循环已经不够用了)。此外,在 C++ 中,没有 virtual static 函数之类的东西


专门的模板类

我能想到的最好的是:

  • 声明一个类template <AudioEffect E> class Processor 并为所有可能的模板参数专门化它。我添加了两个static constexpr 函数来获取minmax。这些都必须在所有专业中声明。我使用了两个函数minmax,它们返回一个最小值和一个最大值,但你可以返回一个包含更多信息的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::tuplesolution 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。) 有了它,您可以完全摆脱BeginEnd,方法是0 循环到检测到的enum 值数。这有点复杂,所以我将留下代码here for you to try。有了它,您可以输出所有类,而无需明确指定所有 enum 值:

std::cout << print<AudioEffect>() << std::endl;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-09-04
    • 1970-01-01
    • 2022-01-23
    • 1970-01-01
    • 2022-12-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多