所以我正在开发一种编程语言,它编译为VM执行的字节码,也编译为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 ++相关的困难,而不是“鸡或蛋”逻辑悖论。显而易见的是,两个对象不可能包含彼此而不会导致某种形式的无限循环。答案 0 :(得分: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
的兼容类型。
({{1}} - 类型成员和原始成员之间可能存在填充/对齐差异,但我找不到这些示例。