C++ OOP library and Interface

Just starting out? Need help? Post your questions and find answers here.
User avatar
StarBootics
Addict
Addict
Posts: 1006
Joined: Sun Jul 07, 2013 11:35 am
Location: Canada

C++ OOP library and Interface

Post by StarBootics »

Hello everyone,

I'm currently looking to use C++ OOP libraries directly in PureBasic using the Interface/EndInterface feature but I can't find a working example.

I have take a look at this topic and also few others but nothing seems to work at least under Linux.

The best will be a complete tutorial explaining everything from the C++ source code, compiling it into a *.so library to Import it Into PureBasic and finally using it in an actual program.

Thanks in advance for any advices.

Best regards
StarBootics
The Stone Age did not end due to a shortage of stones !
User avatar
StarBootics
Addict
Addict
Posts: 1006
Joined: Sun Jul 07, 2013 11:35 am
Location: Canada

Re: C++ OOP library and Interface

Post by StarBootics »

More than 100 view, no answer yet ...

A small C++ class source code, the header file (time.h) :

Code: Select all

#ifndef TIME_H
#define TIME_H

class Time
{
public:

    Time();
    void Update(int, int ,int);
    void SetHour(int);
    void SetMinute(int);
    void SetSecond(int);
    int GetHour() const;
    int GetMinute() const;
    int GetSecond() const;

    ~Time();

private:
    int hour;
    int minute;
    int second;
};

#endif // TIME_H
The corresponding time.cpp file :

Code: Select all

#include <iostream>
#include "time.h"

Time::Time()
{
    hour = minute = second = 0;
}

void Time::Update(int h, int m, int s)
{
    hour    = ( h >=0 && h < 24 ) ? h : 0;
    minute  = ( m >=0 && m < 60 ) ? m : 0;
    second  = ( s >=0 && s < 60 ) ? s : 0;
}

void Time::SetHour(int h)
{
    hour = ( h >= 0 && h < 24 ) ? h : 0;
}

void Time::SetMinute(int m)
{
    minute = ( m >= 0 && m < 60 ) ? m : 0;
}

void Time::SetSecond(int s)
{
    second = ( s >= 0 && s < 60 ) ? s : 0;
}

int Time::GetHour() const
{
    return hour;
}

int Time::GetMinute() const
{
    return minute;
}

int Time::GetSecond() const
{
    return second;
}

Time::~Time()
{
    hour = minute = second = 0; // Maybe over kill in this case
}
So the question how to make a *.so file out of this to be imported into PureBasic later on ?
Any modifications to the original code ?

Thanks
StarBootics
Last edited by StarBootics on Sat Oct 01, 2016 5:43 pm, edited 1 time in total.
The Stone Age did not end due to a shortage of stones !
User avatar
Shield
Addict
Addict
Posts: 1021
Joined: Fri Jan 21, 2011 8:25 am
Location: 'stralia!
Contact:

Re: C++ OOP library and Interface

Post by Shield »

My answer after having done that many years ago: don't do it.
If you want to use the functions in PB, either code them in a procedural style or wrap your objects in functions.

You will have a hard time getting this right because of the way compilers allocate / optimize the class layout.

If you insist on doing it: create a separate create_time_object() function that returns "new Time()",
add a release method that calls "delete this", and mark all methods as "virtual" (this is probably
the most important step, because without the keyword virtual, the method calls are just syntactical sugar
and nothing is actually being written into the virtual table that is required for interfaces to work).

