使用自定义类型实现类似枚举的功能

Implementing enum like functionality with custom types

我正在使用 Teensy 开发 USB MIDI 控制器。控制器是一排7个按钮,每个按钮是一个进行度数,7个按钮组成一个和弦进行。按下时,设备会发送一个 MIDI 音符 on/off 消息来演奏和弦。

在我的代码中,我将 intervals 存储在 enum:

/*
 * Intervals
 */
 typedef enum {
   ROOT = 0,
   UNISON = 0,
   DIMINISHED_SECOND = 0,
   MINOR_SECOND = 1,
   AUGMENTED_UNISON = 1,
   HALFSTEP = 1,
   MAJOR_SECOND = 2,
   DIMINISHED_THIRD = 2,
   WHOLESTEP = 2,
   MINOR_THIRD = 3,
   AUGMENTED_SECOND = 3,
   MAJOR_THIRD = 4,
   DIMINISHED_FOURTH = 4,
   PERFECT_FOURTH = 5,
   AUGMENTED_THIRD = 5,
   DIMINISHED_FIFTH = 6,
   AUGMENTED_FOURTH = 6,
   PERFECT_FIFTH = 7,
   DIMINISHED_SIXTH = 7,
   MINOR_SIXTH = 8,
   AUGMENTED_FIFTH = 8,
   MAJOR_SIXTH = 9,
   DIMINISHED_SEVENTH = 9,
   MINOR_SEVENTH = 10,
   AUGMENTED_SIXTH = 10,
   MAJOR_SEVENTH = 11,
   DIMINISHED_OCTAVE = 11,
   PERFECT_OCTAVE = 12,
   AUGMENTED_SEVENTH = 12,
   DIMISHED_NINTH = 12,
   MINOR_NINTH = 13,
   AUGMENTED_OCTAVE = 13,
   MAJOR_NINTH = 14,
   DIMINISHED_TENTH = 14,
   MINOR_TENTH = 15,
   AUGMENTED_NINTH = 15,
   MAJOR_TENTH = 16,
   DIMINISHED_ELEVENTH = 16,
   PERFECT_ELEVENTH = 17,
   AUGMENTED_TENTH = 17,
   DIMINISHED_TWELFTH = 18,
   AUGMENTED_ELEVENTH = 18,
   PERFECT_TWELFTH = 19,
   DIMINISHED_THIRTEENTH = 19,
   MINOR_THIRTEENTH = 20,
   AUGMENTED_TWELFTH = 20,
   MAJOR_THIRTEENTH = 21,
   DIMINISHED_FOURTEENTH = 21,
   MINOR_FOURTEENTH = 22,
   AUGMENTED_THIRTEENTH = 22,
   MAJOR_FOURTEENTH = 23,
   DIMINISHED_FIFTEENTH = 23,
   PERFECT_FIFTEENTH = 24,
   AUGMENTED_FOURTEENTH = 24,
   AUGMENTED_FIFTEENTH = 25
 } INTERVAL;

我还有一个chords的数组,像这样:

struct Chord {
   String name;
   int tones[7];
 };

Chord chords[6] = {
  { "maj", {
    INTERVAL::UNISON,
    INTERVAL::MAJOR_THIRD,
    INTERVAL::PERFECT_FIFTH }
  },
  { "min", {
    INTERVAL::UNISON,
    INTERVAL::MINOR_THIRD,
    INTERVAL::PERFECT_FIFTH }
  },
  { "maj7", {
    INTERVAL::UNISON,
    INTERVAL::MAJOR_THIRD,
    INTERVAL::PERFECT_FIFTH,
    INTERVAL::MAJOR_SEVENTH }
  },
  { "min7", {
    INTERVAL::UNISON,
    INTERVAL::MINOR_THIRD,
    INTERVAL::PERFECT_FIFTH,
    INTERVAL::MINOR_SEVENTH }
  },
  { "maj9", {
    INTERVAL::UNISON,
    INTERVAL::MAJOR_THIRD,
    INTERVAL::PERFECT_FIFTH,
    INTERVAL::MAJOR_SEVENTH,
    INTERVAL::MAJOR_NINTH }
  },
  { "min9", {
    INTERVAL::UNISON,
    INTERVAL::MINOR_THIRD,
    INTERVAL::PERFECT_FIFTH,
    INTERVAL::MINOR_SEVENTH,
    INTERVAL::MINOR_NINTH }
  }
};

我想以类似于 enum 音程的方式访问和弦,这样我就可以做这样的事情(伪代码):

void playChord(Chord chord, int velocity, int channel) {
    int i;
    for(i=0; i<chord.length; i++) {
        usbMIDI.sendNoteOn(chord[i], velocity, channel);
    }
}

playChord(Chord::MAJOR, 127, 1);

我知道不可能有自定义类型的枚举,但有什么办法可以接近这个吗?我考虑过使用哈希表,但我必须从头开始实现它,如果我能帮上忙,我不希望这样做。

枚举的要点是您创建了一个新类型,它只能采用一组固定值。你的区间使用枚举是合适的,因为实际使用的区间只有这么多,而且在这里创建一个新类型比使用整数常量更方便。

故事因你的和弦而不同。您已经有了一个和弦类型,因此将它们包装在另一个枚举类型中没有帮助。此外,和弦的数量远没有那么有限。我手头的和弦图显示了 22 种形状,但不包括转位。你的和弦结构比用枚举人为限制和弦更合适。

除了枚举之外,C 还有另外两种创建“常量”的机制:preprocessor-defines 和静态变量。

使用预处理器指令,我们可以定义和弦文字。 IIRC 结构文字是 C99 的东西,以前只能有初始化程序文字。

#define CHORD_MAJOR ((Chord){"maj", {ROOT, MAJOR_THIRD, PERFECT_FIFTH}})

使用静态变量,您可以在 header 中声明一个 object:

static const Chord chord_major = {"maj", {ROOT, MAJOR_THIRD, PERFECT_FIFTH}};

请注意,C 没有像 :: 这样的名称空间运算符。相反,您必须自己为任何可能冲突的标识符添加前缀。 C++ 确实有命名空间,但这并不影响本答案中提出的要点。