好的,所以我现在正在写一个翻译(对于我自己设计的语言)并且在处理import
s时遇到问题。
以下是口译员的工作方式:
import <somefile>;
语句后,somefile
将被加载,解析并执行。somefile
中声明了一个函数/类,则在我们的全局函数表中创建了一个条目,以便从那时起可以调用它。现在这里是问题......
问题1:
doSth
中有一个file1
函数,并在file2
中导入file3
导入file2
,则表示file1
的操作也可用。这是不可取的。问题2:
file1
导入file2
(因为,假设我们需要使用file2
中的函数)和file2
导入file1
,则会导致循环引用(很可能一直运行到时间结束)那么,如何解决这些问题呢?有什么想法吗?
我知道这个问题非常专业和复杂,但是如果想知道现有的解释器/编译器如何处理这个问题,那么请说清楚! ; - )
答案 0 :(得分:2)
第1期
C和朋友正好表现出你抱怨的行为,证明无论多么不受欢迎,都有可能与之共存:)但是,许多其他语言也有同样的观点。我所知道的所有解决方案都是“申报出口”的一些变种。通常,您需要一些机制来指定导出哪些定义(或哪些定义不导出);如果您需要显式重新导出导入的定义,那么您将不再受到默认的重新导出。
但是,您现在最终会遇到一个稍微不同的问题:
file3:
export cool_definition
file2:
import file3
export bland_definition
/* Use cool_definition */
file1:
import file2 /* Note: only bland_definition is imported */
import file3 /* We want cool_definition */
现在,您可能必须确保cool_definition
在file1
中直接使用bland_definition
与file2
{{1}}使用时的含义相同。 (取决于您的语言状态,以及您对消除代码重复的关注程度等)。
第2期
假设您同意预处理器导入防护是一个荒谬的黑客,那么您需要记住已导入的文件,并拒绝导入文件(如果已经或当前正在导入)。这很容易。
使循环导入链工作意味着您需要能够在不知道其可能依赖的定义的情况下导入文件。例如,如果这些定义是改变程序语法的宏,那么这可能很棘手。否则,您通常可以收集所有进口的传递关闭;一次处理一个文件以发现定义;然后解决定义(并做任何其他需要做的编译)。
在Google protobuf编译器的源代码中有一个相当可读的策略实现(我想你可以开始浏览here了,但是我看了那段代码已经有一段时间了。)
答案 1 :(得分:1)
有两种截然不同的情况需要考虑。一些有趣的选择包括:
避免不止一次包含同一个文件是微不足道的,但如果你有顺序依赖,除非你管理编译时状态,否则会产生令人讨厌的副作用。现实情况是,包含原始源代码最终会导致比解决的问题更多的问题。
我强烈倾向于只提供(3)。无论文件是原始源,预编译还是完全用不同的语言编写,导入都应该相同。它应该简单地使一组定义可供使用。
让全局符号表中的条目成为访问导入函数的唯一方法并不是一个好主意。导入的文件应提供范围,以便您可以避免命名冲突。目的是如果A导入B和B导入C,则A可以调用B而不是C。
如果您正在寻找可以为您的行为建模的语言,那么首先要忽略C / C ++和Javascript(或者将它们视为 not 要做的事情的例子)。大多数现代语言都提供了合理的模型,具体取决于您尝试做什么。
答案 2 :(得分:0)
循环依赖是一个仍然困扰C的问题。如果文件已经加载,则使用预处理器跳过包含来解决。像这样:
#ifndef __FOO_H
#define __FOO_H
// other #include's go here
#endif // __FOO_H