Ориентация главы

Четыре главы xruntime завершаются. Семейство typed-контейнеров (xtypedarray/xtypedstack/xtypedlist/xtypedtree/xtypedset/xtypeddict) разделяет один контракт значений времени исполнения поверх шести структур данных: заимствованные неизменяемые описания типов, ведомые описаниями семейства операций над значениями, управляющие жизненным циклом значений (копия/перемещение/сравнение/хеш/разрушение — всё через таблицу описаний; факты о типах главы 114, исполненные контейнерами), единые глаголы владения (Set/Add/Push копирует, Take/Pop перемещает, SetTake атомарно переносит при отказе, Clone глубоко копирует), единая атомарность отказа (многошаговые операции идут в рабочем хранилище до фиксаации; отказ не ломает видимого состояния). Инженерные моменты: расширение переиспользует внутренние быстрые пути контейнеров XRT напрямую (базовые реализации не копируются — typed-слой есть «ведомая типами оболочка»; массив int64 работает так же быстро, как массив главы 14); операции над двумя контейнерами требуют равенства указателей описаний (одного ID/имени мало — жизненный ABI и частный контекст невзаимозаменяемы). Восемнадцать глав тома 11 здесь закрываются.

117.1引入

Зачем typed-контейнеры? C-контейнеры глав 14–23 (xarray(int64) и др.) — типизация на этапе компиляции — быстро, статично, но тип вмерзает в момент компиляции. Сценарии языка-хозяева (скриптовые массивы, JSON, загружаемый назад в память, обмен данными плагинов) требуют контейнеров с типами времени исполнения: «тип элементов этого массива — то описание по имени app.Counter из реестра» — один код контейнеров, диспетчеризация операций над значениями по описанию во время исполнения. Наивный подход — массив void* плюс разбросанные обратные вызовы — приведения в каждой операции всюду; typed-слой концентрирует диспетчеризацию в таблице описаний: один код контейнеров, всё поведение типов — в xrttype.

Отношение к динамическим значениям xvalue главы 31: xvalue — «одно динамическое значение» (самоописывающееся размеченное объединение); typed-контейнеры — «наборы однотипных элементов» (описание на уровне контейнера, ноль накладных расходов метки на элемент — массив int64 это непрерывные int64, а не массив Value). Они дополняют друг друга: Value хорош для гетерогенных деревьев (JSON), typed — для однородных наборов (скриптовые массивы/таблицы полей объекта).

117.2概念

Общий контракт: шесть семейств, одно лицо

ГлаголСемантика
Set/Add/Push/Insertкопирует исходное значение — контейнер владеет новым, исходное остаётся у вызывающего
SetTakeатомарный при отказе перенос — при успехе исходное восстанавливается в пустое
Take/Popперемещает в инициализированный выход — контейнер больше не владеет
Remove/Clear/Unitодин Drop на каждое владеемое значение
Clone/Merge/алгебра множествглубокая копия — без скрытого разделяемого владения

Легальность адресов: API, принимающие адрес полного типизированного значения, допускают «внешнее значение или точный живой слот этого контейнера» — адреса, указывающие в структуру контейнера/метаданные/запасную ёмкость/паддинг/середину слота, отвергаются до чтения или записи; выход и источник перемещения обязаны лежать вне памяти контейнера (контейнерная версия защитной проверки).

Особые грани шести семейств

  • array (непрерывное хранение): фиксированная/переменная длина, доступ по индексу, семейство Concat/Equals.
  • stack (LIFO): Push/Pop/Peek.
  • list (разреженные целочисленные ключи): вставка/удаление, итерация по порядку — форма скриптового списка.
  • tree (упорядоченные родовые ключи): ведомое сравнением упорядоченное отображение — типу ключа нужны операции сравнения.
  • set (уникальные значения): хеш-или-порядковая дедупликация — типу ключа нужны хеш/сравнение.
  • dict (текстовые ключи): словарь со строковыми ключами — нагрузка узла динамических полей главы 115 (xrtdynamicfields) — это он и есть.

