Advanced Question Help with Static Libraries (.lib) and classes

Mr Martian

Orbinaut/Addon dev/Donator
Addon Developer
Donator
Joined
Jun 6, 2012
Messages
288
Reaction score
67
Points
28
Location
Sydney, Australia, Earth, Sol
Website
www.orbithangar.com
Hi All,

I have made a static library (.lib) which I am hoping to release later as a part of an SDK.

the library defines a class type, which can be then be called by vessel classes.

My issue is, I would like to be able to interface with the class in the same way as the example below:
Code:
    VESSEL *v = oapiGetVesselInterface(GetHandle());
    v->DoSomething();

oapiGetVesselInterface allows me to get the interface for a specific class object.

Now, what I would optimally like to is interact with my custom class, but the instance called for a specific vessel.

I am having so much difficulty with this and am going in circles. If anyone could offer any help I would really appreciate it.

happy to share my code if that helps too.

Thanks in advance!


UPDATE:

In my .lib project's header, I tried declaring the class like this:
C++:
class MYCLASS : public VESSEL {
public:
    
};

This allows me to interact with MYCLASS, but when I compile a vessel project using this library, I get the following error:
error C2512: 'MYCLASS' : no appropriate default constructor available
 
Last edited:

n72.75

Move slow and try not to break too much.
Orbiter Contributor
Addon Developer
Tutorial Publisher
Donator
Joined
Mar 21, 2008
Messages
2,687
Reaction score
1,337
Points
128
Location
Saco, ME
Website
mwhume.space
Preferred Pronouns
he/him
Does adding an empty

MYCLASS::MYCLASS() constructor definition help anything?
 

Blake

Addon Developer
Addon Developer
Joined
Mar 9, 2009
Messages
225
Reaction score
104
Points
58
Location
Lehi, Utah
If VESSEL is the VESSEL from Orbiter, it has a constructor that looks like this: VESSEL (OBJHANDLE hVessel, int fmodel = 1);

You will need a similar constructor to pass those values to the base.

I may not understand what you are trying to do however.
 

jedidia

shoemaker without legs
Addon Developer
Joined
Mar 19, 2008
Messages
10,842
Reaction score
2,105
Points
203
Location
between the planets
I am having serious trouble to understand what you want to do.

Your words suggest that you have a static lib with a class with which you want to interact form a vessel module:
the library defines a class type, which can be then be called by vessel classes.
However, your code suggest that you are trying to make a class that IS a vessel:
In my .lib project's header, I tried declaring the class like this:
C++:
class MYCLASS : public VESSEL {
public:

};

The first one is possible with a static lib. The second one isn't. You can't make a vessel in a static lib. Vessels require dynamic libs. All the above suggestions also assume that you are building a dynamic lib. You'll have to better explain what your project and use case is in order for us to effectively help, I'm afraid.

(Also, when writing a new vessel module, you should really inherit the latest vessel version available, which at the moment would be VESSEL4, I think).
 

Mr Martian

Orbinaut/Addon dev/Donator
Addon Developer
Donator
Joined
Jun 6, 2012
Messages
288
Reaction score
67
Points
28
Location
Sydney, Australia, Earth, Sol
Website
www.orbithangar.com
Hi guys,

Thanks for the replies, I have tried a mix-match of different constructors etc.

in a nutshell I am basically trying to achieve this:

C++:
    OBJHANDLE obj = GetDockStatus(dh_main);
    VESSEL *v = oapiGetVesselInterface(obj);

    MYCLASS *mc;
    mc = &v;

Basically a way that I can get the class intercface for my class I definied in the .lib, FOR a specific vessel.

Thank you for helping me, sorry I don't think I'm doing the best job of explaining it...
 

Mr Martian

Orbinaut/Addon dev/Donator
Addon Developer
Donator
Joined
Jun 6, 2012
Messages
288
Reaction score
67
Points
28
Location
Sydney, Australia, Earth, Sol
Website
www.orbithangar.com
I am having serious trouble to understand what you want to do.

Your words suggest that you have a static lib with a class with which you want to interact form a vessel module:

However, your code suggest that you are trying to make a class that IS a vessel:


