PDA

View Full Version : C++ Classes in Delphi/FreePascal



Ultra
06-11-2004, 05:03 PM
Hello all!

I want to be able to use C++ and Delphi/FreePascal (in Delphi Mode) for a project of mine. I'll use dll files to handle the communication between the languages.

Anyway after I compiled a dll with FreePascal the application crashed. It worked fine when compiling with Delphi, but that's not good enough for me since I want it to work with both compilers.

Here's a small example which shows what doesn't work:

C++


// in *.h
typedef class IMyClass
{
public:
__cdecl virtual void DoSomething()=0;
} *lpMyClass;

typedef class CMySub: public IMyClass
{
public:
__cdecl void DoSomething();
};

// in *.cpp
__cdecl void CMySub::DoSomething()
{
printf("hello world\n");
};


Pascal:

// MyClass.pas
type
IMyClass = class(TObject)
public
procedure DoSomething; virtual; cdecl; abstract;
end;

// pasdll.dpr
procedure InitDll(MyClass: IMyClass); cdecl;
begin
MyClass.DoSomething;
end;

exports
InitDll name 'InitDll';


And some extra code for initializing everything.

Any ideas on how to get things working with FreePascal?

cairnswm
06-11-2004, 06:36 PM
First - I know little about interfaces
Second - I've never used classes and DLLs together

So: :)

1. Why is _I_MyClass not inheriting from some sort of Interface (IUnknown)>
2. Where Is the MyClass.Create statement?
3. Ummm


But dont believe a word I say here because Its out of my league :)

{MSX}
06-11-2004, 07:43 PM
I'll use dll files to handle the communication between the languages.

That's ok, but DLLs does not handle nicely classes. You can have c++ classes and read them with c++ programs, but not mixing classes from different languages, becouse they're implemented in different ways.

To do what you want usually one write the program in c++ but then writes a layer of code exporting only plain procedures.

Ultra
07-11-2004, 03:01 AM
1. Why is _I_MyClass not inheriting from some sort of Interface (IUnknown)>

Because I'm not entirely sure on how interfaces work in Delphi. I could probably use interfaces and just add stuff like AddRef() and Release() to the C++ class but since I'm not sure how it would work I don't want to use it.



2. Where Is the MyClass.Create statement?

In the C++ code. I can just use CMySub *MyClass = new CMySub(). And then send the returned pointer to Delphi.



That's ok, but DLLs does not handle nicely classes. You can have c++ classes and read them with c++ programs, but not mixing classes from different languages, becouse they're implemented in different ways.

To do what you want usually one write the program in c++ but then writes a layer of code exporting only plain procedures.

I guess this is more or less what's the problem. My guess is that the Virtual Method Table indexes are different in Delphi and FreePascal. (I can't really explain what the VMT is since I'm not entirely sure myself, but think of it as an array of method pointers where all the TObject methods has a negative index. That way a C++ class, with it methods starting at 0, can be called without problems. If these have a positive index in FreePascal MyClass.DoSomething() will point to something irrelevant live MyClass.Create()).
As for not using something like MyClass_DoSomething(). Well, that could probably work (and if I can't come up with some other solution I'll probably use it). However it's not as clean, especially when I have some code like IMyClass.Add(): IMyOtherClass and IMyClass.Find(AName: PChar), then it can be a hell to use...

