简体   繁体   English

C ++或C99理论上可以编译成同等便携的C90吗?

[英]Could C++ or C99 theoretically be compiled to equally-portable C90?

This is a big question, so let me get a few things out of the way: 这是一个很大的问题,所以让我先解决一些问题:

  1. Let's ignore the fact that some C++ features cannot be implemented in C (for example, supporting pre-main initialization for any global static object that is linked in). 让我们忽略一些事实,即某些C ++特性无法在C中实现(例如,支持链接到的任何全局静态对象的预主要初始化)。
  2. This is a thought experiment about what is theoretically possible. 这是一个关于理论上可行的思想实验。 Please do not write to say how hard this would be (I know), or that I should do X instead. 请不要写这么说(我知道)有多难,或者我应该做X。 It's not a practical question, it's a fun theoretical one. 这不是一个实际的问题,它是一个有趣的理论问题。 :) :)

The question is: is it theoretically possible to compile C++ or C99 to C89 that is as portable as the original source code? 问题是:理论上可以将C ++或C99编译为C89,它与原始源代码一样可移植吗?

Cfront and Comeau C/C++ do compile C++ to C already. Cfront和Comeau C / C ++已经将C ++编译为C语言。 But for Comeau the C they produce is not portable, according to Comeau's sales staff. 但根据Comeau的销售人员的说法,对于Comeau来说,他们生产的C不是便携式的。 I have not used the Comeau compiler myself, but I speculate that the reasons for this are: 我自己没有使用过Comeau编译器,但我推测其原因是:

  1. Macros such as INT_MAX, offsetof(), etc. have already been expanded, and their expansion is platform-specific. 诸如INT_MAX,offsetof()等宏已经被扩展,并且它们的扩展是特定于平台的。
  2. Conditional compilation such as #ifdef has already been resolved. #ifdef等条件编译已经解决。

My question is whether these problems could possibly be surmounted in a robust way. 我的问题是这些问题是否可以以强有力的方式克服。 In other words, could a perfect C++ to C compiler be written (modulo the unsupportable C++ features)? 换句话说,是否可以编写完美的 C ++到C编译器(以不可支持的C ++特性为模)?

The trick is that you have to expand macros enough to do a robust parse, but then fold them back into their unexpanded forms (so they are again portable and platform-independent). 诀窍在于你必须扩展宏以进行强大的解析,然后将它们折叠回未扩展的形式(因此它们再次是可移植的和与平台无关的)。 But are there cases where this is fundamentally impossible? 但有哪些情况根本不可能?

It would be very difficult for anyone to categorically say "yes, this is possible" but I'm very interested in seeing any specific counterexamples: code snippets that could not be compiled in this way for some deep reason. 任何人都很难断然说“是的,这是可能的”,但我很有兴趣看到任何具体的反例:由于某种深层原因无法以这种方式编译的代码片段。 I'm interested in both C++ and C99 counterexamples. 我对C ++和C99反例感兴趣。

I'll start out with a rough example just to give a flavor of what I think a counterexample might look like. 我将从一个粗略的例子开始,只是为了说明我认为反例可能会是什么样子。

#ifdef __SSE__
#define OP <
#else
#define OP >
#endif

class Foo {
 public:
  bool operator <(const Foo& other) { return true; }
  bool operator >(const Foo& other) { return false; }
};

bool f() { return Foo() OP Foo(); }

This is tricky because the value of OP and therefore the method call that is generated here is platform-specific. 这很棘手,因为OP的值以及此处生成的方法调用是特定于平台的。 But it seems like it would be possible for the compiler to recognize that the statement's parse tree is dependent on a macro's value, and expand the possibilities of the macro into something like: 但似乎编译器可能会认识到语句的解析树依赖于宏的值,并将宏的可能性扩展为:

bool f() {
#if __SSE__
   return Foo_operator_lessthan(...);
#else
   return Foo_operator_greaterthan(...);
#endif
}

它不仅在理论上是可行的,而且实际上也是微不足道的 - 使用具有cbe目标的LLVM。

Theoretically all Turing-complete languages are equivalent. 从理论上讲,所有图灵完备语言都是等价的。

You can compile C++ to an object code, and then decompile it to plain C or use an interpreter written in plain C. 您可以将C ++编译为目标代码,然后将其反编译为plain C或使用以C语言编写的解释器。

In theory of course anything could be compiled to C first, but it is not practical to do so, specifically for C++. 理论上当然可以先将任何东西编译成C语言,但这样做是不切实际的,特别是对于C ++。

For Foo operator< in your example it could be converted to: 对于Foo运算符<在您的示例中,它可以转换为:

