Подумалось тут: а ведь нет никакой проблемы обеспечить RTTI и передачу разнотиповых параметров при полностью статической типизации без оверхеда. Точнее, сделать так, что оверхед будет возникать только непосредственно при такой передаче, а подпрограмм, ее не использующих, никак не коснется. Всего-то надо — чтобы компилятор помечал такие подпрограммы неким флагом и добавлял скрытый параметр со ссылкой на собственно RTTI фактических аргументов. Так же и переменное число параметров решается.
И чтоб два раза не вставать. Сдается мне, что уважающий себя компилятор в наше время просто обязан уметь отличать чистые функции, маркировать их для себя и вычислять в процессе компиляции, если все аргументы — константы...
А в чём проблема? RTTI нужен только при передаче полиморфных параметров (в остальных случаях тип известен на этапе компиляции), а они всегда передаются по ссылке (либо это объект, либо boxed примитив). Ничто не мешает при выделении памяти под объект/боксинге положить туда ещё и указатель на дескриптор типа (всё равно там лежит ещё и длина области памяти, например).
ОтветитьУдалитьТ.е. выходит код типа
struct object_header{
size_t size;
type_t *rtti;
... // тут может быть прочий хлам, счётчик ссылок, например.
};
object_t *new_object(type_t *type, ...){
struct object_header *ptr = malloc(type->getSize() + sizeof(struct object_header));
ptr -> size = type->getSize();
ptr->rtti = type;
...
return (object_t*)((intptr_t)ptr + sizeof(object_header));
}
И всё. RTTI существует в единственном экземпляре для каждого типа, так что оверхед небольшой.
RTTI не любят использовать, когда даже единственный экземпляр каждого типа стоит слишком дорого, но это же не твой сценарий.
Я тут размышлял вот на какую тему — язык, который одновременно может использоваться как достаточно низкоуровневый, так и как полностью высокоуровневый. И тут возникает проблема — высшие типы (объекты, умные указатели и т.д.) и типы примитивные — это как бы принципиально разные сущности, тогда как я искал модель, где высшие типы плавно расширяют примитивы. Т.е. суть в том, чтобы иметь возможность с реальными примитивами работать как с классами, а не маяться боксингом (это штука неплохая, но я о другой задаче думал).
УдалитьВ общем, это размышлизм об универсальном языке. С полиморфизмом уровня параметров...
Например, чтобы один раз написанная функция sort могла обрабатывать в т.ч. и массивы примитивных типов, лишь бы на типе был задан порядок.
PS. Боксинг, в принципе, решает ту же задачу, но что-то мне кажется, вычислительный оверхед у него поболе будет.
УдалитьНу вот в C# (и вообще в языках для CLR) примитивные типы - частный случай типов-значений (value types), с методами и пропертями (но без возможности наследования, ес-сно).
УдалитьНо это, конечно, не для низкого уровня.
Если говорить о дженерик-алгоритмах, то их, строго говоря, зачастую можно сделать, имея лишь указатели на функции. См. qsort в стандартной библиотеке C.
Но параметрический полиморфизм лучше, конечно. Причём если его делать без (а) боксинга, как в Java (тормоза) и (б) динамической генерации специализированного кода, как в .NET (ибо рантайм толстый), то остаются только макросы (типа шаблонов в C++ и D).
Вообще, D тут образцовый вариант, т.к. в нём можно и функции вызывать как методы (unified function call syntax, UFCS), т.е. эмулировать методы на примитивных типах (в С# тоже можно, но с дженериками это не поможет).
То-то и оно, что это не макросы получаются, а куда более контролируемая штука. И с кодогенерацией, по возможности, при определении, а не при специализации. Т.е. generic-код можно будет и динамически линковать.
Удалить