You then might have some luck getting it to work, but you probably have to fiddle around with correct layouting
and call methods (e.g. mark them as __stdcall).
Image
Blog: Why Does It Suck? (http://whydoesitsuck.com/)
"You can disagree with me as much as you want, but during this talk, by definition, anybody who disagrees is stupid and ugly."
- Linus Torvalds
User avatar
StarBootics
Addict
Addict
Posts: 1006
Joined: Sun Jul 07, 2013 11:35 am
Location: Canada

Re: C++ OOP library and Interface

Post by StarBootics »

Shield wrote:My answer after having done that many years ago: don't do it.
If you want to use the functions in PB, either code them in a procedural style or wrap your objects in functions.
The problem is this : I would like to use Open Cascade in PureBasic and Open Cascade is not something I will code my self.

Even if it's possible to recode an entire geometry kernel in PureBasic, I don't want to do that. And if I need to re-code something complex in C, it will probably better to Switch to C++ all together.

Thanks anyway.
StarBootics
The Stone Age did not end due to a shortage of stones !
User avatar
idle
Always Here
Always Here
Posts: 6095
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: C++ OOP library and Interface

Post by idle »

As far as I remember you can only use C++ libs that have exported their classes functions as pure virtual functions
or wrapped in com.

The calling convention is different between x86 and x64
x86 windows uses thiscall where the object pointer is passed in ecx as first parameter, so you would need a patch or macro to do it
x86 linux uses fastcall, so it should work via interfaces unchanged
and x64 windows and linux both use fastcall

try user_russian's patch for windows x86
http://www.purebasic.fr/english/viewtop ... 12&t=54604
Windows 11, Manjaro, Raspberry Pi OS
Image
User avatar
StarBootics
Addict
Addict
Posts: 1006
Joined: Sun Jul 07, 2013 11:35 am
Location: Canada

Re: C++ OOP library and Interface

Post by StarBootics »

Mainly I'm under Linux x64 so ...

Anyway I will be in training for the next two day, I will make some test next Saturday.

Thanks
StarBootics
The Stone Age did not end due to a shortage of stones !
User avatar
StarBootics
Addict
Addict
Posts: 1006
Joined: Sun Jul 07, 2013 11:35 am
Location: Canada

Re: C++ OOP library and Interface

Post by StarBootics »

Hello everyone,

I'm trying to compile a library but it's not working since I'm not as good at C++ as I'm with PureBasic so I'm asking for help.

This the modified header file (time.h) :

Code: Select all

#ifndef TIME_H
#define TIME_H

class Time
{
public:

    Time();
    virtual void Update(int, int , int);
    virtual void SetHour(int);
    virtual void SetMinute(int);
    virtual void SetSecond(int);
    virtual int GetHour() const;
    virtual int GetMinute() const;
    virtual int GetSecond() const;

    virtual ~Time();

private:
    int hour;
    int minute;
    int second;
};

#endif // TIME_H
The time.cpp :

Code: Select all

#include "time.h"

Time::Time()
{
    hour = minute = second = 0;
}

void Time::Update(int h, int m, int s)
{
    hour    = ( h >=0 && h < 24 ) ? h : 0;
    minute  = ( m >=0 && m < 60 ) ? m : 0;
    second  = ( s >=0 && s < 60 ) ? s : 0;
}

void Time::SetHour(int h)
{
    hour = ( h >= 0 && h < 24 ) ? h : 0;
}

void Time::SetMinute(int m)
{
    minute = ( m >= 0 && m < 60 ) ? m : 0;
}

void Time::SetSecond(int s)
{
    second = ( s >= 0 && s < 60 ) ? s : 0;
}

int Time::GetHour() const
{
    return hour;
}

int Time::GetMinute() const
{
    return minute;
}

int Time::GetSecond() const
{
    return second;
}

Time::~Time()
{
    hour = minute = second = 0; // Maybe over kill in this case
}
The library main file :

Code: Select all

#include "time.h"

extern "C"
{

    Time* CreateTimeInterface()
    {
        return new Time;
    }

}
I'm under Linux (Ubuntu Gnome16.04 x64), I'm using Code::Blocks 16.01 x64 with gcc/g++ compiler.

This is the build log
-------------- Build: Debug in time (compiler: GNU GCC Compiler)---------------

g++ -shared obj/Debug/main.o obj/Debug/time.o -o bin/Debug/libtime.so
/usr/bin/ld: obj/Debug/main.o: relocalisation de R_X86_64_32 en vertu de «__gxx_personality_v0» ne peut être utilisée lors de la création d'un objet partagé; recompiler avec -fPIC
obj/Debug/main.o : erreur lors de l'ajout de symboles : Mauvaise valeur
collect2: error: ld returned 1 exit status
Process terminated with status 1 (0 minute(s), 0 second(s))
1 error(s), 0 warning(s) (0 minute(s), 0 second(s))
I'm stuck there, any help will be welcome.

Best regards
StarBootics
The Stone Age did not end due to a shortage of stones !
User avatar
Shield
Addict
Addict
Posts: 1021
Joined: Fri Jan 21, 2011 8:25 am
Location: 'stralia!
Contact:

Re: C++ OOP library and Interface

Post by Shield »

An error message in a language I understand would be helpful. :)

Try to use make instead of CodeBlocks so you have more control over what the compiler is doing.
I used your time.h and time.cpp, but put the CreateTimeInterface directly into time.cpp.
Using the following make code, it compiled fine as a static library. I didn't test it, though, because
I don't have PB installed and it's 3:45am :)

Code: Select all

all: libtime.a

time.o: time.cpp
	gcc -c time.cpp -o time.o

libtime.a: time.o
	ar rcs libtime.a time.o
Time::~Time()
{
hour = minute = second = 0; // Maybe over kill in this case
}
It's not only overkill, it's completely useless. No need for a destructor.
Deletion of the object by the caller should be done with a Release() function.
Image
Blog: Why Does It Suck? (http://whydoesitsuck.com/)
"You can disagree with me as much as you want, but during this talk, by definition, anybody who disagrees is stupid and ugly."
- Linus Torvalds
User avatar
StarBootics
Addict
Addict
Posts: 1006
Joined: Sun Jul 07, 2013 11:35 am
Location: Canada

Re: C++ OOP library and Interface

Post by StarBootics »

Shield wrote:An error message in a language I understand would be helpful. :)
relocalisation de R_X86_64_32 en vertu de «__gxx_personality_v0» ne peut être utilisée lors de la création d'un objet partagé; recompiler avec -fPIC
Translation : relocation of R_X86_64_32 under «__gxx_personality_v0» can't be used during the creation of a shared object; recompile with -fPIC
obj/Debug/main.o : erreur lors de l'ajout de symboles : Mauvaise valeur
Translation : obj/Debug/main.o : error when adding symbols : Bad value

About the Destructor, ok for the provided example but what will happen if the "Class" allocate dynamic stuff being freed by the destructor ? My guess, we will end up with a memory leak if we don't use the class destructor. So I prefer to have the most general example as possible and include a class Destructor even if it's not needed.

Best regards
StarBootics
The Stone Age did not end due to a shortage of stones !
User avatar
idle
Always Here
Always Here
Posts: 6095
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: C++ OOP library and Interface

Post by idle »

did you try adding -fPic to your command line to gcc it's needed to make a shared object
Windows 11, Manjaro, Raspberry Pi OS
Image
User avatar
StarBootics
Addict
Addict
Posts: 1006
Joined: Sun Jul 07, 2013 11:35 am
Location: Canada

Re: C++ OOP library and Interface

Post by StarBootics »

idle wrote:did you try adding -fPic to your command line to gcc it's needed to make a shared object
I have finally find where to add this directive to the compiler and it work. This test was successful for the "Debug" version of the lib. For the release version, even if I add -fPIC flag I still have the same error telling about -fPIC flag.

As soon as I have it working out I will take time to create a complete tutorial explaining everything how to do it.

@Shield : Can you be more specific about the Release function.

EDIT : Finally I got it to work for both "Debug" and "Release"

Best regards
StarBootics
The Stone Age did not end due to a shortage of stones !
User avatar
Shield
Addict
Addict
Posts: 1021
Joined: Fri Jan 21, 2011 8:25 am
Location: 'stralia!
Contact:

Re: C++ OOP library and Interface

Post by Shield »

StarBootics wrote: @Shield : Can you be more specific about the Release function.
You need something like this:

Code: Select all

void Time::Release()
{
    delete this;
}
Be careful about that though, as this only works with objects allocated with plain "new".
Ensure that nothing touches the object after running this function.
StarBootics wrote: About the Destructor, ok for the provided example but what will happen if the "Class" allocate dynamic stuff being freed by the destructor ? My guess, we will end up with a memory leak if we don't use the class destructor. So I prefer to have the most general example as possible and include a class Destructor even if it's not needed.
Don't include a destructor if it's not needed, just like you're not including random empty functions in your program.
You only need to specify a destructor if you have allocated resources that will not be freed automatically (such as raw
pointers to the heap or things like file handles, for example). But sure, for the purpose of demonstration it's fine.

