I am seriously considering writing a Delphi-to-C++ compiler that is 99% compatible with Delphi XE6.
I’m finding FreePascal to be a bit frustrating, in different ways than the Delphi compiler is frustrating. Embarcadero’s 64-bit compiler has recently profoundly let me down with it’s lack of optimization, while FPC is a maze to navigate and I keep getting random “Fatal: Compilation Aborted” messages that offer no indication to the what the real issue is.
I like to complain a lot that Delphi’s generic support is limited and incomplete, well… I’m finding that FreePascals generic support is even more limited and more incomplete, and the popular Lazarus IDE doesn’t really fully embrace the 2.7.1 compiler in its latest version (am I forced to run an older branch?). I have a lot of code that relies on the elegance that generics can bring to the table. I’d use generics more if either language had a robust implementation of generics. C#, for example, makes great use of generics. FPC supports Generic Classes, but not generic methods at this time. Delphi supports generic methods, but only when attached to a class, there are no generics allowed on global functions. Furthermore there is no proper way to properly reference a generic record because records do not support Interfaces and Interfaces are the way that constraints are expressed in Delphi Generics. I’m tired of a lack of progress by Embarcadero and the community really. I can do this… and it can be good.
Below is just a brainstorm of the various challenges that I would be up against in writing such a contraption. If you’re adventurous and prolific and want to get the most out of your pascal code… drop me a line and join my project…. I’m just getting started. I have a 4.3 million line codebase to protect, so having a complete implementation is going to be quite important.
I think there are certain fossils I’d toss out, like the old Pascal calls and syntax that used to be part of the built-in library such as “AssignFile” and “WriteLn” commands. They are rooted in the archaic. My logic is that if you’re using those calls, you should probably be upgrading them to using TfileStream.
Eventually I’d like to add some features that I think are sorely missing from the language while paving the way for seriously hardcore optimization.
The Delphi/Object Pascal language is structured in such a way that it CAN and SHOULD BE as fast as C++. Since there are no compilers out there that compile Delphi/Object Pascal as fast as C++… I figure, why not just compile it to C++ anyway and let all the R&D that has been put into the open source compilers over the years dictate how fast the end code ultimately runs?
There are just a few considerations…
Consideration #1. The “Volatile” keyword.
C++ has the “Volatile” keyword which is missing from Pascal. The volatile keyword plays a key role in optimization. In C++ any variable or call can be optimized, even executed out of order, and eliminated unless it references volatile memory locations. This level of optimization makes a huge difference at run-time (although it makes it practically impossible to debug). 99% of the time, just about any variable can be considered non-volatile and in C++ and that is the default for any variable. However, if you mark a variable as volatile, then it tells the compiler “hey, even though I just read this from that memory location, there’s a chance that something else could come along and change the value after I read it, so it is probably important to try to always read the most recent, freshest result from that location.” In the Embedded systems world, these volatile variables most often point directly to system hardware registers, flags, etc…. but in a multi-threaded environment, it is also important to mark things volatile that other threads might come along and modify… basically any cross-thread communication. For example if I write code in one that that checks for a flag set by another thread, the optimizer might not ever see the flag change in the other thread because it has optimized it, put it into a system register and forgotten about the original memory location.
Consideration #2. The Best of both worlds.
C++ has a more mature class structure. It supports constructors/destructors in immutable objects (e.g. records). It also has better operator overloading support, multiple-inheritance, and probably a few other features that Delphi/Object Pascal lacks, but for the most part Object Pascal and C++ possess the same language features and are built on the same paradigms. I’d say the overlap is 98%. Delphi, on the other hand, has a few things that C++ lacks: unit initializers/finalizers, a native set of string types, simple but ridiculous integration with windows messages (the “message” keyword), Properties, and a generally more friendly and readable syntax throughout. I’d like to create a variation of Pascal that capitalizes on the strengths of both langugages and is as fast as C++ can be underneath.
Consideration #3. Delphi compiles FAST.
Workflow is immensely important. So if your program has to be compiled in two stages, then we’d also run the risk of our app’s compilation speed being dictated by the speed of the C++ compiler underneath. This is unacceptable. But my observation is that the only reason C++ takes as long as it does to compile is because of poor design decisions that have carried through since C was created. The inherent problem with C++ compilation time stems from the fact that the #include keyword can cause files to be compiled, and recompiled, and recompiled, and recompiled. This is largely due to the rule that use of a #define keyword above the #include can potentially affect the compilation of the file being #included. By rule, therefore, the #include file must be recompiled for every C file in your project. To combat this problem, I propose that we compile the .pas files into .cpp files in such a way that they are either 1) contained in just one .cpp file that does not #include any other file, or a set of files with a similar goal of reducing recompilation of redundant files. With this in mind, I think that the C++ compilation stage will add only a negligible amount of compile time to the end product.
Consideration #4. Object Life Paradigms
C++ supports constructors/destructors on classes which behave more like delphi’s record types. To use a C++ class in a manner similar to a Delphi Class, it must be turned into a pointer. All Delphi Classes behave like pointers, but sometimes it is also nice to have classes behave in a manner similar to C++ classes. I’d like to upgrade the language to support 3 different styles of object lifetime management: 1) Typical Delphi style with “create”… “free”… etc. 2) immutable types that are stack-allocated similar to C++ and delphi’s Record type for when you want to create complex types that behave like regular variables 3) Automatic Reference counting similar to The Delphi Mobile compiler, C#, and Delphi’s implementation of Interfaces. I figure, why am I limited to “records” being one way, classes being another way, and interfaces being a 3rd way? Why can’t all these structures be whatever way I need them to be?
In order to organize my thoughts, I’ve decided to make a list of the prominent langugage features of Delphi that would need to be considered/translated to make this whole contraption work. Am I missing any? Chime in if you have something to contibute.
1. [EASY] Class translation
2. [EASY] Generic Translation
3. [EASY] Namespace/unit resolution
4. [EASY] Function Translation
5. [MODERATE] Specialized reference counted string handling.
6. [MODERATE] Translation/adaption of the hidden “system” unit.
7. [MODERATE] Const buffer and var buffer nonsense
8. [MODERATE] Ancient functions that accept variable numbers of parameters (likely unsupported)
9. [EASY] For-loop translation
10.[Easy] While-loop translation
11.[EASY] try-finally implementation
12.[EASY] try-except implementation
13.[MODERATE] general exception handling challenges
14.[MODERATE] method hooks and function pointers
15.[MODERATE] anonymous methods
16.[MODERATE] DElegate methods of object
17.[HARD] ActiveX/OCX/TLB support
18.[MODERATE] DLL imports/exports
19.[MODERATE] DLL Delayed loading.
20.[HARD] Interface Support and generic constraints
21.[MODERATE] Immutable structures records and packed records
22.[HARD] Integration with the MESSAGE keyword and message pipeline.
23.[HARD] DFM support.