Orbiter-Forum  

Go Back   Orbiter-Forum > Blogs > Project Mercury and object-oriented design
Register Blogs Orbinauts List Social Groups FAQ Projects Mark Forums Read

Here, I describe the evolution of Project Mercury and I describe object-oriented design related to Orbiter that I use in this project. Happy reading!
Rate this Entry

Objects interaction inside a vessel

Posted 12-29-2011 at 03:13 PM by Bibi Uncle
Updated 12-29-2011 at 03:20 PM by Bibi Uncle

There we are, my first entry about something really related to programming.

I found out, working on various testing project that the main problem was interaction between elements in the vessel when we are programming for Orbiter. In this entry, I will explain my idea to correct that problem and ease the development of vessels.
Making the objects know each other
The first thing I had to correct was that each component of the vessel needs to know about the other objects and interact with them. However, why do I have this problem? Simply because I see the vessel as a big class with smaller classes that interact with each other.

For example, let's base ourselves with a car. If I would like to program a car, I would probably consider it has a big class with basic I/O functions (accelerate, brake, change oil, etc.). This class, in order to work, would need smaller classes like the transmission, the wheels, the motor, etc. And these classes would need to interact each other. The motor would rotate the transmission, and transmission would rotate the wheels, etc.

One solution would be to let the car handle all the interactions. This is fairly simple to implement in real code. However, it creates a lot of mutator functions (also known as setter). In our example, the car would need to call an accessor function of the motor to know what is the current force created by the motor. Then, the car would set this force to the transmission using a setter and etc.

Therefore, I found a way to bypass this limitation. However, there was a cost for this. I sacrificed the encapsulation process just a bit. In fact, every components of the vessel class will be publicly available. This does not mean that the smaller classes do not use encapsulation. They actually use it. However, they have direct access to the other classes. Here is a schematic of the process:



As you can see, the vessel class has 3 smaller classes. These classes have a pointer to the vessel class, who has the pointers to every sub-classes. Therefore, every classes know each other.
How to implement it in C++
That was one of my problem. I had the idea, but I could not do it until I found this way. Let's look at each file.
  • The header of our vessel class (Vessel.h) will include every header of its sub-classes. This is logic, because it needs to know those classes before defining them.
  • The implementation of our vessel class (Vessel.cpp) will simply include its header. This is all standard for now.
  • The header of our sub-classes (SmallerClass.h) will define our vessel by itself, without specifing its component. In other words, it will say to the smaller class that Vessel class does exist. What it looks like, we don't know yet. In its definition, it will be able to define a Vessel pointer in its private members. In actual code, it will look like this:
Code:
class Vessel; // Vessel class does exist
 
class SmallerClass
{
public:
          SmallerClass(Vessel* vs); // we need a Vessel pointer at construction
 
private:
          Vessel* m_pVessel;
};
  • The implementation of our sub-class (SmallerClass.cpp) will include its header and Vessel.h.
We cannot include the header of Vessel in the header of SmallerClass because it will creates infinite inclusion. Vessel will include the sub-class, in which it includes Vessel, which includes SmallerClass, etc. If we only include Vessel.h in SmallerClass.cpp, there is no problem because Vessel does not include SmallerClass.cpp.

Now, the sub-classes can use the pointer to the vessel (m_pVessel in our example) and and interact with the other classes. For example, SmallerClass1 could do m_pVessel->m_smallerClass2->Run();. It calls a function inside SmallerClass2 (m_smallerClass2 is a SmallerClass2 defined in Vessel).


There you are, you can now have sub-classes that know about the other sub-classes and interact with them.

Happy programming!
Posted in Project Mercury
Views 4415 Comments 4
« Prev     Main     Next »
Total Comments 4