The first one is possible with a static lib. The second one isn't. You can't make a vessel in a static lib. Vessels require dynamic libs. All the above suggestions also assume that you are building a dynamic lib. You'll have to better explain what your project and use case is in order for us to effectively help, I'm afraid.

(Also, when writing a new vessel module, you should really inherit the latest vessel version available, which at the moment would be VESSEL4, I think).
Hi Jedidia,

Thanks for the reply. Yes you are correct, and yes you are right that method was defining the class as a vessel in the static lib... bad move on my part.

I am playing around with the idea of creating a UMmu-like addon. I want to be able to call on the class defined in the .lib inside vessel classes. This I have achieved no issues.

But I would like to then call functions of my class in different vessels (as in below, code doesnt work but it may help explain my goal better):
C++:
    OBJHANDLE obj = GetDockStatus(dh_main);
    VESSEL *v = oapiGetVesselInterface(obj);

    MYCLASS *mc;
    mc = &v;

I want a method by which I can interact with specific instances of my class called in seperate vessels.

I hope this makes a bit more sense, but again happy to provide more code snippets etc.

thank you again
 

jedidia

shoemaker without legs
Addon Developer
Joined
Mar 19, 2008
Messages
10,842
Reaction score
2,105
Points
203
Location
between the planets
Right, that makes more sense. So you're aiming at creating a vessel class that contains the abstraction layer for your functionality, and provide it to other developers so they can build their stuff on top of it. Got it.

The first thing I'd like you to consider has nothing to do with your code, but with the architecture you're aiming at itself: Implementing your own abstraction layer on top of a VESSEL interface makes sense if you're keeping it in your own project. However, doing it with the intent of making an SDK has serious issues. It makes existing add-ons very difficult to support your SDK if they want to, and may make it virtually impossible to support multiple different implementations at the same time.
I suspect that your project can be realised through composition rather through inheritance. It is vastly more simple to add functionality to a project by composition rather than inheritance.
Things might be a bit different if C++ had a concept of purely abstract interfaces, but even then it would be vastly preferable to implement the interface through delegation rather than directly. It just results in easier modularity.

In that case, you could send a broadcast message to all vessels (I forget the API call, long time no use, but the VESSEL4 interface does have generic messaging capability), and vessels that use the SDK can send back the class with the necessary functionality, without touching any of the VESSEL interfaces directly.

That said, what you want to do should work. The code you posted will work but is highly unsafe, because you cannot know whether the docked vessel is actually an instance of your class or not. As soon as a vessel is docked that is not, your code will crash and burn. You should use something like dynamic_cast() instead of of doing unsafe pointer wizzardry...
 

Mr Martian

Orbinaut/Addon dev/Donator
Addon Developer
Donator
Joined
Jun 6, 2012
Messages
288
Reaction score
67
Points
28
Location
Sydney, Australia, Earth, Sol
Website
www.orbithangar.com
Right, that makes more sense. So you're aiming at creating a vessel class that contains the abstraction layer for your functionality, and provide it to other developers so they can build their stuff on top of it. Got it.

The first thing I'd like you to consider has nothing to do with your code, but with the architecture you're aiming at itself: Implementing your own abstraction layer on top of a VESSEL interface makes sense if you're keeping it in your own project. However, doing it with the intent of making an SDK has serious issues. It makes existing add-ons very difficult to support your SDK if they want to, and may make it virtually impossible to support multiple different implementations at the same time.
I suspect that your project can be realised through composition rather through inheritance. It is vastly more simple to add functionality to a project by composition rather than inheritance.
Things might be a bit different if C++ had a concept of purely abstract interfaces, but even then it would be vastly preferable to implement the interface through delegation rather than directly. It just results in easier modularity.

In that case, you could send a broadcast message to all vessels (I forget the API call, long time no use, but the VESSEL4 interface does have generic messaging capability), and vessels that use the SDK can send back the class with the necessary functionality, without touching any of the VESSEL interfaces directly.