Равенство указателей описаний: почему одного ID мало

Буквально из контракта: «операции с двумя typed-контейнерами требуют, чтобы описания типов элементов были одним и тем же указателем. Одинаковый ID, имя, размер или таблица операций недостаточны, чтобы доказать взаимозаменяемость жизненного ABI и частного контекста двух описаний.» Два модуля статически объявили «одинаковый» тип? Указатели разные — взаимодействия нет: встроенные типы XRT и типы, регистрируемые хозяином, должны использовать процессно-канонические описания (тот же указатель, что возвращает xrtTypeInt64()). Это правило уничтожает опасность «содержимое одинаковое, а место объявления разное» (неявный раскол частного контекста/жизни).

Переиспользование, а не копия

Расширение переиспользует внутренние быстрые пути контейнеров XRT напрямую — непрерывное хранение xtypedarray и есть внутренняя структура array главы 14; typed-слой добавляет входы, «диспетчеризирующие операции над значениями через таблицу описаний». Выгода двусторонняя: производительность (быстрый путь push int64 не замедляется под typed-оболочкой) и сопровождение (один код алгоритмов контейнеров — идея пул-изации главы 22 в ещё одной форме, «переиспользование реализаций»).

Атомарность отказа (общая)

При отказе выделения/инициализации значения/копии/перемещения: публично видимые элементы, ключи, порядок, владение не меняются — многошаговые операции (Clone/Merge/алгебра множеств) сперва завершаются в отдельном рабочем хранилище и фиксируются один раз; очистка при отказе уничтожает только успешно построенные в этот раз значения, сохраняя нижележащую ошибку как причину. Финальная категория OOM всегда XERR_MEMORY. Это продолжает дисциплину атомарности операций Value главы 31 и контейнеров главы 18 — typed-слой не расслабляется из-за «динамичности».

Место typed-контейнеров в четырёх главах xruntime

1Система типов (глава 114)описание xrttype - источник фактов об операциях/сравнении/хеше
2Объекты и графы (глава 115)счётные объекты - контейнер может быть нагрузкой и получить идентичность объекта
3Динамические вызовы (глава 116)значения callable - циркулируют как элементы контейнеров
4Typed-контейнеры (эта глава)шесть ведомых описаниями семейств - взаимодействие только при равных указателях описаний

117.3示例

Первая полная программа: полный набор операций typed-массива

Программа ниже из examples/runtime/typed_array — цикл Init/Push/Clone/Concat/Equals/IndexOf:

extlibs/xruntime/examples/runtime/typed_array/main.c源码 ↗
#include <stdio.h>
#include <xruntime.h>



/* 展示类型数组的值复制、查询和自动生命周期。 */
int main(void)
{
	xtypedarray Values;
	xtypedarray* pCopy;
	xtypedarray* pJoined;
	int64 arrInput[] = { 7, 11, 13 };
	int64 iNeedle = 11;

	if ( !xrtTypedArrayInit(&Values, xrtTypeInt64()) ) {
		return 1;
	}
	for ( size_t i = 0; i < sizeof(arrInput) / sizeof(arrInput[0]); i++ ) {
		if ( !xrtTypedArrayPush(&Values, &arrInput[i]) ) {
			xrtTypedArrayUnit(&Values);
			return 2;
		}
	}
	pCopy = xrtTypedArrayClone(&Values);
	pJoined = pCopy != NULL ? xrtTypedArrayConcat(&Values, pCopy) : NULL;
	if ( (pCopy == NULL) || (pJoined == NULL) ||
		 !xrtTypedArrayEquals(&Values, pCopy) ) {
		xrtTypedArrayDestroy(pJoined);
		xrtTypedArrayDestroy(pCopy);
		xrtTypedArrayUnit(&Values);
		return 3;
	}
	printf("count=%zu joined=%zu index=%zu\n",
		xrtTypedArrayCount(&Values),
		xrtTypedArrayCount(pJoined),
		xrtTypedArrayFind(&Values, &iNeedle));
	xrtTypedArrayDestroy(pJoined);
	xrtTypedArrayDestroy(pCopy);
	xrtTypedArrayUnit(&Values);
	return 0;
}
$ gcc -O1 -DXRT_MODULE_ALL -DXRUNTIME_MODULE_ALL -I single -I single/extlibs -include xrt.h -include xruntime.h impl.c extlibs/xruntime/examples/runtime/typed_array/main.c -lws2_32 -lbcrypt -ladvapi32 -liphlpapi(输出 count/joined/index 的数组操作自检行)

