变体转换系统

时间:2011-05-05 15:56:12

标签: compiler-construction casting language-design compiler-theory variant

我编写了一个变体类,它将用作动态语言中的主要类型,最终将允许256种不同类型的值(标头是无符号字节,实际上只使用了20个)。我现在想要在类型之间实现转换/转换。

我最初的想法是查找表,但是需要的内存剪切量使得实现起来不切实际。

有哪些替代方案?现在我正在考虑另外三种来自其他人的研究和建议的方法:

  1. 将类型分组为更大的子集,例如数字或集合或其他。
  2. 创建一个具有CanCast(from,to)和Cast(Variant)方法的转换接口,并允许将实现该接口的类添加到列表中,然后可以检查该列表以查看是否有任何转换类可以执行演员。
  3. 与(1)类似,但是制作了几种主类型,而且从原始类型到主类型再到最终类型,这是一个两步过程。
  4. 什么是最好的系统?

    编辑:我已经添加了赏金,因为我仍然不确定最好的系统,目前的答案是非常好的,并且肯定得到我的+1但是必须有人在那里做了这个并且可以说什么是最好的方法是。

3 个答案:

答案 0 :(得分:1)

完成类似的事情。

您可以在“标题”中添加另一个字节,指示其实际存储的类型。

C风格编程语言中的示例:

typedef
enum VariantInternalType {
  vtUnassigned = 0;
  vtByte = 1;
  vtCharPtr = 2; // <-- "plain c" string
  vtBool = 3;
  // other supported data types
}

// --> real data
typedef
struct VariantHeader {
  void* Reserved; // <-- your data (byte or void*)
  VariantInternalType VariantInternalType;  
}

// --> hides real data
typedef
  byte[sizeof(VariantHeader)] Variant;

// allocates & assign a byte data type to a variant
Variant ByteToVar(byte value)
{
  VariantHeader MyVariantHeader;
  Variant MyVariant;

  MyVariantHeader.VariantInternalType = VariantInternalType.vtByte;
  MyVariantHeader.Reserved = value;  

  memcpy (&MyVariant, &MyVariantHeader, sizeof(Variant));

  return myVariant;
}

// allocates & assign a char array data type to a variant
Variant CharPtrToVar(char* value)
{
  VariantHeader MyVariantHeader;
  Variant MyVariant;

  MyVariantHeader.VariantInternalType = VariantInternalType.vtByte;
  MyVariantHeader.Reserved = strcpy(value);  

  // copy exposed struct type data to hidden array data
  memcpy(&MyVariant, &MyVariantHeader, sizeof(Variant));

  return myVariant;
}

// deallocs memory for any internal data type
void freeVar(Variant &myVariant)
{
  VariantHeader MyVariantHeader;

  // copy exposed struct type data to hidden array data
  memcpy(&MyVariantHeader, &MyVariant, sizeof(VariantHeader));

  switch (MyVariantHeader.VariantInternalType) {
    case vtCharPtr:
      strfree(MyVariantHeader.reserved);
    break;

    // other types

    default:
    break;
  }

  // copy exposed struct type data to hidden array data
  memcpy(&MyVariant, &MyVariantHeader, sizeof(Variant));
}

bool isVariantType(Variant &thisVariant, VariantInternalType thisType)
{
  VariantHeader MyVariantHeader;

  // copy exposed struct type data to hidden array data
  memcpy(&MyVariantHeader, &MyVariant, sizeof(VariantHeader));

  return (MyVariant.VariantInternalType == thisType);
}

// -------

void main()
{
  Variant myVariantStr = CharPtrToVar("Hello World");
  Variant myVariantByte = ByteToVar(42);

  char* myString = null;
  byte  myByte = 0;

  if isVariantType(myVariantStr, vtCharPtr) {
    myString = VarToCharPtr(myVariantStr);
    // print variant string into screen
  }

  // ...    
}

这只是一个建议,未经过测试。

答案 1 :(得分:1)

我的系统非常“繁重”(许多代码),但非常快,而且功能非常丰富(跨平台C ++)。我不确定你想用你的设计走多远,但这是我所做的最重要的部分:

