C++ “解决循环依赖难题”;优雅地;

C++ “解决循环依赖难题”;优雅地;,c++,c,inline,circular-dependency,expansion,C++,C,Inline,Circular Dependency,Expansion,因此,我正在开发一种编程语言,它可以编译成字节码,用于VM执行,也可以编译成C,作为编译成本机二进制的中间语言。我之所以选择C,是因为它的级别足够低,而且是可移植的,通过重用现有的编译器,并且不必为每个不同的平台及其奇怪之处编写编译器来汇编,从而节省了大量的工作量 但是现有的编译器也有其缺点,其中之一就是循环依赖性问题。我想以一种优雅的方式解决循环依赖关系(与C/C++不同),不需要笨拙的前向声明,不需要使用指针和额外的间接寻址和浪费内存,不需要将声明与定义分开等等。。。换句话说,像某些编程语言

因此,我正在开发一种编程语言,它可以编译成字节码,用于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.A_a = 7;
编译器会做一些“翻译”:

变成:

typedef struct {
    int A_a;
    int b;
} B;
b.A_a = 7;
以同样的方式,所有结构都被折叠成一个只包含基元类型的根结构。这样,结构中绝对不会使用大小未知的类型,因此定义顺序变得无关紧要。当然,这种混乱是隐藏起来的,远离用户,只有编译器的“眼睛看到”,而用户端保持结构化和可读性。不用说,但是为了与常规C/C++代码兼容,保留了二进制封装。折叠的结构是二进制的,与使用聚合或继承的常规结构相同

所以我的问题是:这听起来像个好主意吗?我遗漏了什么可能出错的东西


编辑:我的目标只是解决与C/C++相关的循环依赖问题,而不是“鸡还是蛋”的逻辑悖论。显然,如果不导致某种形式的无限循环,两个对象不可能相互包含。

您不能安全地使用指向子结构的指针,因为您无法通过指向基元成员获得指向“兼容类型”的指针。例如,在

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
类型的成员和基元成员之间可能存在填充/对齐差异,但我找不到这样的示例。

令人印象深刻,但有什么真正的“要点”吗对于内联扩展?当访问
b.A\u A
而不是
b.A.A
时,生成的代码是否有任何不同?我希望它完全相同,因此您要做大量的“优化”工作一些没有太多好处的东西。只是问一下。@unwind-好处是循环依赖变成了一个灭绝的和不相关的问题。啊,我明白了。如果你的C类示例中
A
B
的顺序相反,可能会更清楚。如图所示,C声明很好,并且没有循环问题。@unwind-the代码展示了解决问题的概念,而不是问题本身,这只在文本中解释。我假设人们会阅读问题的主体,而不仅仅是源代码;),但如果你打算使用
B
A
成员,就像它是
A
(即将其转换为
A*
)在对齐、填充等方面,您将遇到无休止的问题。我通过手动插入填充字节来保持二进制兼容性,当然不是手动插入,但我的编译器这样做是为了符合C编译器在聚合结构时所做的操作。