Что только что произошло. (1) xrtTypedArrayInit(&Values, xrtTypeInt64()) — ведомо каноническим описанием встроенного int64: последующие Push копируют int64 (решает Ops описания), Equals сравнивает по int64 — в коде контейнера ноль специфичной для int64 логики. (2) После трёх Push Clone глубоко копирует, Concat склеивает (Values+Copy = 6 элементов), Equals(Values, Copy) решает равенство — глаголы владения работают по общему контракту (Clone глубоко, без разделения). (3) Печать count=3, joined=6, index=найденная позиция — массив типов времени исполнения ведёт себя изоморфно C-массиву; отличие только в том, что «тип приходит из описания». Подставьте xrtTypeString() или пользовательский тип — тот же набор API (всё ведомо описанием).

Вторая полная программа: словарь полей объекта

Вторая программа из examples/runtime/typed_dict — конкретизация динамических полей:

extlibs/xruntime/examples/runtime/typed_dict/main.c源码 ↗
#include <stdio.h>
#include <xruntime.h>



/* 展示类型字典的复制键、默认值和插入顺序迭代。 */
int main(void)
{
	xtypeddict Scores;
	xtypeddictiter Iterator;
	xstrview Key;
	int64 iAlice = 95;
	int64 iBob = 88;
	int64* pScore;

	if ( !xrtTypedDictInit(&Scores, xrtTypeInt64()) ||
		 !xrtTypedDictSet(&Scores, XRT_STR_LITERAL("alice"), &iAlice) ||
		 !xrtTypedDictSet(&Scores, XRT_STR_LITERAL("bob"), &iBob) ) {
		return 1;
	}
	pScore = (int64*)xrtTypedDictGetOrAdd(
		&Scores, XRT_STR_LITERAL("carol"), NULL
	);
	if ( pScore == NULL ) {
		xrtTypedDictUnit(&Scores);
		return 2;
	}
	*pScore = 91;
	if ( !xrtTypedDictIterBegin(&Scores, &Iterator) ) {
		xrtTypedDictUnit(&Scores);
		return 3;
	}
	while ( (pScore = (int64*)xrtTypedDictIterNext(
		&Iterator, &Key
	)) != NULL ) {
		printf("%.*s=%lld\n", (int)Key.Size, Key.Data, (long long)*pScore);
	}
	xrtTypedDictIterEnd(&Iterator);
	xrtTypedDictUnit(&Scores);
	return 0;
}
$ gcc -O1 -DXRT_MODULE_ALL -DXRUNTIME_MODULE_ALL -I single -I single/extlibs -include xrt.h -include xruntime.h impl.c extlibs/xruntime/examples/runtime/typed_dict/main.c -lws2_32 -lbcrypt -ladvapi32 -liphlpapi(输出文本键字典操作的自检行)

