【发布时间】:2021-09-06 09:25:36
【问题描述】:
我正在寻找一种方法来存储一个小的多维数据集,这些数据在编译时是已知的并且永远不会改变。此结构的目的是充当存储在单个命名空间中的全局常量,但可以全局访问而无需实例化对象。
如果我们只需要一个级别的数据,有很多方法可以做到这一点。您可以将 enum 或 class 或 struct 与静态/常量变量一起使用:
class MidiEventTypes{
public:
static const char NOTE_OFF = 8;
static const char NOTE_ON = 9;
static const char KEY_AFTERTOUCH = 10;
static const char CONTROL_CHANGE = 11;
static const char PROGRAM_CHANGE = 12;
static const char CHANNEL_AFTERTOUCH = 13;
static const char PITCH_WHEEL_CHANGE = 14;
};
通过使用这个类和它的成员,我们可以轻松地比较程序中任何地方的数值变量:
char nTestValue = 8;
if(nTestValue == MidiEventTypes::NOTE_OFF){} // do something...
但是,如果我们想要存储的不仅仅是名称和值对怎么办?如果我们还想用 each 常量存储一些 extra 数据怎么办?在上面的示例中,假设我们还想存储每种事件类型必须读取的字节数。
下面是一些伪代码用法:
char nTestValue = 8;
if(nTestValue == MidiEventTypes::NOTE_OFF){
std::cout << "We now need to read " << MidiEventTypes::NOTE_OFF::NUM_BYTES << " more bytes...." << std::endl;
}
我们也应该能够做这样的事情:
char nTestValue = 8;
// Get the number of read bytes required for a MIDI event with a type equal to the value of nTestValue.
char nBytesNeeded = MidiEventTypes::[nTestValue]::NUM_BYTES;
或者:
char nTestValue = 8;
char nBytesNeeded = MidiEventTypes::GetRequiredBytesByEventType(nTestValue);
和:
char nBytesNeeded = MidiEventTypes::GetRequiredBytesByEventType(NOTE_OFF);
这个问题不是关于如何让实例化 类做到这一点。我已经可以做到了。问题是关于如何存储和访问与常量相关/附加的“额外”常量(不变)数据。 (运行时不需要这个结构!)或者如何创建一个多维常量。看起来这可以用静态类来完成,但我尝试了下面代码的几种变体,每次编译器发现有不同的地方要抱怨:
static class MidiEventTypes{
public:
static const char NOTE_OFF = 8;
static const char NOTE_ON = 9;
static const char KEY_AFTERTOUCH = 10; // Contains Key Data
static const char CONTROL_CHANGE = 11; // Also: Channel Mode Messages, when special controller ID is used.
static const char PROGRAM_CHANGE = 12;
static const char CHANNEL_AFTERTOUCH = 13;
static const char PITCH_WHEEL_CHANGE = 14;
// Store the number of bytes required to be read for each event type.
static std::unordered_map<char, char> BytesRequired = {
{MidiEventTypes::NOTE_OFF,2},
{MidiEventTypes::NOTE_ON,2},
{MidiEventTypes::KEY_AFTERTOUCH,2},
{MidiEventTypes::CONTROL_CHANGE,2},
{MidiEventTypes::PROGRAM_CHANGE,1},
{MidiEventTypes::CHANNEL_AFTERTOUCH,1},
{MidiEventTypes::PITCH_WHEEL_CHANGE,2},
};
static char GetBytesRequired(char Type){
return MidiEventTypes::BytesRequired.at(Type);
}
};
此特定示例不起作用,因为它不允许我创建 static unordered_map。如果我不制作unordered_mapstatic,那么它会编译但GetBytesRequired() 无法找到地图。如果我将GetBytesRequired() 设为非静态,它可以找到地图,但是如果没有MidiEventTypes 的实例,我就无法调用它,而且我不想要它的实例。
同样,这个问题不是关于如何修复编译错误,而是关于存储不只是键/值对的静态/常量数据的适当结构和设计模式。
这些是目标:
-
数据和大小在编译时是已知的,永远不会改变。
-
使用每个数据集的人类可读密钥访问一小组数据。关键应该 映射到特定的非线性整数。
-
每个数据集都包含相同的成员数据集。 IE。每个
MidiEventType都有一个NumBytes属性。 -
可以使用命名键或函数访问子项。
-
使用键,(或表示键值的变量),我们应该 能够读取与键的常量项关联的额外数据 指向,对额外数据使用另一个命名键。
-
我们不需要实例化一个类来读取这些数据,因为 没有任何变化,并且不应有超过一份 数据集。
-
事实上,除了包含指令之外,不需要任何东西来访问数据,因为它应该表现得像一个常量。
-
我们在运行时不需要这个对象。目标是通过使用命名标签结构存储数据组,而不是到处使用(不明确的)整数文字,使代码更有条理,更易于阅读。
-
它是一个常量,您可以深入了解...例如 JSON。
-
理想情况下,不应该要求强制转换来使用常量的值。
-
我们应该避免重复数据并且可能不同步的冗余列表。例如,一旦我们定义了
NOTE_ON = 9,文字9就不应出现在其他任何地方。应改为使用标签NOTE_ON,以便只能在一处更改值。 -
这是一个笼统的问题,MIDI只是作为一个例子。
-
常量应该可以有多个属性。
存储在编译时已知的小型、固定大小、分层(多维)静态数据集的最佳方法是什么?使用与常量相同的用例?
【问题讨论】:
-
这主要是基于意见。不过,我会使用
enum MidiEventType,它只是作为const std::unordered_map<MidiEventType,MidiEventData>的键,而不是为数据保留单独的字段,而是全部在MidiEventData -
通过一些修改为我工作:ideone.com/Evm7Dg 除非您真的要抓取几个字节,否则我会将字节值设为 int。
-
只有一个维度,有很多属性,没有很多维度。
-
我认为典型的是一个普通数组。
-
您说在运行时不需要此结构,但显示您正在从非
constexpr键查找属性的示例代码。您是否需要您绘制的运行时查找?还是您实际上总是使用文字常量键(NOTE_ON等)?
标签: c++ containers