That said, what you want to do should work. The code you posted will work but is highly unsafe, because you cannot know whether the docked vessel is actually an instance of your class or not. As soon as a vessel is docked that is not, your code will crash and burn. You should use something like dynamic_cast() instead of of doing unsafe pointer wizzardry...
Hi Jedidia,

Thank you for the detailed response! Everything you have explained makes sense to me, but I'm afraid my C++ skills are probably not up to scratch to be able to implement this. I may have to put this project on the backburner for now....

I've been going round in circles for the past couple days with this. As you mentioned, pointers are probably not the best way to go since I could cause a crash if a vessel instance returns no pointer (does not use my addon).

But from what I have experimented with thus far I cannot seem to create a pointer at all. is it even possible to have a function in my .lib called by one vessel class to interact with an instance of my function called in another? Sorry if now my questions seem incoherant. I am just struggling to wrap my head around it.

I tried to create a pointer in my .lib like this:
C++:
MYCLASS *MYCLASS::GetMYCLASSInterface ()
{
    return this;
}

which I could then call in vessel classes (shuttlePB for example) as such:
C++:
MYCLASS *m = mclass.GetMYCLASSInterface();

however this returns only the class instantiated in the vessel.

I would at least like to find some way to interact with ALL instances called accross all vessels. At least that way I will have something to experiment/learn with.


Apologies again for the rambling, this is the first time I have worked with static libraries. Making simple functions to be called only by the instantiating vessel is straightforward enough for me, but beyond that is a learning curve.

Thanks again
 

jedidia

shoemaker without legs
Addon Developer
Joined
Mar 19, 2008
Messages
10,842
Reaction score
2,105
Points
203
Location
between the planets
I would at least like to find some way to interact with ALL instances called accross all vessels. At least that way I will have something to experiment/learn with.
Your initial approach was fine for that, you just didn't account for the possibility that some vessels might not be of your class (which is almost a certainty). What I'd recommend you to do is to get the base interface, and then dynamic_cast() it to your class. In case it isn't of your class, you'll end up with a null-pointer, so you will know that that isn't one of the vessels you are looking for. Something like this (note that I'm only using C++ rather irregularly these days, so there may be syntax errors in here):

C++:
// Retrieve all vessels that inherit MYVESSEL currently in the simulation
unsigned int vesselCount = oapiGetVesselCount();
vector<MYVESSEL*> myVessels;
for (unsigned int i = 0; i < vesselCount; ++i) {
    OBJHANDLE vHandle = oapiGetVesselByIndex(i);
    VESSEL* vInterface = oapiGetVesselInterface(vHandle);
    MYVESSEL* myVessel = dynamic_cast<MYVESSEL*>(vInterface);
    if (myVessel != NULL) {
        myVessels.push(myVessel);
    }
}
 

dbeachy1

O-F Administrator
Administrator
Orbiter Contributor
Addon Developer
Donator
Beta Tester
Joined
Jan 14, 2008
Messages
9,214
Reaction score
1,560
Points
203
Location
VA
Website
alteaaerospace.com
Preferred Pronouns
he/him
Another possible approach if you don't have dynamic_cast checks enabled in your build settings is to simply check the vessel's class name before you cast it; e.g., you could do this in the loop above:

Code:
const OBJHANDLE hVesselHandle = oapiGetVesselByIndex(i);
const VESSEL *pVessel = oapiGetVesselInterface(hVesselHandle);

if (pVessel && !strcmp(pVessel->GetClassName(), "MyVesselClassName"))  // TODO: replace "MyVesselClassName" here with your vessel's actual class name (i.e., your vessel's DLL filename minus the ".dll" part).
    myVessels.push(<MYVESSEL *>pVessel);
 

jedidia

shoemaker without legs
Addon Developer
Joined
Mar 19, 2008
Messages
10,842
Reaction score
2,105
Points
203
Location
between the planets
Right, I completely forgot that method existed... ?‍♂️
dynamic_cast is faster, though. Not that I'd expect it to matter, unless that thing runs every frame.
 

Mr Martian

