Система конвертации вариантов - PullRequest
4 голосов
/ 05 мая 2011

Я написал вариантный класс, который будет использоваться в качестве основного типа в динамическом языке, который в конечном итоге допускает 256 различных типов значений (заголовок является байтом без знака, фактически используется только 20). Теперь я хочу реализовать приведение / преобразование между типами.

Моей первоначальной мыслью была таблица поиска, но из-за большого количества памяти, которое потребовалось бы, сделать это нецелесообразным.

Какие есть альтернативы? Сейчас я рассматриваю еще три метода исследования и предложения других людей:

  1. Сгруппируйте типы в большие подмножества, такие как числовые или коллекционные или другие.
  2. Создайте интерфейс преобразования, который имеет методы CanCast (from, to) и Cast (Variant), и разрешает добавление классов, реализующих этот интерфейс, в список, который затем можно проверить, чтобы проверить, может ли какой-либо из классов преобразования выполнить актерский состав.
  3. Аналогично (1), но выполняется несколько основных типов, и приведение является двухэтапным процессом от исходного типа к основному типу, а затем снова к окончательному типу.

Какая система будет лучшей?

Редактировать: Я добавил награду, так как я все еще не уверен в лучшей системе, текущий ответ очень хороший, и определенно получил мой +1, но должны быть люди, которые сделали это и могут сказать, что лучше метод.

Ответы [ 3 ]

1 голос
/ 13 мая 2011

Моя система очень «тяжелая» (много кода), но очень быстрая и очень многофункциональная (кроссплатформенная C ++). Я не уверен, насколько далеко вы хотели бы пойти с вашим дизайном, но вот большая часть того, что я сделал:

DatumState - Класс, содержащий «enum» для «type», плюс нативное значение, которое является «объединением» среди всех примитивных типов, включая void*. Этот класс не связан со всеми типами и может использоваться для любых собственных / примитивных типов и типа «ссылка на» void*. Так как «enum» также имеет контекст «VALUE_OF» и «REF_TO», этот класс может быть представлен как «полностью содержащий» a float (или некоторый примитивный тип), или как «ссылка, но не не владеющий "float (или каким-то примитивным типом). (У меня на самом деле есть контексты "VALUE_OF", "REF_TO" и "PTR_TO", поэтому я могу логически хранить значение , эталон, который не может быть нулевым или указатель, который может быть нулевым или нет , и который, как я знаю, мне нужно удалить или нет .)

Datum - Класс, полностью содержащий DatumState, но расширяющий интерфейс для поддержки различных «известных» типов (таких как MyDate, MyColor , MyFileName и т. Д.) Эти общеизвестные типы на самом деле хранятся в void* внутри элемента DatumState. Однако, поскольку часть enum в DatumState имеет контекст «VALUE_OF» и «REF_TO», она может представлять «pointer-to-MyDate» или «value-of-MyDate».

DatumStateHandle - Вспомогательный класс шаблона, параметризованный (общеизвестным) типом (например, MyDate, MyColor, MyFileName и т. Д.). Этот метод доступа используется Datum для извлечения состояния из известного типа. Реализация по умолчанию работает для большинства классов, но любой класс с определенной семантикой для доступа просто переопределяет свою конкретную параметризацию / реализацию шаблона для одной или нескольких функций-членов в этом классе шаблона.

Macros, helper functions, and some other supporting stuff - Чтобы упростить «добавление» известных типов в мой Datum / Variant, я счел удобным централизовать логику в несколько макросов, предоставить некоторые функции поддержки, такие как перегрузка оператора и установление некоторых других соглашений в моем коде.

В качестве «побочного эффекта» этой реализации я получил массу преимуществ, включая семантику ссылок и значений, параметры «null» для всех типов и поддержку гетерогенных контейнеров для всех типов.

Например, вы можете создать набор целых чисел и проиндексировать их:

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

Я могу хранить наборы элементов (изначально) или наборы Datum s (упаковывая каждый элемент). В любом случае я могу "развернуть" рекурсивно:

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" излишня, но они были необходимы для "развертывания набора".

Я сделал это "Variant" с девятью различными конструкциями, и мой ток является "самым тяжелым" (больше всего кода), но тот, который мне нравится больше всего (почти самый быстрый с довольно маленьким размером объекта) и я осудил остальные восемь проектов для моего использования.

"Минусы" моего дизайна:

  1. Доступ к объектам осуществляется через static_cast<>() от void* (Тип безопасно и довольно быстро, но требуется перенаправление; но, побочным эффектом является то, что дизайн поддерживает хранилище "null".)
  2. Компиляции длиннее из-за известные типы, которые подвергаются через интерфейс Datum (но вы можете использовать DatumState, если вы этого не сделаете хочу API известных типов).

Независимо от вашего дизайна, я бы порекомендовал следующее:

  1. Используйте "enum" или что-то, чтобы сказать Вы " тип ", отдельно от " значение ". (Я знаю, что вы можете сжать их в один "int" или что-то с битовой упаковкой, но это медленный доступ и очень сложно поддерживать как новые типы введен.)

  2. Опирайтесь на шаблоны или что-то для централизации операций, с механизм для конкретного типа (переопределить) обработку (если вы хотите обрабатывать нетривиальные типы).

Название игры «упрощенное обслуживание при добавлении новых типов» (или, по крайней мере, для меня). Как хорошая курсовая работа, очень хорошая идея, если вы переписываете, переписываете, переписываете, чтобы удерживать или увеличивать свою функциональность, так как вы постоянно удаляете код, необходимый для поддержания система (например, минимизируйте усилия, необходимые для адаптации новых типов к существующей инфраструктуре Variant).

Удачи!

1 голос
/ 07 мая 2011

Сделано что-то похожее.

Вы можете добавить еще один байт к заголовку, указывая тип, который он действительно хранит.

Пример на языке программирования в стиле 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
  }

  // ...    
}

Это только предложение, и оно не проверено.

0 голосов
/ 27 мая 2011

Может быть, вы уже сделали расчет, но объем памяти, необходимый для таблицы поиска, не , что много.

Если вам просто нужно проверить совместимость типов, вам нужно (256 * 256) / 2 бита. Это требует 4 Кб памяти.

Если вам также нужен указатель на функцию преобразования, тогда вам нужно (256 * 256) / 2 указателя. Это требует 128 КБ памяти на 32-битной машине и 256 КБ на 64-битной машине. Если вы хотите сделать низкоуровневую компоновку адресов, вы можете уменьшить ее до 64 КБ как на 32-разрядных, так и на 64-разрядных компьютерах.

...