DatumState - 持有“类型”的“枚举”的类加上原生值,它是所有基本类型(包括void*)中的“联​​合”。此类与所有类型解耦,可用于任何本机/基元类型,以及“引用”void*类型。由于“enum”也有“VALUE_OF”和“REF_TO”上下文,因此该类可以表示为“完全包含”float(或某种原始类型),或“引用但不拥有”float(或某种原始类型)。 (我实际上有“VALUE_OF”,“REF_TO”和“PTR_TO”上下文,因此我可以在逻辑上存储引用 - 那个 - 不能为空,或者一个指针 - 可能是空的或者不是,而且我知道我需要 delete-or-not 。)

Datum - 完全包含 DatumState 的类,但扩展了其界面以适应各种“众所周知”类型(如{ {1}},MyDateMyColor等)这些众所周知的类型实际上存储在MyFileName成员内的void*中。但是,由于DatumState的“enum”部分具有“DatumState”和“VALUE_OF”上下文,因此它可以代表“REF_TO”或“pointer-to-MyDate”。

value-of-MyDate - 使用(众所周知的)类型参数化的辅助模板类(如DatumStateHandleMyDateMyColor等。)这是MyFileName用于从众所周知的类型中提取状态的访问器。默认实现适用于大多数类,但具有特定访问语义的任何类仅覆盖此模板类中一个或多个成员函数的特定模板参数化/实现。

Datum - 为了简化“添加”知名类型到我的Macros, helper functions, and some other supporting stuff / Datum,我发现将逻辑集中到少数几个方便宏,提供一些支持函数,如运算符重载,并在我的代码中建立一些其他约定。

作为此实现的“副作用”,我获得了许多好处,包括引用和值语义,所有类型的“null”选项,以及对所有类型的异构容器的支持。

例如,您可以创建一组整数并将它们编入索引:

Variant

类似地,一些数据类型由字符串或枚举索引:

int my_ints[10];
Datum d(my_ints, 10/*count*/);
for(long i = 0; i < d.count(); ++i)
{
  d[i] = i;
}

我可以存储项目集(本机)或集合MyDate my_date = MyDate::GetDateToday(); Datum d(my_date); cout << d["DAY_OF_WEEK"] << endl; cout << d[MyDate::DAY_OF_WEEK] << endl; // alternative s(包装每个项目)。无论哪种情况,我都可以递归“解包”:

Datum

有人可能会说我的“MyDate my_dates[10]; Datum d(my_dates, 10/*count*/); for(long i = 0; i < d.count(); ++i) { cout << d[i][MyDate::DAY_OF_WEEK] << endl; } ”和“REF_TO”语义是矫枉过正的,但它们对于“设置 - 解包”至关重要。

我用九种不同的设计完成了这个“VALUE_OF”的事情,我目前是“最重的”(大多数代码),但我最喜欢的那个(几乎是最快的一个非常小的对象)足迹),我已经弃用了其他八种设计供我使用。

我的设计的“缺点”是:

  1. 通过访问对象 来自Variant的{​​{1}} (类型安全且相当快,但是 间接是必需的;但, 副作用是设计支持 存储“static_cast<>()”。)
  2. 编译因为更长 暴露的众所周知的类型 通过void*界面(但你 如果不这样做,可以使用null 想知道类型的API)。
  3. 无论您的设计如何,我都会推荐以下内容:

    1. 使用“Datum”或其他内容告诉您 你是“ type ”,与之分开 “”。 (我知道你可以压缩 将它们分成一个“DatumState”或其他东西 有点打包,但就是这样 访问速度慢,非常棘手 维持新的类型 引入)。

    2. 倾向于使用模板或某些东西来集中操作 特定类型的机制 (覆盖)处理(假设你想要 处理非平凡类型。)

    3. 游戏名称​​“添加新类型时的简化维护”(或者至少,它适合我)。就像一篇好的学期论文一样,如果你不断地删除所需的代码,那么重写,重写,重写,保持或增加你的功能是一个非常好的主意。维护系统(例如,最大限度地减少使新类型适应现有enum基础架构所需的工作量。)

      祝你好运!

答案 2 :(得分:0)

也许您已经完成了计算,但查找表所需的内存量

如果您只需要检查类型是否兼容,那么您需要(256 * 256)/ 2位。这需要4k的内存。

如果你还需要一个指向转换函数的指针,那么你需要(256 * 256)/ 2指针。这需要32位机器上的128k内存和64位机器上的256k内存。如果你愿意做一些低级地址布局,你可以在32位和64位机器上将其降低到64k。