bool isLess(const struct Foo * left, const struct Foo * right );

as a function signature. 作为功​​能签名。 (If C90 doesn't allow bool then return int or char, and similarly old C versions that don't allow const, just don't use it). (如果C90不允许bool然后返回int或char,同样旧的C版本不允许const,只是不要使用它)。

Virtual functions are more tricky, you need function pointers. 虚函数更棘手,需要函数指针。

struct A
{
   virtual int method( const std::string & str );
};

struct A
{
   int (*method)( struct A*, const struct string *);
};

a.method( "Hello" );


a.method( &a, create_String( "hello" ) ); 
          // and take care of the pointer returned by create_String

There are a number of subtle differences. 有许多微妙的差异。 For example, consider the line: 例如,考虑以下行:

int i = UINT_MAX;

IIRC, in C++ this assigns an implementation-defined value. IIRC,在C ++中,它分配了一个实现定义的值。 In C99 and C89, it assigns an implementation-defined value, or raises an implementation-defined signal. 在C99和C89中,它分配实现定义的值,或者引发实现定义的信号。 So if you see this line in C++, you can't just pass it through to a C89 compiler unmodified unless you make the non-portable assumption that it won't raise a signal. 因此,如果你在C ++中看到这一行,你不能只是将它传递给一个未经修改的C89编译器,除非你做出不可移植的假设它不会引发信号。

Btw, if I've remembered wrong, think of your own example of differences in the standards relating to relatively simple expressions... 顺便说一下,如果我记得错了,想想你自己在相对简单表达方面的标准差异的例子......

So, as "grep" says, you can do it because C89 is a rich enough language to express general computation. 因此,正如“grep”所说,你可以这样做,因为C89是一种足够丰富的语言来表达一般计算。 On the same grounds, you could write a C++ compiler that emits Perl source. 基于同样的原因,您可以编写一个发出Perl源代码的C ++编译器。

By the sound of your question, though, you're imagining that the compiler would make a set of defined modifications to the original code to make it compile as C89. 但是,根据您的问题,您可能会想到编译器会对原始代码进行一组定义的修改,以使其编译为C89。 In fact, even for simple expressions in C++ or C99, the C89 emitted might not look very much like the original source at all. 实际上,即使对于C ++或C99中的简单表达式,发出的C89也可能看起来与原始源不同。

Also, I've ignored that there may be some parts of the standard libraries you just can't implement, because C89 doesn't offer the capabilities, so you'd end up with a "compiler" but not a complete implementation. 另外,我忽略了标准库中可能存在一些你无法实现的部分,因为C89不提供这些功能,因此你最终会得到一个“编译器”而不是一个完整的实现。 I'm not sure. 我不确定。 And as dribeas points out, low-level functions like VLAs present problems - basically you can't portably use the C89 "stack" as your C99 "stack". 正如dribeas指出的那样,低级函数(如VLA)存在问题 - 基本上你无法将C89“堆栈”作为C99“堆栈”使用。 Instead you'd have to dynamically allocate memory from C89 to use for automatic variables required in the C99 source. 相反,您必须从C89动态分配内存以用于C99源中所需的自动变量。

One big problem is exceptions . 一个大问题是例外 It might be possible to emulate them using setjmp , longjmp etc., but this would always be extremely inefficient compared to a real device-aware unwind engine. 可以使用setjmplongjmp等来模拟它们,但与真正的设备感知的展开引擎相比,这总是非常低效。

http://www.comeaucomputing.com http://www.comeaucomputing.com

There's no better proof of feasibility than a working example. 没有比工作实例更好的可行性证明。 Comeau is one of the most conforming c++03 compiler, and has support for many features of the upcoming standard, but it does not really generate binary code. Comeau是最符合标准的c ++ 03编译器之一,它支持即将推出的标准的许多功能,但它并不真正生成二进制代码。 It just translates your c++ code into c code that can be compiled with different C backends. 它只是将您的c ++代码转换为可以使用不同的C后端编译的c代码。

As for portability, I would assume it is not possible. 至于便携性,我认为这是不可能的。 There are some features that cannot be implemented without compiler specific extensions. 如果没有特定于编译器的扩展,有些功能无法实现。 The first example that comes to mind is C99 dynamic arrays: int n; int array[n]; 想到的第一个例子是C99动态数组: int n; int array[n]; int n; int array[n]; that cannot be implemented in pure C89 (AFAIK) but can be implemented on top of extensions like alloca . 这不能在纯C89(AFAIK)中实现,但可以在alloca之类的扩展上实现。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM