C++ ABI Compatible?

General FreeBASIC programming questions.
Imortis
Moderator
Posts: 1924
Joined: Jun 02, 2005 15:10
Location: USA
Contact:

C++ ABI Compatible?

Post by Imortis »

I was under the impression that freeBASIC was not C++ ABI compatible. Is that still true? Thanks to anyone who can give me an accurate answer.
D.J.Peters
Posts: 8586
Joined: May 28, 2005 3:28
Contact:

Re: C++ ABI Compatible?

Post by D.J.Peters »

This isn't new and the point why I made so many C++ wrappers for FreeBASIC :-)

FLTK c++ gui wrapper
Assimp c++ modell importer wrapper
Squirrel c++ script language wrapper
Steinberg VST c++ audio/midi interface wrapper
Horde3D c++ OpenGL engine wrapper
TrueAxis c++ physics wrapper
React Physics c++ wrapper
Tokamak c++ Physics wrapper
libOpenB3D c++ OpenGL engine wrapper
wxWidget c++ wx-c-0-9-0-2 wrapper
...
Imortis
Moderator
Posts: 1924
Joined: Jun 02, 2005 15:10
Location: USA
Contact:

Re: C++ ABI Compatible?

Post by Imortis »

That is what I thought. Just confirming. Thanks, D.J. Peters.
caseih
Posts: 2157
Joined: Feb 26, 2007 5:32

Re: C++ ABI Compatible?

Post by caseih »

FB is compatible with GCC's c++ name mangling scheme and calling convention. So it's partially ABI compatible with GCC's C++ (but not Microsoft's or anyone else's compiler). But there's more to it than that. C++ depends the vtable scheme also, which FB is not compatible with. So it's impossible in FB to interface with classes from the the C++ standard library such as std::string, and the lack of compatible templating means a lot of C++ code based on the standard library is inaccessible. Qt is impossible to access from FB also. Besides vtables there's also the object lifecycle, scoping, etc. The only way to make FB compatible with C++ is to make it a dialect of C++, which I don't think anyone here really wants to see.

Except for simple cases where FB's name mangling and class capabilities are enough to interface with a C++ library is through wrappers. A wrapper usually is written in C++ where it manages the C++ objects and their lifecycles, and exposes a simpler C-based interface that FB can talk to. This is what Joshy has done for those libraries.
Last edited by caseih on Feb 25, 2020 22:59, edited 1 time in total.
badidea
Posts: 2591
Joined: May 24, 2007 22:10
Location: The Netherlands

Re: C++ ABI Compatible?

Post by badidea »

caseih wrote:Except for simple cases where FB's name mangling and class capabilities are enough to interface with a C++ library is through wrappers. I'm not clear if Joshy's examples are wrappers or just header files. A wrapper usually is written in C++ where it manages the C++ objects and their lifecycles, and exposes a simpler C-based interface that FB can talk to.
I had a quick look at Joshy's "React Physics c++ wrapper". The wrapper looks a wrapper to me.
There is a reactphysics3d-c-wrapper.cpp and reactphysics3d-c-wrapper.h next to the original reactphysics3d code.
caseih
Posts: 2157
Joined: Feb 26, 2007 5:32

Re: C++ ABI Compatible?

Post by caseih »

Okay right. Thank you. I didn't read his original response carefully enough!
angros47
Posts: 2324
Joined: Jun 21, 2005 19:04

Re: C++ ABI Compatible?

Post by angros47 »

What are the main differences in vtable? What is that is NOT supposed to work, specifically?
caseih
Posts: 2157
Joined: Feb 26, 2007 5:32

Re: C++ ABI Compatible?

Post by caseih »

I'm not totally sure what you mean. FB has its own idea of a vtable. I have no idea what that is, nor how it differs from GCC C++'s vtable implementation. If FB were to adopt the GCC vtable structure as its own, then perhaps more compatibility with C++ would be possible. And maybe it's already pretty similar. A developer would have to comment on that.

vtables are specific to a compiler implementation as well. MS VC++ is not compatible with binary code produced by GCC g++ and vice versa.
angros47
Posts: 2324
Joined: Jun 21, 2005 19:04

Re: C++ ABI Compatible?

Post by angros47 »

Interesting .... I was trying to interface FreeBasic with a class written in C++ and compiled with GCC

Normal methods worked as expected, but virtual methods seemed to be "shifted by one". Basically, calling one virtual method from FreeBasic activates the previous virtual method in the C++ class

Looks like this happens because C++ implements two destructors, not just one:
https://eli.thegreenplace.net/2015/c-de ... or-delete/
https://stackoverflow.com/questions/179 ... -address-o
https://stackoverflow.com/questions/445 ... bly-output

https://itanium-cxx-abi.github.io/cxx-a ... l#obj-ctor
The entries for virtual destructors are actually pairs of entries. The first destructor, called the complete object destructor, performs the destruction without calling delete() on the object. The second destructor, called the deleting destructor, calls delete() after destroying the object. Both destroy any virtual bases; a separate, non-virtual function, called the base object destructor, performs destruction of the object but not its virtual base subobjects, and does not call delete().


Can anyone confirm this? How does FreeBasic work in the same situation?
Last edited by angros47 on May 24, 2020 23:53, edited 1 time in total.
angros47
Posts: 2324
Joined: Jun 21, 2005 19:04

Re: C++ ABI Compatible?