As for using C++ classes in Delphi I used this article (http://rvelthuis.bei.t-online.de/articles/articles-cppobjs.htm).

[edit] Seems like I don't really know how to use URLs... :?

BenBE
07-11-2004, 07:41 PM
@Ultra: Just leave out the quotation marks :D Then it works fine.

But in general you should ALWAYS avoid using classes for transaction between modules like Program<->DLL.

cairnswm
08-11-2004, 05:37 AM
Many years ago I did some work on a PC game that actually made it to the shelves. I worked on a Rugby Match simulator. I did the DLL in Delphi and the rest of the project team worked in C++.

I went back and had a look at my code. I used Classes internal to my DLL but only exported procedures and functions.

My Type declarations were somethign like:

Type
TPlayer = Class
Name : Stirng;
Skill : Integer;
End;


My functions that got exported were

Procedure FindPlayer(Name : String);
Function PlayerSkill(Name : Stirng):Integer;
Procedure FirstPlayer;
Procedure NextPlayer;


Then the guys could call PlayerSkill('Ultra') and get his skill.

So my DLL managed all the Class stuff and memory issues that went with that and the exports were static to find a single item of information (sort of like TSearchRec does it).

Ultra
08-11-2004, 12:28 PM
I'm probably gonna just use regular functions when I call the dll's. It's a bit disappointing but should work with both Delphi and FreePascal.

Clootie
08-11-2004, 01:53 PM
Just some irregular thoughts:
1) Have you enabled Delphi compatibility in FPC?
2) Try decralring class as: "IMyClass = class" (without TObject)

Ultra
09-11-2004, 01:00 AM
Just some irregular thoughts:
1) Have you enabled Delphi compatibility in FPC?
2) Try decralring class as: "IMyClass = class" (without TObject)

1) Yes, I've written {$IFDEF FPC}{$MODE DELPHI}{$ENDIF} at the top of the file. If I remember correctly "class" isn't a reserved word in FreePascal so it wouldn't even compile.

2) Does it matter? TObject should always be inherited whether or not you explicitly say it. I just write so for clarity.
[Edit] I did try it though. Didn't make any change.

Almindor
27-12-2004, 08:42 PM
Using classes in different languages is impossible and will never be as far as I know via SO's or DLL's.

You could use things like COM or COBRA but those are winshit only.

The problem lies in the VMT and in the memory layout of classes.
This is not FPC specific it's common for all OOP language(hence COM)

If it's not cruical I'd suggest using classes internaly and exporting only procedures/functions.

savage
28-12-2004, 09:44 AM
Maybe I have missed something here, but I thought that FreePascal is not able to create DLLs/SOs yet? I thought that was the main issue as to why Lazarus does not load it's components dynamically, hence why the whole IDE is compiled everytime a component is added.

Don't confuse this with the fact that FreePascal CAN access/interact with DLLS, but I don't think it can create them.

Can anyone from the FreePascal community confirm or deny the ability to create DLLs/SOs using FreePascal? Oh yeah, and if it can't, when the hell will they add that in :).


Thanks.

Marco
28-12-2004, 03:27 PM
FPC has some rudimentary DLL and PIC support. However it is not always userfriendly. (read: slight mod might break dll compability)

FPC afaik has no full C++ support yet, though theer has been toyed with that in the past. There is no C++ mangling yet, and it is not yet clear which C++ compiler to support anyway (gcc is most likely, not BCB or VC++)

Lightning
29-12-2004, 03:46 PM
FPC can compile .so/.dll with no problems, the problem here is name mangling changes from one GCC release to another (i think i will kill those GCC developers, just joking :) ) and i don't know if C++ classes are compatible with FPC ones, i don't think GCC classes are compattible with BCB or VC++ classes, most libs don't export classes for another language, look at windows .dlls all export functions not classes.
Talk to the FPC team for more info on this.

savage
29-12-2004, 05:29 PM
Ok I am lost guys. If FPC does support creating DLLs and SOs then why doesn't Lazarus use them to dynamically load DLLs as part of the IDE, for it's component architecture? What am I missing here?

Lightning
29-12-2004, 06:17 PM
It will, it's in the "planning stage" for some time now, recently i needed to make browser plugins that will work in any browser on any OS and i found the XPCOM technology used by almost all browsers except IE wich uses COM(ActiveX), XPCOM is OpenSource unlike ActiveX wich is proprietary.
This technology could be used as base to the IDE packages wich are plugins, of course it sounds simple and it is but a lot of work is needed and there are simply to few programmers to do it.
I'm one of those wich loves IDE packages and i plan to add them myself if i have some time and of course after i complete the things on my ToDo list like the plugins i was talking about :)

