Page 1 of 1
Software architecture
Posted: Sun May 02, 2010 1:09 pm
by Escen
Hello Guys,
As my applications are growing I have the need of managing my programs.
My today's problems are more associated with program complexity rather then coding itself (for now). This makes it hard to start-up.
I have more plans to extend my applications, therefore at this point it is still possible to apply any structure software.
I don't have any experience with software architecture, so I'm wondering if anyone has any advise of choosing.
Thanks
Escen
Posted: Sun May 02, 2010 2:00 pm
by ent1ty
I create a struct for everything, and then in main.cpp init everything and set some parametres, so it looks like this
Code: Select all
initShaders(video);
levelManager levelMgr(smgr);
weatherManager weatherMgr(smgr);
grassManager grassMgr(smgr);
treeManager treeMgr(smgr);
weaponManager weaponMgr(smgr);
UIManager UIMgr(device);
npcManager npcMgr(smgr);
levelMgr.loadLevel("./data/levels/example/level.irr");
levelMgr.setLevelFog(SColor(0,128,128,128), 200, 800);
weatherMgr.initWeather();
weaponMgr.loadWeapons("./data/config/weapons.xml");
player* player1= new player(smgr, timer);
player1->setPrimaryWeapon(weaponMgr.createWeaponFromIndex(0), camera);
player1->setSecondaryWeapon(weaponMgr.createWeaponFromIndex(1), camera);
UIMgr.setActivePlayer(player1);
UIMgr.init();
npcMgr.setTerrain((ITerrainSceneNode*)smgr->getSceneNodeFromType(ESNT_TERRAIN));
...
I don't know if it is the best solution, but works so far. But should I try multiplayer some day, I will probably run to problems with my player/npc management.
Posted: Mon May 10, 2010 4:07 pm
by ent1ty
Ok, here is one question: I have 2 classes. Now I need to use method from first class in the second class. What is the best way to do this? I though about passing pointer on first class to second class, but with multiple classes it could get a little messy.
Posted: Mon May 10, 2010 4:55 pm
by randomMesh
You could do it like this:
You have a class Game with takes care of the IrrlichtDevice (init, rendering, dropping, etc.) and other stuff like networking, physics, etc.
Code: Select all
class Game
{
public:
bool init() { /* init device here */ }
irr::IrrlichtDevice* getIrrlichtDevice() const { return device; }
private:
irr::IrrlichtDevice* device;
Physics* physics;
};
Then you pass a pointer of this class to the other classes.
Code: Select all
class Player
{
public:
Player(Game* game) :
game(game)
private:
Game* game;
};
Now you can access all the shared classes (device, physics, etc.) from the game pointer. All without using globals!
You need to use forward declaration for this to work. i.e. don't include the Game.h in Player.h but say:
Code: Select all
class Game; //forward declaration
class Player
{
}
and only include Game.h in Player.cpp.
Another thing you can do is to derive Game from irr::IEventReceiver and have a bool OnEvent(), so you can handle the events in your game class and access the other classes without too much hassle.
Posted: Tue May 11, 2010 6:06 am
by Brainsaw
I came to the conclusion that inter-class communication in my next project will be done using a single message queue (e.g. the "select level" dialog needs to tell the "play game" class which level and options were selected). I already have such a queue in my IrrOde wrapper (which is quite extensible btw), but I'm not sure about whether or not I should re-use this one or add another one to my state machine. It will be something like this:
Code: Select all
#include <irrlicht.h>
using namespace irr;
using namespace core;
//the message interface to be sent
class IMessage {
public:
virtual u32 getCode()=0;
};
//the receiver interface that handles messages
class IMessageReceiver {
public:
virtual void handleMessage(IMessage *aMessage)=0;
virtual void queueDestroyed()=0;
};
//the messaging queue
class CMessageQueue {
protected:
list<IMessageReceiver *> m_lReceivers;
public:
~CMessageQueue() {
list<IMessageReceiver *>::Iterator it;
for (it=m_lReceivers.begin(); it!=m_lReceivers.end(); it++) (*it)->queueDestroyed();
}
void addReceiver(IMessageReceiver *p) {
//first make sure one receiver is only added once
list<IMessageReceiver *>::Iterator it;
for (it=m_lReceivers.begin(); it!=m_lReceivers.end(); it++) if ((*it)==p) return;
printf("CMessageQueue::addReceiver: adding receiver\n");
m_lReceivers.push_back(p);
}
void removeReceiver(IMessageReceiver *p) {
list<IMessageReceiver *>::Iterator it;
for (it=m_lReceivers.begin(); it!=m_lReceivers.end(); it++) {
printf("CMessageQueue::removeReceiver: removing receiver\n");
m_lReceivers.erase(it);
return;
}
}
void postMessage(IMessage *pMsg) {
list<IMessageReceiver *>::Iterator it;
printf("distributing message\n");
for (it=m_lReceivers.begin(); it!=m_lReceivers.end(); it++)
(*it)->handleMessage(pMsg);
}
};
//an implementation of the message to send a text string
class CTextMessage : public IMessage {
protected:
stringc m_sText;
public:
CTextMessage(stringc sText) { m_sText=sText; }
virtual ~CTextMessage() { }
virtual u32 getCode() { return 23; }
stringc getText() { return m_sText; }
};
//The actual receiver
class CReceiver : public IMessageReceiver {
protected:
CMessageQueue *m_pQueue;
public:
CReceiver(CMessageQueue *pQueue) { pQueue->addReceiver(this); m_pQueue=pQueue; }
virtual ~CReceiver() { if (m_pQueue) m_pQueue->removeReceiver(this); else printf("queue destroyed!\n"); }
virtual void handleMessage(IMessage *aMessage) {
if (aMessage->getCode()==23) { //the code of the text message
CTextMessage *p=(CTextMessage *)aMessage;
printf("text received: \"%s\"\n",p->getText().c_str());
}
}
//make sure we don't try to remove the receiver from a deleted queue
virtual void queueDestroyed() { m_pQueue=NULL; }
};
int main(void) {
//create queue, receiver and a test text message
CMessageQueue *pQueue=new CMessageQueue();
CReceiver *pReceiver=new CReceiver(pQueue);
CTextMessage *pMsg=new CTextMessage("Hello World");
//post the message to all registered receivers
pQueue->postMessage(pMsg);
//cleanup
delete pMsg;
delete pReceiver;
delete pQueue;
return 0;
}
I think I'll try this piece of code once I'm back home ... got no idea if it really works, but it should.
This way you can have all classes communicate with each other without the need to know each other. E.g. the "select level" dialog sends a message with the selected level and some other information to the queue, the "play game" class reads the message and prepares to load the level whereas the "show highscore" class just ignores it. I found that this is quite a flexible approach to such communication things. In my "Stunt Marble Racers" project there are a lot of classes that know a lot of other classes, and a lot of special communication methods (like "selectLevel"), and for the next project I want to get rid of that.
Edit: of course some stuff is missing in the example, it is e.g. necessary to have a "remove message receiver" method in the queue. I think I'll do some additions once I'm home.
Posted: Tue May 11, 2010 7:21 am
by Ulf
Brainsaw wrote:I came to the conclusion that inter-class communication in my next project will be done using a single message queue
That is not actually a conclusion. It is called a "decision". Yea?
I think I'll try this piece of code once I'm back home ... got no idea if it really works, but it should.
Dreaming.
This way you can have all classes communicate with each other without the need to know each other.
This could get messy!
Posted: Tue May 11, 2010 8:58 am
by Brainsaw
Ulf wrote:Brainsaw wrote:I came to the conclusion that inter-class communication in my next project will be done using a single message queue
That is not actually a conclusion. It is called a "decision". Yea?
Yes, after messing around with the call-method communication method I made the decision that next time it's going to be a message queue.
Ulf wrote:
I think I'll try this piece of code once I'm back home ... got no idea if it really works, but it should.
Dreaming.
I'll see this afternoon or evening
Ulf wrote:
This way you can have all classes communicate with each other without the need to know each other.
This could get messy!
[/quote]
Well ... it would be possible to have e.g. send one message directly to another class, but using a message distributor is imho a good idea. I use this architecture in my IrrODE wrapper quite a lot, and it is still quite clean - I just missed using it for most of the other communication when it came to my project.
Posted: Tue May 11, 2010 10:45 am
by Brainsaw
Just tested my code. Did (almost) work. A missing semicolon, and some typos. Updated my original post

