# Advanced QuestionHelp with Static Libraries (.lib) and classes

#### jedidia

##### shoemaker without legs
Addon Developer
Could you please post your declaration of the OEMUFUNCTION class? Going on what you write, it sounds like there's a fundamental misunderstanding happening somewhere.

#### Mr Martian

##### Orbinaut/Addon dev/Donator
Addon Developer
Donator
Could you please post your declaration of the OEMUFUNCTION class? Going on what you write, it sounds like there's a fundamental misunderstanding happening somewhere.
Hi Jedidia,

Okay so this is the structure I have:

• I have a static library, which contains the class "OEMUFUNCTION"
• This library, and its header can be included when compiling vessel classes (much like Dansteph's UMmuSDK.lib and UMmuSDK.h when adding UMmu to a vessel class)

this is my declaration of OEMUFUNCTION in the .lib project:
C++:
using std::vector;   // added this as only way to make use of vector...

class OEMUFUNCTION /*: public VESSEL*/ {
public:
//  constructor / destructor (commented out as has no effect???)
//    OEMUFUNCTION();
//    ~OEMUFUNCTION();

BOOL EVASelectedCrew ();

// ... a bunch of other functions to call from vessel classes

private:
int maxseats;
int selected;
int numberonship;
// ... a bunch of other private variables (specific for each vessel class instantiating OEMUFUNCTION)
};
#endif

in another project, I have the default huttlePB I am editing, in it's header under the class' private section I have instantiated OMEUFUNCTION like this:
C++:
OEMUFUNCTION omeu;

and then in the class I can do things like:
C++:
oemu.EVASelectedCrew();

problem is, for transferring to other vessels, I will need to add a crew member to another vessel. Normally for calling functions of other VESSEL classes, I can do something like this:
C++:
VESSEL *v = oapiGetVesselInterface(/*handle of vessel to interact with*/);
v->SetThrusterGroupLevel(THGROUP_MAIN, 1); // for example...

Obviously though I cannot do this for class OEMUFUNCTION.

Optimally I would like to do this in a function of OEMUFUNCTION, so that developers can simply call that function, same as oemu.EVASelectedCrew();

I would also like to mention, VS 2010's static library project opens with some preset headers which say things like "#pragma once" and "#define WIN32_LEAN_AND_MEAN"... I am not sure if this will cause issues for me, but when I remove these I cannot compile at all, and get about 500 errors...

#### Blake

##### Addon Developer
Addon Developer
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?
It means OEMFUNCTION must be a class that publicly inherits from at least VESSEL2. VESSEL4 is the latest, you may as well use that.

class OEMFUNCTION : public VESSEL4
{
// your class def...
};

It means instances of OEMFUNCTION are vessels, or to be precise, at least VESSEL2's. If it does not make sense for instances of OEMFUNCTION to be vessels, you may need to rethink what you are doing.

#### jedidia

##### shoemaker without legs
Addon Developer
this is my declaration of OEMUFUNCTION in the .lib project:
Congratulations, as I had the impression from your last statements, you have done what I recommended you to do in the first place: You implemented OEMUFUNCTION by composition rather than by inheritance. I.e. the vessel has a member that serves as the interface to your functionality, rather than the vessel incorporating that interface into its own.

The trouble is, you're still trying to access it as though it was a part of the vessels interface. Which it is not. For that to be the case, and so for the code on the last pages to actually work, OEMUFUNCTION must be a VESSEL2, i.e. must inherit VESSEL2 (or any class that inherits VESSEL2) and must be instantiated by orbiter itself.

Don't bother though, as mentioned this way is better. This way, your vessel is not your OEMUFUNCTION, which keeps things much cleaner. The question now becomes, how can one of your vessels get at the OEMUFUNCTION of another vessel?
You can either do a static global manager where every vessel registers their OEMUFUNCTION instance and others can retrieve it, or you can implement the clbkGeneric method of the vessel to send out the instance when it receives a certain message.

#### Mr Martian

##### Orbinaut/Addon dev/Donator
Addon Developer
Donator
Congratulations, as I had the impression from your last statements, you have done what I recommended you to do in the first place: You implemented OEMUFUNCTION by composition rather than by inheritance. I.e. the vessel has a member that serves as the interface to your functionality, rather than the vessel incorporating that interface into its own.

The trouble is, you're still trying to access it as though it was a part of the vessels interface. Which it is not. For that to be the case, and so for the code on the last pages to actually work, OEMUFUNCTION must be a VESSEL2, i.e. must inherit VESSEL2 (or any class that inherits VESSEL2) and must be instantiated by orbiter itself.

Don't bother though, as mentioned this way is better. This way, your vessel is not your OEMUFUNCTION, which keeps things much cleaner. The question now becomes, how can one of your vessels get at the OEMUFUNCTION of another vessel?
You can either do a static global manager where every vessel registers their OEMUFUNCTION instance and others can retrieve it, or you can implement the clbkGeneric method of the vessel to send out the instance when it receives a certain message.
Hi Jedidia,

Thank s for the reply. My apologies for the confusion, this is what you meant by inheritance. Sorry my C++ literacy is not fantastic I wasn't 100% sure what you meant, I would have saved you the trouble.

Have you ever used the clbkGeneric function before? I have found one thread which discusses it: https://www.orbiter-forum.com/threads/deleting-data-passed-via-clbkgeneric.22947/ but beyond that I can't find much info on it.

I'm away from my home PC at the moment, but I have something like this in my .lib for EVASelectedCrew():
Code:
BOOL OEMUFUNCTION::EVASelectedCrew(OBJHANDLE h)
{
VESSEL v* = oapiGetVesselInterfcate(h);
OEMUFUNCTION o*;
v->clbkGeneric(OEMU_MSG, 1, &o);

if (o != NULL) return TRUE;
else return FALSE;
}

and then in my vessel class I have something like this:
C++:
ShuttlePB::clbkGeneric (int msg, int additional, void *parameter) // cant remember exact arguments off the top of my head
{
msg = OEMU_MSG;
additional = 1;
parameter = *oemu;    // oemu is how I instantiated OEMUFUNCTION in my header (OEMUFUNCTION oemu;)
}

I did define OEMU_MSG in my .lib's header too. Can't remember exactly what I had in there off the top of my head, but this causes a CTD. I think I am using clbkGeneric inforrectly. Do I have to define it in my vessel class at all? there is very limited info out there...

#### Blake

##### Addon Developer
Addon Developer
C++:
// Define MY_GENERIC_MSG in some header that both ShuttlePB and OEMFUNCTION have access to
int MY_GENERIC_MSG = VMSG_USER + <some number>;  // this creates a unique message you will recognize
...

BOOL OEMUFUNCTION::EVASelectedCrew(OBJHANDLE h)
{
VESSEL v* = oapiGetVesselInterface(h);  // Get interface to the vessel that has an override for clbkGeneric
v->clbkGeneric(MY_GENERIC_MSG, 1, this); // Call clbkGeneric, passing it a pointer to 'this' instance of OEMFUNCTION.

return true;  // not sure what the result here means.
}

C++:
ShuttlePB::clbkGeneric (int msg, int additional, void *parameter) // cant remember exact arguments off the top of my head
{
if (msg == MY_GENERIC_MSG) {  // Important, so we know that void* parameter is a pointer to OEMUFUNCTION
OEMUFUNCTION* func = (OEMUFUNCTION*)parameter;  // Cast parameter to a pointer of OEMUFUNCTION.
func->SomeMethodOnOEMUFUNCTIONClass(); // Make calls on the instance of OEMUFUNCTION passed in the method above.

// note: when this if block exits, you should assume 'func' is no longer valid.  Don't try to save it someplace and use it later.
}
}

I have not tested any of this, but I think this is what you are going for. There is nothing special about clbkGeneric, it is just a known method you can implement for any vessel. The msg parameter is intended to help you recognize the caller so that you don't try to use 'parameter' if you don't know what it is pointing to.

It looks like LUA uses clbkGeneric, so if you have a vessel that implements it, and you are not checking 'msg' for your message, that is likely the cause of a CTD.

Last edited:

#### Mr Martian

##### Orbinaut/Addon dev/Donator
Addon Developer
Donator
C++:
...

I have not tested any of this, but I think this is what you are going for. There is nothing special about clbkGeneric, it is just a known method you can implement for any vessel. The msg parameter is intended to help you recognize the caller so that you don't try to use 'parameter' if you don't know what it is pointing to.

It looks like LUA uses clbkGeneric, so if you have a vessel that implements it, and you are not checking 'msg' for your message, that is likely the cause of a CTD.
Hi Blake,

That is a big help! thank you so much! I now have a way of communicating with different OEMUFUNCTION objects.

One thing now, clbkGeneric has an argument "void *context"

This is fine for sending pointers to class types, no issue there, but I would like to be able to use this send a struct. Do you think this is possible?

in OEMUFUNCTION header (ouside the class) I have a struct like this:
C++:
typedef struct {
char firstname[24];
char lastname[24];
int age;
double weight;
} CREWMEMBER;

I can send this to a vessel like so:
C++:
CREWMEMBER crew;
// bunch of code etc.
v->clbkGeneric(MY_MESSAGE, 1, &crew);

and then in my definition of clbkGeneric in another vessel:
C++:
    if (msgid == MY_MESSAGE) {
CREWMEMBER me = (CREWMEMBER&)context;
sprintf (oapiDebugString(), "WEIGHT: %0.2f", me.weight);
}

I just had this print a debug string for the weight so I can see if it works, but instead it returns some ludicrous negative value....

Am I doing this correctly? all these pointers and conversions are getting a little confusing, I feel like I am not correctly doing this.

Thanks again

#### Blake

##### Addon Developer
Addon Developer
C++:
if (msgid == MY_MESSAGE) {
CREWMEMBER me = (CREWMEMBER*)context;
sprintf (oapiDebugString(), "WEIGHT: %0.2f", me->weight);
}

Try this change. You are sending the address of your struct, so treat it like a pointer in the clbkGeneric method. That should work.

#### Mr Martian

##### Orbinaut/Addon dev/Donator
Addon Developer
Donator
C++:
if (msgid == MY_MESSAGE) {
CREWMEMBER me = (CREWMEMBER*)context;
sprintf (oapiDebugString(), "WEIGHT: %0.2f", me->weight);
}

Try this change. You are sending the address of your struct, so treat it like a pointer in the clbkGeneric method. That should work.
Hi Blake,

That is what I tried originally, but I can't compile, I get an error "cannot convert from 'CREWMEMBER *' to 'CREWMEMBER '".

#### Blake

##### Addon Developer
Addon Developer
Hi Blake,

That is what I tried originally, but I can't compile, I get an error "cannot convert from 'CREWMEMBER *' to 'CREWMEMBER '".

Opps, try this:

C++:
CREWMEMBER* me = (CREWMEMBER*)context;

#### Mr Martian

##### Orbinaut/Addon dev/Donator
Addon Developer
Donator
Opps, try this:

C++:
CREWMEMBER* me = (CREWMEMBER*)context;
Perfect! I could have sworn that was the first thing I tried!

Many thanks Blake

#### Mr Martian

##### Orbinaut/Addon dev/Donator
Addon Developer
Donator
@jedidia @Blake @n72.75 @dbeachy1

Thank you all for your ongoing help with this, I really appreciate it.

This is still in its early stages but I seem to be making progress, and wanted to ask if any of you would be at all interested in beta testing this further down the track? My goal is to design this as simple as possible, so should be a matter of adding the SDK to a vessel project and giving it a try. No pressure whatsoever, but thought I'd give you all first choice of suggestions etc

Just let me know if you'd be interested at all

#### jedidia

##### shoemaker without legs
Addon Developer
My apologies for the confusion, this is what you meant by inheritance. Sorry my C++ literacy is not fantastic I wasn't 100% sure what you meant, I would have saved you the trouble.
Don't worry about it. This isn't so much related to C++ as these are generic terms from object-oriented programming. You're just learning them by bashing your head against C++, which is a pretty hard way given the utter unhelpfulness of the language, but it's exactly the same way I learned them: By screwing together wild stuff in orbiter with at best half a clue of what I was doing. Now I work as a software developer professionally, so apparently that way of learning does work. Or maybe it's just sheer dumb luck that any of my code ever works. Some days it still feels that way...