Since early XE versions C++ Builder supports Delphi units directly added in C++ project and compiles them as well as C/C++ source. However, this approach has several drawbacks:
- Usually, Delphi files are stored in separated folders of other applications and packages. So your C++ project will point to many relative or absolute paths beyond the project root folder
- Delphi units may have many dependencies. Therefore, you should include them all into your C++ project as well
The solution is to group Delphi units into package library and then use it in C++ Builder project with a minimum of dependencies. The example below is tested with Delphi/C++ Builder Berlin 10.1 but should work with previous versions too.
Prepare project directories
I suggest the following folder structure for my test example.
MyProjects ├───Include ├───Libs ├───MyCBuilderApp └───MyDelphiLib
MyCBuilderApp
and MyDelphiLib
are the subdirectories for corresponding projects. Folders Include
and Libs
will store shared generated headers and libraries.
Create Delphi package library
- Start Delphi and create new Delphi project of "Package" type
- Save project as
MyDelphiLib.dproj
intoMyDelphiLib
folder - Add new Delphi unit and save it as
MyDelphiUnit.pas
in the project folder - Change project settings and specify C++ generation options
- Write unit code as in following example
- Build project and verify that HPP and LIB files are generated
MyDelphiUnit
contains the class THello
that prints out the messages using an assigned event handler or a delegate class implementing the output. The implementation of the output method is injected into the class constructor so you can see a dependency injection pattern at the same time.
unit MyDelphiUnit; interface type TOnMessageEvent = procedure (const Msg: string) of object; TMessageDelegate = class(TObject) public procedure DoMessage(const AMsg: string); virtual; abstract; end; THello = class(TObject) private FOnMessage: TOnMessageEvent; FDelegate: TMessageDelegate; protected procedure DoMessage(const AMsg: string); public constructor Create(const AOnMessage: TOnMessageEvent); overload; constructor Create(const ADelegate: TMessageDelegate); overload; destructor Destroy; override; procedure PrintHello; end; implementation { THello } constructor THello.Create(const AOnMessage: TOnMessageEvent); begin inherited Create; FOnMessage := AOnMessage; DoMessage('THello.Create'); end; constructor THello.Create(const ADelegate: TMessageDelegate); begin inherited Create; FDelegate := ADelegate; DoMessage('THello.Create'); end; destructor THello.Destroy; begin DoMessage('THello.Destroy'); inherited Destroy; end; procedure THello.DoMessage(const AMsg: string); begin if Assigned(FOnMessage) then FOnMessage(AMsg) else if Assigned(FDelegate) then FDelegate.DoMessage(AMsg); end; procedure THello.PrintHello; begin DoMessage('Hello from MyDelphiLib!'); end; end.
Create C++Builder application
- Start C++ Builder and create new console application. When prompted by project wizard, specify VCL framework to use.
- Save project as
MyCBuilderApp.cbproj
intoMyCBuilderApp
folder as well asmain.cpp
and header files - Change project options for "include" and "library" search paths
- In the main file include
MyDelphiUnit.hpp
as well as corresponding library - Write some C++ code that uses Delphi class as in following example
- Build project and run it
#include <vcl.h> #include <windows.h> #pragma hdrstop #pragma argsused #include <tchar.h> #include <stdio.h> #include <MyDelphiUnit.hpp> // Delphi-generated header #pragma comment(lib, "MyDelphiLib.lib") // Delphi-generated library to link using namespace std; class MyDelegate : public TMessageDelegate { public: virtual void __fastcall DoMessage(const UnicodeString Msg) { wcout << L"DC: " << Msg << endl; } }; class HelloCall { private: void __fastcall OnMessage(const UnicodeString Msg) { wcout << L"FP: " << Msg << endl; } public: void Hello() { THello *hello = new THello(&OnMessage); hello->PrintHello(); delete hello; } }; int _tmain(int argc, _TCHAR* argv[]) { // Function pointer (event) implementation HelloCall hc; hc.Hello(); // Delegate class implementation MyDelegate *delegate = new MyDelegate(); THello *hello = new THello(delegate); hello->PrintHello(); delete hello; delete delegate; return 0; }
If all sources were built with no errors, you should see the following output on Windows console.
Debugging Delphi package sources in C++Builder
You may need to step into Delphi package sources from C++ code sometimes. Open Delphi package project settings, select target debug configuration for all or specific platform and check following options.
- Delphi compiler
- Compiling
- Optimization = "false"
- Debug information = "Debug information"
- Stack frames = "true"
- Use debug DCU's = "true"
- Linking
- Debug information = "true"
- Include remote debug symbols = "true"
- Compiling
Save settings and rebuild Delphi package.
In C++Builder project options:
- go to Debugger/Source path;
- add paths to Delphi sources.
Restart C++ Builder and recompile the project. From now you should be able to step into Delphi source code.
Conclusions
Using Delphi code in C++ Builder is pretty easy but you should structure your packages to share them with a minimum of dependencies. Adding Delphi files directly into C++ Builder project is an excusable solution only for standalone Delphi units with a small number of dependencies in the uses
section that won't be shared for other projects later.