Orbinaut/Addon dev/Donator
Addon Developer
Donator
Joined
Jun 6, 2012
Messages
288
Reaction score
67
Points
28
Location
Sydney, Australia, Earth, Sol
Website
www.orbithangar.com
Your initial approach was fine for that, you just didn't account for the possibility that some vessels might not be of your class (which is almost a certainty). What I'd recommend you to do is to get the base interface, and then dynamic_cast() it to your class. In case it isn't of your class, you'll end up with a null-pointer, so you will know that that isn't one of the vessels you are looking for. Something like this (note that I'm only using C++ rather irregularly these days, so there may be syntax errors in here):

Thank you jedidia!

That makes alot of sense now I can see it like that! @dbeachy1 thank you too, you rmethod also makes sense to me.

One thing I have found though, I cannot seem to use dynamic_cast, I get the error: " 'VESSEL' is not a polymorphic type "

For both your method's though, apparently "push" is not a member of vector... I can modify this to make use of "push_back", however @dbeachy1 your method also invokes an error of
Code:
"std::vector<_Ty>::push_back' : no overloaded function takes 0 arguments"

I don't understand this since I am proving an argument. Could this be due to my compiler settings? or perhaps this worked in VS 2005? has microsoft made 'push' obsolete perhaps?

Thank you again!
 
Last edited:

n72.75

Move slow and try not to break too much.
Orbiter Contributor
Addon Developer
Tutorial Publisher
Donator
Joined
Mar 21, 2008
Messages
2,687
Reaction score
1,337
Points
128
Location
Saco, ME
Website
mwhume.space
Preferred Pronouns
he/him
Are you casting to vessel or to your derived type?

What are you trying to push onto your vector?
 

Mr Martian

Orbinaut/Addon dev/Donator
Addon Developer
Donator
Joined
Jun 6, 2012
Messages
288
Reaction score
67
Points
28
Location
Sydney, Australia, Earth, Sol
Website
www.orbithangar.com
Are you casting to vessel or to your derived type?

What are you trying to push onto your vector?
Hi n72.5 thanks for the reply.

At this point I am not sure if casting is the best way to go...

it would proably be easier if I show what I am doing. I have followed jedidia's advice and used his suggested code. I had issues with dynamic_cast as for some reason iot seems to think Orbiter's VESSEL is not polymorphic, so I adapted with reinterpret_cast.

This is what I have in my .lib:

C++:
BOOL OEMUFUNCTION::TransferToShip (OBJHANDLE h)
{
unsigned int vesselCount = oapiGetVesselCount();
    vector <OEMUFUNCTION*> oemu_s;
    for (unsigned int i = 0; i < vesselCount; ++i) {
        OBJHANDLE vHandle = oapiGetVesselByIndex(i);
        VESSEL* v = oapiGetVesselInterface(vHandle);
        OEMUFUNCTION* oemu = reinterpret_cast<OEMUFUNCTION*>(v);
        if (oemu != NULL) {
            oemu->TransferSelectedCrew();
            oemu_s.push_back(oemu);
        }
    }
}

