Решение загадки круговой зависимости "элегантно"
Поэтому я разрабатываю язык программирования, который компилируется в байт-код для выполнения виртуальной машины, а также в C как промежуточный язык для компиляции в нативный двоичный код. Я выбрал C, потому что он достаточно низкоуровневый и переносимый, что экономит огромное количество усилий за счет повторного использования существующих компиляторов и не требует написания компиляторов для сборки для каждой отдельной платформы и ее странностей.
Но существующие компиляторы имеют свои недостатки, одним из которых является проблема циклической зависимости. Я хочу решить циклические зависимости элегантным способом (в отличие от C/C++) без неуклюжих предварительных объявлений, без необходимости использовать указатели и лишнюю косвенность и потерянную память, без необходимости отделять объявления от определений и так далее... Другими словами, уберите эту проблему от разработчика, как это делают некоторые языки программирования.
На мой взгляд, главная проблема современных компиляторов C / C++ в том, что они не могут "заглядывать в будущее", хотя это не совсем будущее, поскольку намерение программиста уже выражено в коде, у моего компилятора такой проблемы нет. Он не знает ничего за пределами определенной точки анализа, он знает размеры объектов с круговыми зависимостями и может рассчитать соответствующие смещения и тому подобное.
Я уже реализовал "фальшивое" наследование, которое просто выполняет "встроенное расширение" членов унаследованных структур, поэтому я думаю, что я могу также использовать тот же подход для фактического фальсификации агрегации. В самом простом и простом примере:
typedef struct {
int a;
} A;
typedef struct {
A a;
int b;
} B;
будет выглядеть так:
typedef struct {
int A_a;
int b;
} B;
и компилятор делает немного "перевода":
B b;
b.a.a = 7;
будет выглядеть так:
b.A_a = 7;
И таким же образом все структуры сворачиваются до единой корневой структуры, которая содержит только примитивные типы. Таким образом, нет абсолютно никаких типов, используемых в структурах, размеры которых заранее неизвестны, поэтому порядок определения становится неактуальным. Естественно, этот беспорядок скрыт от пользователя и предназначен только для того, чтобы компилятор мог его увидеть, пока пользовательская сторона остается структурированной и читаемой. И само собой разумеется, но двоичный код сохраняется для совместимости с обычным кодом C / C++, свернутая структура является двоичной, идентичной обычной структуре, которая будет использовать агрегацию или наследование.
Итак, мой вопрос: звучит ли это как здравая идея? Что-нибудь, что могло пойти не так, как я скучаю?
РЕДАКТИРОВАТЬ: Я только нацелена на решение связанных с C / C++ трудностей с круговыми зависимостями, а не логическим парадоксом "курица или яйцо". Очевидно, что два объекта не могут содержать друг друга, не приводя к некоторой форме бесконечного цикла.
1 ответ
Вы не можете безопасно использовать указатели на подструктуры, потому что вы не можете получить указатели на "совместимые типы", указывая на примитивные члены. Например после
struct Foo {
short a;
int b;
};
struct Bar {
struct Foo foo;
};
struct Bar bar;
указатели &bar.foo
а также &bar.foo.a
имеют разные типы и не могут использоваться взаимозаменяемо. Они также не могут быть приведены к типам друг друга, не нарушая строгое правило псевдонимов, вызывая неопределенное поведение.
Эту проблему можно избежать, если struct
определение каждый раз:
struct Bar {
struct { short a; int b; } foo;
};
Сейчас &bar.a
это указатель на struct {short; int;}
который является совместимым типом для struct Foo
,
(Там также могут быть различия между отступами / выравниванием struct
члены и примитивные члены, но я не смог найти пример этого.