Что только что произошло. (1) Словарь с текстовыми ключами и ведомым описанием типом значения — Set/Take/Remove по общему контракту; ключи строки (встроенное описание STRING), значения произвольные (здесь демонстрационные типы). (2) Эта форма и есть нагрузка xrtdynamicfields главы 115 — таблица динамических полей объекта хозяина = словарный узел в графе объектов: граф трассирует словарный узел, а значения словаря трассируют ссылки объектов времени исполнения через xrtTypeValue() — три главы (114/113/116) сходятся здесь. (3) Семья компаньонов покрывает все шесть семейств (typed_stack/list/tree/set) плюс три формы очередей (typed_queue_spsc/mpsc/mpmc — typed-версия специализированных очередей топологии главы 21) и typed_value_containers (контейнеры с упаковкой Value) — по одному образцу на структуру — приёмочная грань контракта шести семейств.

117.4契约

  • Заимствование описаний: шесть семейств заимствуют неизменяемый xrttype; стабильно на время регистрации и жизни контейнера (дисциплина главы 114).
  • Равенство указателей: межконтейнерные операции требуют одинакового указателя описаний — одинакового ID/имени/размера/таблицы операций мало; встроенные и типы хозяина — процессно-канонические описания.
  • Диспетчеризация операций над значениями: всё через семейство операций описания (ведомость таблицей) — контейнеры с нулём типоспецифичного кода.
  • Глаголы владения: семейство Set копирует / SetTake атомарно переносит / семейство Take перемещает / семейство Remove по одному Drop / семейство Clone глубоко копирует.
  • Легальность адресов: адреса значений — только внешние или точные живые слоты этого контейнера; структура/метаданные/ёмкость/паддинг/середина слота отвергаются; выход и источник перемещения вне контейнера.
  • Атомарность отказа: видимое состояние неизменно при отказе; многошаговые сперва в рабочее хранилище, потом фиксация; OOM всегда XERR_MEMORY с сохранённой причиной.
  • Переиспользование реализаций: внутренние быстрые пути контейнеров XRT напрямую — ноль налога производительности от typed-оболочки, один код алгоритмов.
  • Граница обычных C-контейнеров: C-контейнеры без счёта ссылок и идентичности объекта — слабые ссылки/возврат циклов компонуются через runtime_object с контейнером в роли нагрузки.
  • Особенности семейств: array непрерывный / stack LIFO / list разреженно-упорядоченный / tree сравнительно-упорядоченный / set уникальный / dict текстовые ключи — каждый специализирован поверх общего контракта.

Типичные ошибки

Ловушка 1: взаимодействие «одинаковых типов» с разными указателями описаний:Ловушка 1: взаимодействие «одинаковых типов» с разными указателями описаний

Симптом: Concat/сравнение двух контейнеров с «совершенно одинаковыми» типами элементов отвергается — каждый модуль статически объявил тип с одним именем.

Причина: равенство указателей — жёсткое правило; одинаковое содержимое не доказывает взаимозаменяемости жизненного ABI и частного контекста. Единообразно используйте процессно-канонические описания (встроенный xrtTypeInt64() / процессно-канонические описания реестра).

错误写法
/* описания Counter, статически объявленные в модулях A и B независимо */
xrtTypedArrayConcat(&A_Array, &B_Array);   /* указатели описаний разные: отвергнуто */
正确写法
/* один источник описаний на весь процесс: брать процессно-каноническое из реестра */
const xrttype* T = host_registry_find("app.Counter");
xrtTypedArrayInit(&A, T); xrtTypedArrayInit(&B, T);
xrtTypedArrayConcat(&A, &B);   /* один указатель: взаимодействие возможно */
Ловушка 2: после Clone разделяются значения, «казавшиеся независимыми»:Ловушка 2: после Clone разделяются значения, «казавшиеся независимыми»

Симптом: клонировали контейнер, изменили один — другой следом; в значениях спрятаны ссылки/дескрипторные элементы.

Причина: Clone — глубокая копия, но «глубина» определяется Ops типа элементов: копия ссылочного элемента (значения-объекта) — это наращивание сильной ссылки (Copy=Ref у ObjectValueOps главы 115) — сам объект остаётся разделяемым (ровно как и должно быть у типов с ссылочной семантикой). Ожидание «разделить даже объекты» требует элементного собственного Clone.