So this function "TransferToShip (OBJHANDLE h)" is in my static library. I want to be able to call this from a VESSEL class to transfer from one ship to another, if that makes sense. basically I want to be able to interact with different instances of my class accross orbiters simulation. (note in the vessel class' header an instance of OEMUFUNCTION is created like so:
C++:
class ShuttlePB: public VESSEL3 {
private:
    OEMUFUNCTION oemu;

transferToShip has an OBJHANDLE argument as I originally was designing it to cross check with a privately storred handle so that the user can choose the destination vessel by inputting its handle, if that makses sense.

This code above causes a crash though. I replaced the section:
C++:
        if (oemu != NULL) {
            oemu->TransferSelectedCrew();
            oemu_s.push_back(oemu);
        }

with a simple debug string to print in "if (oemu != NULL)" that worked, so I am not sure the issue here.

At this point I would love to just be able to cycle through all instances of my class, and cross check the OBJHANDLE, in something like this:

C++:
for (int i = 0; i < total_instantiated; i++) { 
    OEMUFUNCTION *oemu = i;
    if (h == myprivatehandle) oemu->TransferSelectedCrew();
    else continue;
}

But I know this doesnt seem possible...

Thanks again all for taking the time to help me with this.
 

Blake

Addon Developer
Addon Developer
Joined
Mar 9, 2009
Messages
225
Reaction score
104
Points
58
Location
Lehi, Utah

That post, and thread (its short) may be helpful. Summary, VESSEL is not polymorphic and will not work with dynamic_cast. VESSEL2 is, so check the version and cast. If you don't check for VESSEL2 you will get a runtime error if you try to cast to something that will not work.
 

jedidia

shoemaker without legs
Addon Developer
Joined
Mar 19, 2008
Messages
10,842
Reaction score
2,105
Points
203
Location
between the planets
Summary, VESSEL is not polymorphic and will not work with dynamic_cast. VESSEL2 is
Oh... I wasn't aware there was a fundamental break between VESSEL and VESSEL2. Whoops.

For both your method's though, apparently "push" is not a member of vector... I can modify this to make use of "push_back"
Remember when I told you that there might be syntax errors in my example because my C++ is a bit rusty? Yeah, that was one of those...
 

Mr Martian

Orbinaut/Addon dev/Donator
Addon Developer
Donator
Joined
Jun 6, 2012
Messages
288
Reaction score
67
Points
28
Location
Sydney, Australia, Earth, Sol
Website
www.orbithangar.com

That post, and thread (its short) may be helpful. Summary, VESSEL is not polymorphic and will not work with dynamic_cast. VESSEL2 is, so check the version and cast. If you don't check for VESSEL2 you will get a runtime error if you try to cast to something that will not work.
Thank you Blake that is helpful, I did no realise conversion to VESSEL2 would have been so simple!
 

Mr Martian

Orbinaut/Addon dev/Donator
Addon Developer
Donator
Joined
Jun 6, 2012
Messages
288
Reaction score
67
Points
28
Location
Sydney, Australia, Earth, Sol
Website
www.orbithangar.com
Oh... I wasn't aware there was a fundamental break between VESSEL and VESSEL2. Whoops.


Remember when I told you that there might be syntax errors in my example because my C++ is a bit rusty? Yeah, that was one of those...
Ah yes I do recall, despite in that thread you were the one who wrote that code for your IMS class! that was back in 2013 though;)

Well I have my code compiling now, but it invokes a runtime error:
1649676878419.png

This is what I have:
C++:
unsigned int vesselCount = oapiGetVesselCount();
    vector <OEMUFUNCTION*> myVessels;
    for (unsigned int i = 0; i < vesselCount; ++i) {
        OBJHANDLE vHandle = oapiGetVesselByIndex(i);
        VESSEL* vInterface = oapiGetVesselInterface(vHandle);
        OEMUFUNCTION* myVessel = dynamic_cast<OEMUFUNCTION*>((VESSEL2*)vInterface);
        if (myVessel != NULL) {
            return TRUE;
        }
    }

Even just a simple return causes this crash... I've tried have a constructor and no constructor for my .lib, always this same error. @jedidia do you recall, for your IMS class was that a static library? or a VESSEL class? how did you go about instatiating it?

Thank you!
 

Blake

Addon Developer
Addon Developer
Joined
Mar 9, 2009
Messages
225
Reaction score
104
Points
58
Location
Lehi, Utah
You still need to check that the vessel you are casting is indeed (at least) version 2. Not all stock vessels are VESSEL2, so they will throw a runtime error if you try and dynamic_cast it to VESSEL2.

if (vInterface->Version() >= 2) { ...do dynamic cast } else {bail}
 

Mr Martian

Orbinaut/Addon dev/Donator
Addon Developer
Donator
Joined
Jun 6, 2012
Messages
288
Reaction score
67
Points
28
Location
Sydney, Australia, Earth, Sol
Website
www.orbithangar.com
You still need to check that the vessel you are casting is indeed (at least) version 2. Not all stock vessels are VESSEL2, so they will throw a runtime error if you try and dynamic_cast it to VESSEL2.

if (vInterface->Version() >= 2) { ...do dynamic cast } else {bail}
Hi Blake,

Thank you this makes sense. Does this mean my class can only be interacted with this way if it is instatiated in VESSEL2 calss vessels though?
 
Top