Comments

  1. Old Comment
    RisingFury's Avatar
    Quote:
    I sacrificed the encapsulation process just a bit. In fact, every components of the vessel class will be publicly available.
    AAAAAAAAAAAAA!!!!!!!!

    This is only as much of a solution, as is leaving your house front door open, so you don't need to lock the door!

    You should never ever sacrifice a program because of your own lazyness and not wanting to write accessors functions is exactly that.


    Quote:
    hese classes have a pointer to the vessel class
    It's generally a better idea to hand down the OBJHANDLE for the vessel and get the pointer to the vessel every time you need it. There is a possibility that you'll make a mistake and one of the "smaller classes" will be left running after the vessel gets deleted. If it does, the pointer will not get set to NULL. It'll stay whatever it was and cause a crash next time you use it. However, OBJHANDLE will become invalid.

    At least, always test for oapiIsVessel(MyObjHandle) every time you use the pointer.


    Quote:
    The header of our vessel class (Vessel.h) will include every header of its sub-classes. This is logic, because it needs to know those classes before defining them.
    In time, as the complexity of your code increases, you might start having problems with that. Let's say that class Car needs to know about class Engine and class Transmission. But class Transmission needs to know about class Engine and class Car and class Engine needs to know about Car and Transmission. This gets you into a situation where compiling fails and returns a bunch of weird errors. You can avoid that putting class Transmission; and class Engine; into the header file of Car.h. That will allow you to use the identifier Transmission and Engine from inside the Car's header. However, you'll need to include Engine.h and Transmission.h in the Car.CPP file, and not in the Car.h file.
    Posted 12-31-2011 at 11:39 PM by RisingFury RisingFury is offline
    Updated 12-31-2011 at 11:50 PM by RisingFury
  2. Old Comment
    Bibi Uncle's Avatar
    Thank you RisingFury for your comment!

    I knew that the encapsulation bypass would cause conflict with others. Personnaly, I'm not a very strict C++ developper. I am the kind of person that uses singletons and function pointers without problems.

    It's probably lazyness, but I can't see why I would spend a lot of time doing accessors, when I can do this bypass. I know it is less secure. I could easily delete one of the public pointers and crash the entire program. However, I prefer to take this risk (which is very improbable to happen), then writting lots of functions.

    Also, I am not writting a library here. It is not in my priority to ensure that every developper uses good pointer and does not delete my pointers. If I was writting a library, I would probably keep everything in private or at least protected scope.

    For your next comment, it's impossible that my vessel pointer will be NULL, because when Orbiter tries to delete my vessel, I delete the classes that use the vessel pointer. They are destructed in my vessel destructor.



    For your last comment, I can't see why it would not compile. Car is the "super-class", the class that contains the Engine and the Transmission.
    • Car will include Engine.h and Transmission.h.
    • Engine will define Car in its header, and then will include the Car.cpp into its Engine.cpp. So, it will know about Car and Trasmission.
    • Transmission will define Car in its header, and then will include the Car.cpp into its Transmission.cpp. So, it will know about Car and Engine.
    I would be glad to exchange further on this issue with you! My concept is probably not perfect. Exchanging leads to progress!
    Posted 01-03-2012 at 02:03 AM by Bibi Uncle Bibi Uncle is offline
  3. Old Comment
    RisingFury's Avatar
    Just be careful with pointers. I'm using a similar derivation system for the instructions in OBSP as you do. All the instructions are in an instruction queue, a component of a subclass of VESSEL3 made by ETF.

    The instruction queue gets deleted when a vessel is deleted and it deletes all the instructions, but for a reason I still don't understand, Orbiter sometimes crashed when a vessel got deleted. I traced the problem to pointers and since then started checking if the pointer is valid more often. Just be careful when you use pointers. They're one of C's (and C derived languages) greatest tools and greatest evils.

    As for keeping thing private, I know. I'm just freaking out because of my own personal preferences
    I just tend to go with the "class variables are private and classes have methods, struct variables are public and have no methods" rule of thumb. You probably know that a struct in C++ is *almost* identical to a class, except for the default privacy level.

    One powerful argument for writing friendly code in Orbiter add-ons is that there's a certain probability that other people will pick up your project after you abandon it.
    Posted 01-07-2012 at 06:11 AM by RisingFury RisingFury is offline
  4. Old Comment
    Bibi Uncle's Avatar
    Thank you again for sharing your opinion!

    For your destruction problem, I don't know if it is related, but Orbiter is a bit "weird" (I can't find the right word for it...) during destruction.

    In the Extra tab, in Orbiter Shutdown Options (or something like that), when you specify to terminate the process, which is more convenient, the destructor of your vessel is never called. I had difficulties with that because I had lots of memory leaks. Then, I found that when the shutdown is set to default, the destructor is called and everyone is happy.

    I'll probably write about memory leaks in my next entry.

    I don't know if you knew it, but it may be helpful in your projects.

    P.S. You're right: structures should never have methods. I had a lot of trouble with this in the past...

    Bibi Uncle
    Posted 01-11-2012 at 09:56 PM by Bibi Uncle Bibi Uncle is offline
 

All times are GMT. The time now is 06:51 PM.

Quick Links Need Help?


About Us | Rules & Guidelines | TOS Policy | Privacy Policy

Orbiter-Forum is hosted at Orbithangar.com
Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2019, vBulletin Solutions Inc.
Copyright ©2007 - 2017, Orbiter-Forum.com. All rights reserved.