Post by angros47 »

Here is an example:

File class.cpp :

Code: Select all

  
#include <iostream> 
using namespace std; 
  
class base { 
public: 
base();
virtual ~base();
    virtual void print() ;
    virtual void show() ;
}; 
  
class derived : public base { 
public: 
derived();
virtual ~derived();
    void print() ;
    void show() ;
}; 

base::base(){
cout << "base constructor" << endl; 
}

base::~base(){
cout << "base destructor" << endl; 
}

void base::print() 
   { 
        cout << "print base class" << endl; 
    } 
  
    void base::show() 
    { 
        cout << "show base class" << endl; 
    } 

derived::derived(){
cout << "derived constructor" << endl; 
}

derived::~derived(){
cout << "derived destructor" << endl; 
}

    void derived::print() 
    { 
        cout << "print derived class" << endl; 
    } 
  
    void derived::show() 
    { 
        cout << "show derived class" << endl; 
    } 
compile with gcc -c class.cpp

test.bas

Code: Select all

#undef base

extern "c++"
type base extends object
	declare constructor
	declare virtual destructor
	declare virtual sub print()
	declare virtual sub show
end type

type derived extends base
	declare constructor
	declare virtual destructor
	declare sub print()
	declare sub show
end type

end extern


dim z as base ptr=new derived

'z->print
z->show
compile with fbc t.bas class.o -l stdc++

Calling z->print will activate the destructor. Calling z->show will return "print derived class"
caseih
Posts: 2157
Joined: Feb 26, 2007 5:32

Re: C++ ABI Compatible?

Post by caseih »

Interesting. And I confirm the bug. I don't know enough about FBC internals to even know where to look for this. I've looked over the source code a bit and see lots of references to the vtable and virtual calls. I suspect this is an impedence mismatch between FB and C++. From a casual glance, it looks to me like FB's own vtables (for FB code) are implemented in a similar--even nearly compatible--way as GCC. However it could be that FB only ever has one destructor. Not sure how hard it would be to make that conditional on extern c++ symbols. Also FBC would have to know how to call the right C++ destructor.
fxm
Moderator
Posts: 12110
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: C++ ABI Compatible?

Post by fxm »

FB inheritance polymorphism

Mechanism under the hood for an instance of the Type 'derived' (the above example):

Code: Select all

                                 vtbl (vtable)
                             .----------------------.
                         [-2]|    reserved (0)      |              RTTI info             Mangled Typename
                             |----------------------|       .-----------------------.       .--------.
   Instance of derived   [-1]|  Ptr to RTTI info    |--->[0]|     reserved (0)      |       | Length |
   .-----------------.       |----------------------|       |-----------------------|       | (ASCII)|
[0]|vptr: Ptr to vtbl|--->[0]|Ptr to derived.dtor() |   [+1]|Ptr to Mangled Typename|--->[0]|    &   |
   |_________________|       |----------------------|       |-----------------------|       |Typename|
                         [+1]|Ptr to derived.print()|   [+2]| Ptr to base RTTI info |---.   | (ASCII)|
                             |----------------------|       |_______________________|   |   |________|
                         [+2]|Ptr to derived.show() |   ________________________________|
                             |______________________|  | 
                                                       |             Base RTTI info
                                                       |       .----------------------------.
                                                       '--->[0]|        reserved (0)        |
                                                               |----------------------------|
                                                           [+1]|Ptr to Mangled base Typename|--->
                                                               |----------------------------|
                                                           [+2]|  Ptr to object RTTI info   |---.
                                                               |____________________________|   |
                                                                                                |
                                                                                                V
fxm
Moderator
Posts: 12110
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: C++ ABI Compatible?

Post by fxm »

Could you test this hacking workaround (for test.bas)?

Code: Select all

' .....
' .....

dim z as base ptr = new derived
cptr(any ptr ptr ptr, z)[0] += 1  '' Shifting by +1 step the vptr value of the instance z

z->print
z->show

delete z

sleep
In addition, is the 'derived::~derived()' then properly called by 'delete z'?


Warning:
With such a hacking workaround ('cptr(any ptr ptr ptr, z)[0] += 1'), access to RTTI info is broken (for example, bad behavior of the IS operator unless of temporarily reestablishing the right value of vptr).
angros47
Posts: 2324
Joined: Jun 21, 2005 19:04

Re: C++ ABI Compatible?

Post by angros47 »

There is a simpler workaround: in "base", you can add the line:

Code: Select all

	declare virtual destructor
	declare virtual sub dummy_destructor    'This function will fill a space in the vtable
Since the method "dummy_destructor" does not exist in the exported class, you have to add it in Freebasic, with something like:

Code: Select all

private sub base.dummy_destructor
'This sub can be empty, since the code here will never be executed. 
'Calling dummy_destructor by hand will actually activate the destructor from the C++ code
end sub
Note: the bug happens only if the instance is created by the C++ code: if there is no constructor in C++, FreeBasic will create the class instance, and so the vtable will be made according to FreeBasic rule
fxm
Moderator
Posts: 12110
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: C++ ABI Compatible?

Post by fxm »

I thought about it too, but I don't know whether to add this line ('declare virtual sub dummy_destructor') before or after 'declare virtual destructor'.
This is why I also asked my last question: "Is the 'derived::~derived()' then properly called by 'delete z'?"
Post Reply