错误写法
pCopy = xrtTypedArrayClone(&Objs);   /* клон массива объектов: ссылки +1, не новые объекты */
mutate(pCopy);                        /* изменилось тело: «изменился» и исходный контейнер */
正确写法
/* ссылочная семантика - это свойство: много контейнеров делят один объект (возврат - забота графа) */
/* для действительно независимых копий: тип элемента даёт Ops глубокого Clone */
Ловушка 3: typed-контейнер как корень графа объектов (потерянная трассировка):Ловушка 3: typed-контейнер как корень графа объектов (потерянная трассировка)

Симптом: ссылки объектов, удерживаемые контейнером, граф не трассирует — цикл течёт.

Причина: обычные typed-контейнеры не несут счёта ссылок и идентичности объекта — граф трассирует runtime_object (глава 115). Правильные пути: контейнер как нагрузка объекта (контейнер сам — объект) или узлы динамических полей (нагруза dict трассируется через операции значения типа).

错误写法
/* голый typed array держит ссылки объектов в надежде, что граф сам обнаружит */
xrtTypedArrayPush(&Arr, &ObjRef);   /* граф не знает о существовании Arr */
正确写法
   или использовать xrtdynamicfields (узлы-словари полей трассируют нативно) */

Практические задания

Базовое: обход шести семейств

Прогнать каждый из шести контейнеров через минимальный цикл (Init → две операции → Destroy) — с одним типом элемента (int64). Критерии приёмки: глаголы владения согласованы между семействами; атомарность отказа (имитация OOM) оставляет видимое состояние каждого семейства неизменным.

Продвинутое: полиморфный список

list с типом элемента xrtTypeValue() (упаковка динамических значений главы 31) — хранить int/string/object три вида значений и обходить с печатью (диспетчеризация по Kind). Критерии приёмки: три вида сосуществуют; глубокие клоны независимы (элементы со семантикой значений); сортировка по сравнению Value.

Вызов: движок полей объекта

Таблица полей в стиле xrtdynamicfields: объект (глава 115) + нагруза typed dict + set/get/перечисление полей + трассировка графа объектов (сбор циклов у самоссылающихся полей). Критерии приёмки: сильные ссылки полей перечисляются Trace; самоссылочные циклы возвращаются в безопасной точке; типы полей вперемешку (int/string/объект).

Итоги

ТемаКратко
Позиционированиешесть структур данных делят один контракт значений времени исполнения — ведомость типами, переиспользование быстрых путей XRT
Дисциплина описанийзаимствуют неизменяемые описания; межконтейнерные операции — равенство указателей (одинакового содержания мало)
Диспетчеризация значенийвсё через семейство операций описания — ноль типоспецифичного кода контейнеров
ВладениеSet копирует / SetTake атомарный перенос / Take перемещает / Remove по одному Drop / Clone глубоко
Атомарность отказавидимое состояние неизменно; сперва рабочее хранилище, потом фиксация; OOM всегда MEMORY с причиной
Шесть семействarray непрерывный / stack LIFO / list разреженный / tree сравнительный / set уникальный / dict текстовые ключи
Против xvalueValue = одно динамическое значение (гетерогенные деревья); typed = однородные наборы (ноль метки на элемент)
Идентичность объектаC-контейнеры без счёта ссылок — объектная семантика компонуется через runtime_object
Замыкание трассировкидинамические поля = нагруза dict + трассировка Value — сходятся 114/113/116
Три формы очередейspsc/mpsc/mpmc в typed-версии — продолжение выбора топологии главы 21

📖 Справочники API в этой главе:xruntime-typed_array (31 записей)xruntime-typed_dict (30 записей)xruntime-runtime_type (60 записей)

Полные сигнатуры, ограничения параметров, возвращаемые значения и ошибки