Using Delphi library with C++ Builder
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.
blog comments powered by Disqus