Posted: Wed May 12, 2010 7:07 pm
by ent1ty
Well thanks everyone for ideas, but as much as I hate it, I will have to use globals(but only 3 of them!

). I just don't want to rewrite approx. 1/3 of my code.. I will use this approach in my next project

Posted: Tue Jun 01, 2010 5:52 pm
by ent1ty
randomMesh wrote:...
Ok, I have a little problem with that. Here is what I do:
this is how the architecture looks like
#include "engine.cpp"
#include "renderer.cpp"
and in engine.cpp the class from renderer.cpp(class Crenderer) is needed to be used. so in engine.cpp, I do
Code: Select all
class Crenderer; //forward declaration
but code in engine.cpp still does not know about Crenderer class(left of '->render' must point to class/struct/union/generic type)
So what is the right way do do this?
Posted: Tue Jun 01, 2010 5:58 pm
by Dorth
Hmm, any reason you include the .cpp and not the .h?
Posted: Tue Jun 01, 2010 6:03 pm
by ent1ty
hm, I dont know, maybe I'm wrong there. I don't have .h's, just .cpp's.
Edit: oh, right, now I finally understand how to do forward declaration of class

Posted: Thu Jun 03, 2010 4:12 pm
by ent1ty
OK, there seems to be another problem. When I forward declare class MyClass, I can now use that class, but I still can't use methods from it. How do I make also methods available? In other words, class A needs to use methods from class B and class B methods from class A.
Posted: Thu Jun 03, 2010 9:43 pm
by ArakisTheKitsune
ent1ty wrote:class A needs to use methods from class B and class B methods from class A.
Ummm that is not possible. Maybe to create base class with those functions and to make A and B inherit that base class or to make class C with those functions?