Also, the destructor should not be virtual here because that will result in another entry in the virtual table.
(However, it might be possible to include the destructor as the first virtual function instead of a Release function,
but this working is just all as fuzzy as the rest we're trying to do here).

I'd still recommend you wrap the member functions you want access to into regular C functions.
Doing so will get rid of all the hassle and ensures that your program will run correctly, independent of platform and compiler.
Image
Blog: Why Does It Suck? (http://whydoesitsuck.com/)
"You can disagree with me as much as you want, but during this talk, by definition, anybody who disagrees is stupid and ugly."
- Linus Torvalds
User avatar
StarBootics
Addict
Addict
Posts: 1006
Joined: Sun Jul 07, 2013 11:35 am
Location: Canada

Re: C++ OOP library and Interface

Post by StarBootics »

Hello everyone,

This is an example about how to Interface with a C++ Class. I'm using Code::Blocks V16.01 with gcc compiler under Ubuntu Gnome 16.04 x64.
At this point I can't tell if this will work under x86 Linux or under other operating system, you will have to make some test for that.

https://www.dropbox.com/s/2ry3xi4pighlk ... e.zip?dl=0

The provided package contain the C++ library source code, the Code::Blocks project file and a PureBasic example of use.
If everything work as expected you should see this in the PureBasic debugger output when you try the provided example :

Code: Select all

21
6
30
The Time Object is invalid !
Thanks to all programmers who have provided help, it's greatly appreciated.

Best regards
StarBootics
The Stone Age did not end due to a shortage of stones !
User avatar
Shield
Addict
Addict
Posts: 1021
Joined: Fri Jan 21, 2011 8:25 am
Location: 'stralia!
Contact:

Re: C++ OOP library and Interface

Post by Shield »

Great that you got it working. :)

One thing I saw while quickly looking over it was that you now have a Release function and a destructor.
That doesn't quite work, because upon calling Release(), the destructor will also be called (and thus needlessly zeroing out the values twice).

I recommend not to include a destructor at all and instead free your resources in Release().
The destructor cannot be called directly from PB anyway. :)
Image
Blog: Why Does It Suck? (http://whydoesitsuck.com/)
"You can disagree with me as much as you want, but during this talk, by definition, anybody who disagrees is stupid and ugly."
- Linus Torvalds
User avatar
StarBootics
Addict
Addict
Posts: 1006
Joined: Sun Jul 07, 2013 11:35 am
Location: Canada

Re: C++ OOP library and Interface

Post by StarBootics »

Shield wrote:Great that you got it working. :)

One thing I saw while quickly looking over it was that you now have a Release function and a destructor.
That doesn't quite work, because upon calling Release(), the destructor will also be called (and thus needlessly zeroing out the values twice).

I recommend not to include a destructor at all and instead free your resources in Release().
The destructor cannot be called directly from PB anyway. :)
I think I got it with a Virtual Destructor and without a Release() method inside the class it self. The class it's self without a Virtual Destructor give me a warning about Polymorphic stuff. So the library main file look like this instead :

Code: Select all

// The functions contained in this file are pretty dummy
// and are included only as a placeholder. Nevertheless,
// they *will* get included in the shared library if you
// don't remove them :)
//
// Obviously, you 'll have to write yourself the super-duper
// functions to include in the resulting library...
// Also, it's not necessary to write every function in this file.
// Feel free to add more files in this project. They will be
// included in the resulting library.

#include "time.h"

extern "C"
{

    Time* CreateTimeInterface()
    {
        return new Time;
    }

    int DeleteTimeInterface(const Time * ToBeDeleted)
    {
        delete ToBeDeleted;
        return 0;
    }

}
That way, no warning during the compilation of the library and working in PureBasic flawlessly.

This is the second version of the demonstration : https://www.dropbox.com/s/uh5hdcipudpe9 ... 2.zip?dl=0

Best regards
StarBootics
The Stone Age did not end due to a shortage of stones !
Post Reply