marcov
31-12-2004, 12:16 PM
Ok I am lost guys. If FPC does support creating DLLs and SOs then why doesn't Lazarus use them to dynamically load DLLs as part of the IDE, for it's component architecture? What am I missing here?

More advanced features, that make up the difference between dll and
packages:
- unified memmanager over dynamic parts (sharemem unit in Delphi),
- avoiding that objects get one VMT in the DLL, and one in the .exe etc

You should see a FPC DLL now as a standalone program, with an own RTL that you can load and call. (a slight workaround via cmem exempt), not as a modular loadable part of your own mainprogram

Clootie
29-01-2005, 10:26 PM
Back to original topic...

Couple of days ago when testing how D3DX9 loading of .X meshes with embedded hierachy working in FreePascal I've stumbled the same problem as Ultra: details of FreePascal implementation of VMT are different to C++ / TurboPascal / Delphi ones. In Delphi/C++ first virtual method is allocated in VMT with Offset == 0, but in FreePascal it has positive offset of 80 bytes. :twisted:

I found a solution to this problem, althrow not very elegant, but at least working. In mine case I've needed to export Pascal class to C++ code. First I've thought about patching class so Self will be pointing to "correct" method, but in this case Pascal code will loose ability to access class data members. Finally I've decided to use interfaces feature of object pascal (not least due to both mine C++ headers and Ultra ones pretend to be using some kind of COM incompatible interfaces :D ).

So for Ultra case solution will be:
[background=#FFFFFF][normal=#000000][number=#0000FF][string=#0000FF][comment=#248F24][reserved=#000000]
// MyClass.pas
type
IMyClass = interface
public
procedure DoSomething; virtual; cdecl; abstract;
end;

// pasdll.dpr
procedure InitDll(const MyClass: IMyClass); cdecl;
begin
// Before using MyClass patch interface to skip IUnknown fields - not needed in our case
// WARNING: you should NOT assign MyClass to other interfaces, as it's not REAL COM INTERFACE!!!
Dec(PInteger(MyClass)^, $C);

MyClass.DoSomething;

// After using - restore interface VMT
Inc(PInteger(MyClass)^, $C);
end;

exports
InitDll name 'InitDll';


In case you need to pass Pascal "interface" to C++ (as I needed) you should perform reverse operation: Inc Self pointer before passing to C++ code, and Dec it after call. There are some other implementation details in this case - I'll later post excerpts from mine code to demonstrate complete solution.

Clootie
31-01-2005, 09:22 PM
OK, after complaining on this compatibility issue of FPC-dev mail list, another solution was proposed to me... FreePascal allows to define "raw" interfaces - interfaces which do not include IUnknown by default. Side effect of these interfaces is that automatic management (reference counting) of these interfaces is disabled, but it's really a plus in our cituation.

So, final result looks like this (again for Ultra's case):
[background=#FFFFFF][normal=#000000][number=#0000FF][string=#0000FF][comment=#248F24][reserved=#000000]// MyClass.pas
type
{$INTERFACES CORBA}
IMyClass = interface
procedure DoSomething; cdecl;
end;
{$INTERFACES DEFAULT}

// pasdll.dpr
procedure InitDll(const MyClass: IMyClass); cdecl;
begin
MyClass.DoSomething;
end;

exports
InitDll name 'InitDll';

But remember this will not work in Delphi! :)

Anonymous
20-03-2005, 12:56 PM
Back to original topic...

Couple of days ago when testing how D3DX9 loading of .X meshes with embedded hierachy working in FreePascal I've stumbled the same problem as Ultra: details of FreePascal implementation of VMT are different to C++ / TurboPascal / Delphi ones. In Delphi/C++ first virtual method is allocated in VMT with Offset == 0, but in FreePascal it has positive offset of 80 bytes. :twisted:


This is a known incompability, and there is not much that can be done about it, since it is a limitation of the GNU linker, to not be able to use negative offsets to a symbol.

I understand how the workaround works, but I have no idea how general it is.