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

jedidia

shoemaker without legs
Addon Developer
Joined
Mar 19, 2008
Messages
10,158
Reaction score
1,277
Points
203
Location
between the planets
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
Joined
Jun 6, 2012
Messages
259
Reaction score
38
Points
28
Location
Sydney, Australia, Earth, Sol
Website
www.orbithangar.com
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
Joined
Mar 9, 2009
Messages
187
Reaction score
59
Points
43
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
Joined
Mar 19, 2008
Messages
10,158
Reaction score
1,277
Points
203
Location
between the planets
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
Joined
Jun 6, 2012
Messages
259
Reaction score
38
Points
28
Location
Sydney, Australia, Earth, Sol
Website
www.orbithangar.com
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
Joined
Mar 9, 2009
Messages
187
Reaction score
59
Points
43
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
Joined
Jun 6, 2012
Messages
259
Reaction score
38
Points
28
Location
Sydney, Australia, Earth, Sol
Website
www.orbithangar.com
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
Joined
Mar 9, 2009
Messages
187
Reaction score
59
Points
43
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
Joined
Jun 6, 2012
Messages
259
Reaction score
38
Points
28
Location
Sydney, Australia, Earth, Sol
Website
www.orbithangar.com
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
Joined
Mar 9, 2009
Messages
187
Reaction score
59
Points
43
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
Joined
Jun 6, 2012
Messages
259
Reaction score
38
Points
28
Location
Sydney, Australia, Earth, Sol
Website
www.orbithangar.com
@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
Joined
Mar 19, 2008
Messages
10,158
Reaction score
1,277
Points
203
Location
between the planets
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... :p
 
Top