Моя система очень «тяжелая» (много кода), но очень быстрая и многофункциональная (кроссплатформенный C++). Я не уверен, как далеко вы хотели бы зайти со своим дизайном, но вот основные части того, что я сделал:
DatumState
— класс, содержащий "перечисление" для "типа" и собственное значение, которое представляет собой "объединение" всех типов-примитивов, включая void*
. Этот класс не связан со всеми типами и может использоваться для любых нативных/примитивных типов и типа "ссылка на" void*
. Поскольку "enum
" также имеет контекст "VALUE_OF
" и "REF_TO
", этот класс может быть представлен как "полностью содержащий" 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
" с девятью различными проектами, и мой текущий - "самый тяжелый" (больше всего кода), но тот, который мне нравится больше всего (почти самый быстрый с довольно небольшим размером объекта), и я устарели другие восемь дизайнов для моего использования.
«Минусы» моего дизайна:
- Доступ к объектам осуществляется через
static_cast<>()
из void*
(типобезопасный и довольно быстрый, но требуется косвенное обращение; но побочным эффектом является то, что дизайн поддерживает хранение «null
».)
- Компиляции длиннее из-за общеизвестных типов, которые доступны через интерфейс
Datum
(но вы можете использовать DatumState
, если вам не нужны API общеизвестных типов).
Независимо от вашего дизайна, я бы рекомендовал следующее:
Используйте "enum
" или что-то подобное, чтобы указать "тип" отдельно от "значения". (Я знаю, что вы можете сжать их в один «int
» или что-то еще с битовой упаковкой, но это медленно для доступа и очень сложно поддерживать по мере появления новых типов.)
Опирайтесь на шаблоны или что-то еще для централизации операций с механизмом обработки для конкретного типа (переопределения) (при условии, что вы хотите обрабатывать нетривиальные типы).
Суть игры — упрощение обслуживания при добавлении новых типов (по крайней мере, так было у меня). Как и в случае с хорошей курсовой работой, это очень хорошая идея, если вы переписываете, переписываете, переписываете, чтобы удерживать или увеличивать свою функциональность, поскольку вы постоянно удаляете код, необходимый для поддерживать систему (например, минимизировать усилия, необходимые для адаптации новых типов к существующей Variant
инфраструктуре).
Удачи!
person
charley
schedule
13.05.2011