C является языком низкого уровня, почти переносимым ассемблером, поэтому его структуры данных и языковые конструкции близки к металлу (структуры данных не имеют скрытых затрат - кроме дополнения, выравнивания и ограничений размера, наложенных аппаратным обеспечением и ABI ). Так что C действительно не имеет встроенной динамической типизации. Но если вам это нужно, вы можете принять соглашение, согласно которому все ваши значения являются агрегатами, начиная с некоторой информации о типе (например, с некоторого enum
...); Использование union
-s и (для массива-подобных вещей) гибкого элемента массива в struct
содержащий также размер массива.
(при программировании на C вы несете ответственность за определение, документирование и выполнение полезных соглашений - в частности, предварительных и постусловий и инвариантов; также динамическое выделение памяти в C требует free
объяснения соглашений о том, кто должен иметь malloc
зону с кучей памяти)
Таким образом, для представления значений, которые представляют собой целые числа в виде блоков, или строки, или какой-либо символ , подобный Схеме , или векторы значений, вы концептуально будете использовать теговое объединение (реализованное как объединение указателей) - всегда начиная с типа type - например:
enum value_kind_en {V_NONE, V_INT, V_STRING, V_SYMBOL, V_VECTOR};
union value_en { // this union takes a word in memory
const void* vptr; // generic pointer, e.g. to free it
enum value_kind_en* vkind; // the value of *vkind decides which member to use
struct intvalue_st* vint;
struct strvalue_st* vstr;
struct symbvalue_st* vsymb;
struct vectvalue_st* vvect;
};
typedef union value_en value_t;
#define NULL_VALUE ((value_t){NULL})
struct intvalue_st {
enum value_kind_en kind; // always V_INT for intvalue_st
int num;
};
struct strvalue_st {
enum value_kind_en kind; // always V_STRING for strvalue_st
const char*str;
};
struct symbvalue_st {
enum value_kind_en kind; // V_SYMBOL
struct strvalue_st* symbname;
value_t symbvalue;
};
struct vectvalue_st {
enum value_kind_en kind; // V_VECTOR;
unsigned veclength;
value_t veccomp[]; // flexible array of veclength components.
};
Чтобы получить динамический тип некоторого значения
enum value_kind_en value_type(value_t v) {
if (v.vptr != NULL) return *(v.vkind);
else return V_NONE;
}
Вот «динамическое приведение» к векторам:
struct vectvalue_st* dyncast_vector (value_t v) {
if (value_type(v) == V_VECTOR) return v->vvect;
else return NULL;
}
и «безопасный метод доступа» внутри векторов:
value_t vector_nth(value_t v, unsigned rk) {
struct vectvalue_st* vecp = dyncast_vector(v);
if (vecp && rk < vecp->veclength) return vecp->veccomp[rk];
else return NULL_VALUE;
}
Обычно вы определяете большинство коротких функций, описанных выше, как static inline
в некотором заголовочном файле.
Кстати, если вы можете использовать сборщик мусора от Boehm, вы сможете довольно легко кодировать его в каком-то высокоуровневом (но небезопасном) стиле, и таким образом выполняется несколько интерпретаторов Scheme. Вариативный векторный конструктор может быть
value_t make_vector(unsigned size, ... /*value_t arguments*/) {
struct vectvalue_st* vec = GC_MALLOC(sizeof(*vec)+size*sizeof(value));
vec->kind = V_VECTOR;
va_args args;
va_start (args, size);
for (unsigned ix=0; ix<size; ix++)
vec->veccomp[ix] = va_arg(args,value_t);
va_end (args);
return (value_t){vec};
}
и если у вас есть три переменные
value_t v1 = somevalue(), v2 = otherval(), v3 = NULL_VALUE;
вы можете построить вектор из них, используя make_vector(3,v1,v2,v3)
Если вы не хотите использовать сборщик мусора Boehm (или проектировать свой собственный), вы должны быть очень осторожны с определением деструкторов и документированием того, кто, как и когда память должна быть free
-d; посмотрите этот пример. Таким образом, вы могли бы использовать malloc
(но затем проверить его на отказ) вместо GC_MALLOC
вышеупомянутого, но вам нужно тщательно определить и использовать некоторую функцию деструктораvoid destroy_value(value_t)
Сила C в том, чтобы быть достаточно низкоуровневым, чтобы сделать возможным выполнение кода, подобного описанному выше, и определить ваши собственные соглашения (особенно для вашего программного обеспечения).