C - мова низького рівня, майже портативний асемблер, тому його структури даних та мовні конструкції близькі до металевих (структури даних не мають прихованих витрат - крім обмежень, накладень , вирівнювання та розмірів, накладених апаратними засобами та ABI ). Тому C справді не має динамічного натискання тексту. Але якщо вам це потрібно, ви можете прийняти умову про те, що всі ваші значення - це сукупності, що починаються з відомостей про тип (наприклад, деякі enum
...); Використання union
-s і (для масиву-подібних речей) гнучкого елемента масиву в struct
якому також розмір масиву.
(при програмуванні на C ви несете відповідальність за визначення, документування та дотримання корисних конвенцій - особливо до- та після умов та інваріантів; також динамічне розподіл пам’яті вимагає free
роз'яснення домовленостей про те, хто повинен обробляти malloc
зону пам’яті)
Таким чином, для представлення значень , які затиснуті цілі числа, або рядки, або яке - то схема -like символу , або вектори значень, ви будете концептуально використовувати позначений союз (реалізований у вигляді об'єднання покажчиків) -Завжди починаючи від типу виду -, наприклад:
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, ви зможете кодувати досить легко в якомусь вищому (але небезпечному) стилі, і кілька інтерпретаторів схем робляться саме так. Варіантний конструктор вектор може бути
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 полягає в тому, щоб бути достатньо низьким, щоб зробити можливим подібний код та визначити власні конвенції (зокрема, до вашого програмного забезпечення).