我有以下情况:有一个结构,其中包含大量不同类型的数据。还有另一个带有整数成员的结构,我将ID对应于我想要的第一个结构的数据。还有第三个结构应该携带数据。例如:
struct MyData
{
char data1;
float data2;
short data3;
//...
};
struct MyRequest
{
int desiredDataId;
};
struct DataValue
{
long data;
};
在系统中,我使用MyRequest发送消息给它的一部分,告诉“我想要数据X”。然后系统应该查看所请求的Id对应的哪个对应缓冲区(MyData成员);然后创建一个DataValue并将所需的数据(已转换)放到data
成员。
关键点:最明显的方法是做系统根据MyRequest的Id确定哪个MyData成员被请求的部分是一个switch-case:
switch(MyRequest.desiredDataId)
{
case 0: //Id correspondent to data1
DataValue.data = (long) MyData.data1;
break;
case 1: //Id correspondent to data2
DataValue.data = (long) MyData.data2;
break;
case 3: //Id correspondent to data3
DataValue.data = (long) MyData.data1;
break;
//...
}
问题:当MyData内的成员数量很少时,这很好;但我需要一个非常大的MyData的解决方案 - 现在,有一个像370个成员的东西,什么会导致一个巨大的开关案例 - 370个案例!
问题:是否有其他方法可以使用较少的编码进行此类映射?
目前,我将创建一个void*
数组,将指针映射到MyData(在我的情况下全局声明)。因此,不是巨大的开关情况,我必须首先设置一个巨大的数组,然后调用
DataValue.data = *(long)pointerArray[MyRequest.data]
问题是,尽管避免使用巨大的交换机箱,现在我必须面对一个巨大的阵列初始化,所以不是“主解决方案”。
还有另一种可行的选择吗?
注意:如果我写错了C语法,请原谅我;我急着写这个问题:)
答案 0 :(得分:1)
你的问题似乎是一个经典的情况,使用像哈希表这样的键/值存储(在c中有例如ghash和uthash),其中MyRequest.data(又名MyRequest。 desiredDataId)将是键,DataValue.data将是值。使用void指针的解决方案几乎是最简单的实现:因为所有键都是整数,所以hash函数是identity函数。
我不确定你为什么要寻找替代方案,但如果它是速度而且你确实找到了比哈希表更快的东西,我相信世界会想知道。我认为你的void *解决方案的速度和它一样快(我希望你实际上并没有使用char,int和float,但那些只是示例,并且长转换是类似序列化例程的抽象形式 - 如果它不是一个长数组,转换为赋值,并保存间接)。
但是,正如我猜测的那样,您不希望在实现数据结构和维护数据时遇到问题,您应该能够使用哈希表获得相对快速的解决方案。
答案 1 :(得分:0)
假设有以下要求:
struct MyData
必须包含具有不同类型的所有数据字段,具体针对每个字段。MyRequest::desiredDataId
标识连续范围内的数据字段,或至少具有可填充虚拟字段的非常小的间隙。struct DataValue
,并将字段值转换为long
中的DataValue::data
。我建议以下实施:
class DataField {
public:
virtual ~DataField() {}
virtual long toLong() const = 0;
};
// Wrapper for T that implements toLong()
template <typename T>
class DataFieldImpl : public DataField {
public:
DataFieldImpl() = default;
DataFieldImpl(T v) : value(v) {} // implicit construction from T
DataFieldImpl& operator=(T v) { // direct assignment from T
value = v;
return *this;
}
operator T() const {return value;} // implicit conversion to T
const T& value() const {return value;} // const accessor
T& value() {return value;} // mutable accessor
virtual long toLong() const {
return static_cast<long>(value);
}
private:
T value;
};
struct MyData {
static const size_t N = 370;
DataField* data[N];
// Implement constructor and destructor to create and destroy the fields.
} myData;
void doRequest(const MyRequest& request, DataValue* response) {
if (request.desiredDataId < MyData::N) {
response->data = myData.data[request.desiredDataId].toLong();
}
}
关于“热门”void*
解决方案:您无法将void
转换为long
。这将需要有关每个字段的元数据信息,指示每个字段的类型以及如何将每个void*
转换为long
,如简单的operator*
,然后转换为long
willn'工作。您可以设计这些元数据,这将是一个有效且高效的解决方案(也是您在C中的唯一选择)。虚拟方法和模板实际上只是使用C ++语言功能的那些元数据的实现。虚函数调用的开销(至少在理论上)相当于在类似C的解决方案中获取元数据,相当于在big switch语句中进行分支(假设switch子句的编译器优化正确)。