[fpc-devel] Linking to C++
Peter Popov
ppopov at tamu.edu
Tue Jan 23 23:27:20 CET 2007
>> Op Mon, 22 Jan 2007, schreef Felipe Monteiro de Carvalho:
>> > Maybe the compiler could hide the
>> > procedurization/re-object-orientation, thus making interfacing easier.
>>
>> Already looked at many times, but it is not realistic.
>
> Can you point me to previous discussion of this? Or perhaps a short
> explanation of why, would be nice =)
>
> I did a quick search on fpc mailling lists, found lot´s of talk about
> linking to c++ in general, but nothing on the specific topic of hiding
> the procedurization/re-object-orientation
>
> thanks,
For a realistic example, download the Kylix patch for QT3
(http://sourceforge.net/project/showfiles.php?group_id=106820) and look at
QtLibrary.pas and QtWrappers.pas
It looks something like this:
/* On the C++ side */
class CMyClass{
.....
public:
CMyClass(){/* def constructor */};
~CMyClass(){/* def destructor */};
virtual int DoSomething(){ return 42; };
}
/* THE FOLLOWING FUNCTIONS ARE ALL EXPORTED. */
/* The export modifier is different from compiler to compiler, so I skip
here */
CMyClass *MyClassFactory(){
return new CMyClass();
}
DestroyMyClass(CMyClass *instance){
delete instance();
}
int CMyClass_DoSomething(CMyClass *instance){
return instance->DoSomething();
}
{ ON THE PASCAL SIDE }
TMyClassImport = class
protected
// Override NewInstance and throw a readable exception,
// explaining that you cannot allocate an imported class.
// This will happen if you call the Create constructor.
class function NewInstance: TObject; override;
public
// Instead of Create, use the next function as a constructor.
class function New: TMyClass;
// This will be used as a destructor
procedure Delete; cdecl;
function DoSomething(): Integer; cdecl;
// Override Free and redirect it to use the Delete function
// instead of the default Destroy constructor.
procedure Free; override;
end;
class function TMyClassImport.NewInstance: TObject;
begin
raise Exception.Create('Please, create an instance by using
TMyClassImport.New');
end;
function MyClassFactory: TMyClassImport; external DLL_NAME
'MyClassFactory';
class function TMyClassImport.New: TMyClassImport;
begin
Result := MyClassFactory;
end;
TMyClassImport.Delete; cdecl; external DLL_NAME 'DestroyMyClass';
// Override Free and redirect it to use the Delete function
// instead of the default Destroy destructor
TMyClassImport.Free;
begin
if Self <> nil then Delete;
end;
TMyClassImport.DoSomething; cdecl; external DLL_NAME
'CMyClass_DoSomething';
At this point you have all the functionality of CMyClass.
Finally, you can define
TMyClass = class
private
handle: TMyClassImport;
public
function DoSomething: Integer; virtual;
.......
end;
and here you can redirect all functions to the handle object
(TMyClassImport). You can have a normal Create constructor and Destroy
destructor, etc. Also, you can do some basic extension of the
functionality. In this example,
function TMyClass.DoSomething;
begin
Result := handle.DoSomething;
end;
Conceivably, you can do some limited extension of DoSomething. TMyClass
and all derived classes will allocate a handle of type TMyClassImport so,
internally they redirect the calls to the basic methods of CMyClass. So,
the C++ side always gets the handle object which has the correct class
layout (because it was allocated on the C++ side).
However, suppose that a non-virtual function of CMyClass, say
DoSomethingElse, relies on the answer of DoSomething. Any change you make
in DoSomething, by dering classes from TMyClass and overriding it will not
result in polymorphic behavior of DoSomethingElse. That is,
TMyDerivedClass.DoSomethingElse will still forward the handle of the
original C++ class (handle.DoSomethingElse) and the later will use the
original CMyClass:DoSomething. So the answer will always be 42.
Peter
More information about the fpc-devel
mailing list