After working within a group project to build pretty much a copy of IRC, and manning the GUI battle station, I realise I have written code for much more than the GUI (user commands, font commands, chat filter, etc).
This being my first attempt at OOP at all, i am now stuck with a huge GUI class which i would love to split into smaller classes.
However, now I am faced with a situation like the following:
Class A {
public:
int printStuff();
int doThings();
}
A::printStuff() {
return doThings;
}
A::doThings() {
return 2;
}
A situation where the classes still need to interact.
Keep in mind, the class system is a lot more complex than this, and there are interactions between all 5 of my proposed subclasses.
but class A should only be printing things, not doing things; as such, i would love to have it like this:
Class A {
public:
int printStuff();
}
Class B {
public:
int doThings();
}
What are the best/most elegant ways to go about doing so?
Heres the actual chunk from the header, if it helps; I've commented in the functions/variables each method requires- and also outlined my proposed subclasses:
// ncurses (GUI, screens)
void setup(); // initScreen(); initWindows();
void initScreen(); // ncurses.h
void initWindows(); // screen vars, ncruses.h, dummy text
void resize(); // screen vars, ncruses.h, showScreen();
void Routine();
// message factory (GUI, controll, vars)
string mRequested(); // _messageQueue
void mRecieved(string message); // command(), printchat(), _chatlog, _user
bool command(string message, int out_in); // _user, _messageQueue, mRecieved -- quit(not made), removeUser
int checkVulgar(string *message); // _user
string upperCase(string message);
// GUI (windows, vars)
void printAscii(WINDOW *scr, char *gaphics[], int sizey, int sizex, int starty, int startx); // ncurses.h, windows
void printServers(WINDOW *scr, int a); // ncurses.h, windows
void printMessage(string message, int message_lines, int *h_index, int *attempt, int *message_len, int prev_len, int endline); // ncurses.h, windows
void printUsers(WINDOW *scr); // _users, ncurses.h, windows
void printTimeout();
void printChat(); // printMessage(), fontcommands, _chatlog, ncurses.h, windows //
void showScreen(int a, int b); // printAscii,servers,users,chat , ncurses.h, windows
// control (GUI, _users)
void updateUsers(vector<User> users); // _users, printUsers();
void removeUser(string user); // _users, printUsers();
// Boundary (ncurses, GUI, messageFactory, vars)
void userInput();
void userInputB(); // _user, resize(), printchat(), mRecieved(), checkvulgar(), quit(), command(), _messageQueue
int selectServer(); // showScreen()
What are the best/most elegant ways to go about doing so?
Sit down on a sofa for a few days and come up with a robust and elegant design.
Then implement that design.
The mistake you made was not designing before you began coding. Now it's already time to refactor.
Figure out what classes should do what, try and make some CRC cards. They are a great help to figure out what the class diagram should look like.
After reworking the class design and starting from scratch, I've managed to separate and reuse most of my earlier code.
For anyone wondering, the mainApp now talks directly to the MessageFactory; MF contains an object of both GUI and Windows (ncurses in previous image).
This is really how it should have been from the beginning, and poor planning (due to not knowing anything about command line GUI, and the excitement of adding new features) has caused me a great deal of grief.
Plan properly, or suffer.
Related
I write games in C++ with SDL. I wrote games in C for more than a year and now I've been writing in C++ for the last 7 months. I'm trying to avoid almost all global variables and move to a system of ownership, where every object is owned by something else. This way I can just manage the life of classes who have shared_ptr members and almost never worry about freeing pointers.
For example, my game is a class with its subsystems.
class Game
{
public:
Game();
~Game();
void runFrame();
std::shared_ptr<Display> display;
std::shared_ptr<Audio> audio;
std::shared_ptr<Save> save;
};
But I run into messy looking nested classes, like my audio class below.
class Audio
{
public:
Audio(Game& parent);
~Audio();
struct MusicFiles;
struct SfxFiles;
std::shared_ptr<MusicFiles> musicFiles;
std::shared_ptr<SfxFiles> sfxFiles;
private:
class Music
{
public:
class File
{
public:
File(Music& parent, std::string fileAddr);
~File();
void play();
void fadeIn();
void stop();
private:
Music& _parent;
std::string addr;
Mix_Music* chunk;
};
Music(Audio& parent);
~Music();
void setVolume();
private:
Audio& _parent;
bool _enabled;
int _volume;
};
class Sfx
{
public:
class File
{
public:
File(Sfx& parent, std::string fileAddr);
~File();
void play();
void stop();
private:
Sfx& _parent;
std::string addr;
Mix_Chunk* chunk;
int channel;
};
Sfx(Audio& parent);
~Sfx();
void setVolume();
private:
Audio& _parent;
bool _enabled;
int _volume;
};
Game& _parent;
Music _music;
Sfx _sfx;
};
I have nested classes because I dislike having to write "Music" or "Sfx" in every function name, like setMusicVolume(), setSfxVolume(), setMusicHook(), setSfxHook(), etc. etc.
I can pull the nested classes out but Music and Sfx only need to exist within the Audio class. I'd rather reorganize everything and have a better design.
Do you have any better design suggestions?
Organize your various classes into a namespace to group them, instead of a single class. This thing will become very hard to maintain once it grows. You want to use your classes to encapsulate functionality and provide a simple and comprehensive interface to the 'outside world'. Typically, each class will get its own header file (.h or .hpp) and compilation unit (.cpp).
Accept that there are many good reasons for using getter and setter functions - they are fundamental for good encapsulation. If you really have to, you can always use friend class Audio in your nested classes to make private and protected members visible to Audio, but not to all other classes in your project that may (at some time in the future, maybe) include these classes.
You say that you dislike writing audio.setMusicVolume(0.8f) instead of audio.music.setMusicVolume(0.8f). Some may say that audio.getMusic().setVolume(0.8f) would be a good compromise, but Demeter disagrees. By using wrapper functions like setMusicVolume, every other class only needs to know about and communicate with Audio while Music is strictly internal and only known by Audio itself. If you use function chaining, you lose this advantage and expose Music and Sfx to the world - which is not necessarily a bad thing. If you want to stick to your syntax, my suggestion would be to keep the public interfaces very small and use friend to expose more functionality to Audio, if you need to.
FYI, in addition to using namespaces, nested types can be placed out of line, just like member functions, by using their qualified name. So you can do:
class Audio
{
public:
Audio(Game& parent);
~Audio();
struct MusicFiles;
struct SfxFiles;
std::shared_ptr<MusicFiles> musicFiles;
std::shared_ptr<SfxFiles> sfxFiles;
private:
class Music;
};
in another file
#include "Audio.h"
class Audio::Music
{
public:
class File;
Music(Audio& parent);
~Music();
void setVolume();
private:
Audio& _parent;
bool _enabled;
int _volume;
};
My application makes heavy use of text-to-speech (through libespeak). It is written in C++/Qt5 with a QML-based frontend.
I have no formal C++ training (I have a Java background though) and as such I'm not entirely sure how to properly implement some of the more esoteric features.
libespeak supports a callback feature, which is called every time speech is synthesized.
The callback function takes three arguments, which I would like to use to visualize the speech. The code below works in the sense that the callback function is called correctly, but not useful since I can't access other member functions or variables.
itemvoice.h
#include "espeak/speak_lib.h"
int callback(short *wav, int numsamples, espeak_EVENT *events);
class ItemVoice : public Item
{
public:
explicit ItemVoice(QQuickItem *parent = 0);
};
itemvoice.cpp
#include "itemvoice.h"
extern int callback(short *wav, int numsamples, espeak_EVENT *events)
{
// do stuff
}
ItemVoice::ItemVoice(QQuickItem *parent):Item(parent)
{
espeak_Initialize(AUDIO_OUTPUT_PLAYBACK,500,NULL,0);
espeak_SetSynthCallback(callback);
}
I would like to make the callback function a member of the ItemVoice class. However if I try (and set the callback function with espeak_SetSynthCallback(ItemVoice::callback), the code won't compile anymore because of arguments which cannot be converted.
UPDATE: The suggestion below works. However, I have now run into another problem.
This is what the class looks like now:
itemvoice.h
#include "espeak/speak_lib.h"
int staticCallback(short *wav, int numsamples, espeak_EVENT *events);
class ItemVoice : public Item
{
Q_OBJECT
public:
explicit ItemVoice(QQuickItem *parent = 0);
void startSpeaking();
void stopSpeaking();
signals:
void updateGUI();
}
itemvoice.cpp
#include "itemvoice.h"
ItemVoice::ItemVoice(QQuickItem *parent):Item(parent)
{
espeak_Initialize(AUDIO_OUTPUT_PLAYBACK,500,NULL,0);
espeak_SetSynthCallback(staticCallback);
}
int staticCallback(short *wav, int numsamples, espeak_EVENT *events)
{
espeak_EVENT_TYPE type=events->type;
if(type==2) // start sentence
(static_cast<ItemVoice*>(events[0].user_data))->startSpeaking();
else if(type==6) // stop sentence
(static_cast<ItemVoice*>(events[0].user_data))->stopSpeaking();
}
void ItemVoice::startSpeaking()
{
//do stuff
updateGUI();
}
void ItemVoice::stopSpeaking()
{
// do stuff
updateGUI();
}
This works correctly. startSpeaking() is called when synthesis begins, and stopSpeaking() when it stops. The problem is that I need to send a Qt signal to update the GUI (updateGUI), and about a second after it's sent, my application crashes with a segmentation fault, even if the signal is not connected anywhere. It works perfectly otherwise.
Any idea?
Thanks for reading!
There is no direct way to do what you want. In your case you are lucky because there is void* user_data field in espeak_EVENT. You can set it to this when you call espeak_Synth():
void ItemVoice::synthSpeech() {
espeak_Synth(...., this);
}
So in the callback (which is still a global function, or a static function in ItemVoice) you can do roughly this:
int staticCallback(short *wav, int numsamples, espeak_EVENT *events) {
if (numsamples > 0)
return (static_cast<ItemVoice*>(events[0].user_data))->nonStaticCallback(wav, numsamples, events);
}
Coming from Java to C++ I'm attempting to understand abstraction through object orientation.
To put this into a practical example, I am developing a small game using the SFML library for graphics. However this question does not relate to that, simply think of it as background info. Anyway, the way the game works is to process through a number of different states. In this case 2:
The Menu State: The menu of the game is drawn and the game will begin here.
The Game State: This state controls the game, will update entities and draw them.
In order to do this I have created the following classes:
GameStateManager.h
#ifndef GAMESTATEMANAGER_H
#define GAMESTATEMANAGER_H
#include <SFML/Graphics.hpp>
#include <iostream>
#include "GameState.h"
class GameStateManager
{
public:
// Constructor
GameStateManager();
// State variables
static const int NUMGAMESTATES = 2;
static const int MENUSTATE = 0;
static const int GAMESTATE = 1;
// Public Functions
void set_state(int state);
void update();
void draw(sf::RenderWindow &win);
void input(sf::Event event);
private:
// Array of gamestates
GameState game_states[];
// The current state
int current_state;
// Private functions
void load_state(int state);
void unload_state(int state);
};
#endif
GameState.h
#ifndef GAMESTATE_H
#define GAMESTATE_H
#include <iostream>
#include <SFML/Graphics.hpp>
#include "GameStateManager.h"
class GameState
{
protected:
GameStateManager gsm;
public:
virtual void init() = 0;
virtual void update() = 0;
virtual void draw(sf::RenderWindow &win) = 0;
virtual void input(sf::Event event) = 0;
};
#endif
Now you may have noticed the Array of GameStates in Game State Manager? This provides an error to which I do not understand: zero-sized array. Does this mean initialization needs to be made within the header file? Further to this point the compiler mentions an Array of Abstract class isn't allowed?
The second issue is that the field gsm in the abstract GameState class does not recognize and brings up yet another error: Missing type specifier.
Now to complicate things further I have the following class: MenuState. This class is meant to extend GameState.
MenuState.h
#ifndef MENUSTATE_H
#define MENUSTATE_H
#include "GameState.h"
class MenuState: public GameState
{
public:
MenuState(GameStateManager gsm);
void init();
void update();
void draw(sf::RenderWindow &win);
void input(sf::Event event);
private:
sf::Texture title_texture;
sf::Sprite title_sprite;
};
#endif
As mentioned this class will control the menu of the game.
Implementing GameStateManager is done as follows:
GameStateManager.cpp
/*
* GameState Manager will take care of the various states of the game.
* In particular there will be two states: Menu or Ingame. GameStateManager
* will load and unload each state as needed.
*
* Author: Ben Euden
* Date: 2/5/2014
*/
#include "GameStateManager.h"
// Class Constructor
GameStateManager::GameStateManager()
{
game_states = game_states[NUMGAMESTATES];
current_state = MENUSTATE;
load_state(current_state);
}
/*
* Load the current game by creating and initialising the state
* then storing it in the game_states array.
* #Param state The state we wish to load.
*/
void GameStateManager::load_state(int state)
{
if(state == MENUSTATE)
game_states[state] = MenuState(this);
//if(state == GAMESTATE)
//game_states[state] = MainGameState(this); // Not implemented yet.
}
/*
* Unload the state we loaded with load_state
*/
void GameStateManager::unload_state(int state)
{
game_states[state] = NULL;
}
void GameStateManager::set_state(int state)
{
unload_state(state);
current_state = state;
load_state(state);
}
void GameStateManager::update()
{
try{
game_states[current_state].update();
}
catch(int e)
{
std::cout << "Exception occured during update of game state" << e << std::endl;
}
}
void GameStateManager::draw(sf::RenderWindow &win)
{
try{
game_states[current_state].draw(&win);
}
catch(int e)
{
std::cout << "Exception occured when trying to draw gamestate: " << current_state << "Exception number: " << e << std::endl;
}
}
void GameStateManager::input(sf::Event event)
{
game_states[current_state].input(event);
}
And MenuState as follows:
/*
* This class extends the Game State header and will deal with the menu of the game
* this includes drawing the correct text to the screen, moving the selector and
* either exiting, bringing up about or starting the game.
*
* Author: Ben Euden
* Date: 2/5/2014
*/
#include "MenuState.h"
MenuState::MenuState(GameStateManager gsm)
{
gsm = gsm;
init();
}
void MenuState::init()
{
title_texture = sf::Texture();
title_texture.loadFromFile("sprites/Title.png");
title_sprite = sf::Sprite();
title_sprite.setTexture(title_texture);
title_sprite.setPosition(512, 200);
}
void MenuState::update(){}
void MenuState::draw(sf::RenderWindow &win)
{
win.draw(title_sprite);
}
void MenuState::input(sf::Event event)
{
}
Please ignore inplemented methods and positionings. At this point I began to attempt to compile the project (I'm using Visual Studio) when the errors appeared.
Now in understand that the MainGameState hasn't been implemented yet but even with MenuState I'm sure I'm missing something vital here as I am still learning C++. With this in mind also please excuse any breakage of conventions etc again I am learning so feel free to correct me, it is better I learn the right way now rather than develop bad habits.
In Summary I'd like to understand why I am receiving the following errors:
protected:
GameStateManager gsm;
This produces the error: missing ';' before gsm.
GameState game_states[];
Produces the errors of: zero-size array, array of abstract class not allowed.
I believe if I fix these the rest will sort themselves out.
Thank you for your patience, time and assistance with this.
Euden
To be short: you don't know any basics of C++, and as a beginner, you should really aproach it as a totally different language than Java or C, so you should stop your project right now and find a good book for C++ beginners. Don't try to mix your Java knowledge and just fill the gaps to reach C++ knowledge, it will not work because even if the syntaxe is close, they are widely different beasts.
I always recommend learning C++ as a new and different language, whatever your background. Right now you are doing big errors that shows you're on the wrong path to learn C++. You should get back to basic tutorials (I'm not trying to be harsh, you really need to learn the basics before even managing to compile this code).
You use of arrays and members like if they were references shows your lack of understanding of "value semantic" and several other basic concepts which are must-known of C++ usage.
For example, if I have
class A
{
int k = 42; // C++11
};
Here a A object will contain a k object. What I mean is that k is not a pointer to an int, it's the actual value, within memory allocated into the A object.
So if I have
A my_object; // object on the stack
Then my_object is an object taking the size of an int. So if I do:
class B
{
int u;
A a;
};
Then an instance of B will actually be the size of a A object, plus the size of an int. B objects will contain all these data in a single block of memory.
So when you do:
class GameState
{
protected:
GameStateManager gsm;
What you actually do here is that you build a full GameStateManager into any GameState object. Yes, gsm is not a reference, it's the full object.
What you should do here is either use a c++ reference (if the game manager should never change) or use a pointer (or a smart poitner if there is ownership involved).
I see a lot of other problems, like your array member into GameStateManager have absolutely not the same meaning than in Java. Basically, you're note coding in C++ here. (and you should use either std::vector or std::array but your GameState are dynamic so it would vector or array of pointers - or even map or another container).
As there is too much to point, I should get to the core point:
Whatever the language that you have learnt before, even C or Java which are related, never ever assume you know anything of C++ yet, you absolutely don't. You need to approach it as a beginner. Learn it as a very new language.
And make sure you read actually good material as the list provided there. It's extremely easy to learn bad practice online about C++, unfortunately (but it gets better).
Also, you might want to read this: https://softwareengineering.stackexchange.com/questions/76675/how-can-a-java-programmer-make-the-most-of-a-new-project-in-c-or-c/76695#76695
By the way, a related recommendation: read "SFML Game Development" book for example of simpler and safer (and C++-idiomatic) ways to do what you are trying to achieve here.
Another side recommendation would be to avoid using "manager" in your type names, it only makes things hard to understand and design.
The "zero-size array" error is caused by GameState game_states[];.
In C++ you have to specify the array size at declaration time, either by specifically writing the size or direct initializing it.
Example:
GameState game_states[ ]; // Error, compiler can't know how much memory to reserve for this.
GameState game_states[4]; // OK, explicit size given, compiler will reserve enough memory for 4 `GameState` objects.
GameState game_states[ ] = { GameState( ), GameState( ) }; // OK, direct initialization, compiler will reserve enough memory for 2 `GameState` object.
In your case it should be:
GameState game_states[ NUMGAMESTATES ];
And you should drop the following line from GameStateManager constructor:
game_states = game_states[NUMGAMESTATES]; // Meaningless in C++.
"Array of Abstract class isn't allowed" arises from this declaration also, the problem is C++ differs from Java here. In C++ this declares a variable which is a GameState instance, which is not allowed because GameState has pure virtual methods and as so can't be instantiated ( Just as Java abstract classes can't be newed ). To achieve this polymorphic behavior in C++ you have to use pointers or references, which is what Java uses implicit for you.
Fixing this should give you:
GameState * game_states[ NUMGAMESTATES ];
"missing ';' before gsm" is happening just because the compiler couldn't compile GameStateManager, fixing the bugs I mentioned should solve this.
Few tips:
Think of variables in C++ as ints from Java, even for types you've declared yourself. Which means they are instantiated just by declaring then ( no new needed ) and they are copied when assigned to another variable. ( no reference semantics by default as Java )
Look for good C++ books/tutorials as you don't seem to understand some very important basic concepts from C++.
Obligatory - I'm a newbie. Have a job that involves programming and I'm teaching myself as I go. Needless to say as a teacher I get things wrong frequently and thoroughly.
Where I'm at right now: I've created the class "Graph", it (surprisingly enough) makes graphs. But now I want to make it so that on a mouse click I modify the graph. But I can't seem to get a mouse handler to be a member function of the class.
cv::setMouseCallback(windowName, onMouse, 0); // Set mouse handler to be onMouse
Doesn't work with
cv::setMouseCallback(windowName, Graph::onMouse, 0);
It gives me lack of parameter errors. According to this I can't make it a member function. After following the answer given, it compiles but my this pointer is nulled. Ugh.
OnMouse looks like this:
void onMouse(int event, int x, int y,int, void*)
{
if (event == CV_EVENT_LBUTTONDOWN)
{
cvMoveWindow("Window", 500, 500); //Just to see if stuff happened
}
return;
}
I don't care about moving the window, I want to modify the graph itself - which is stored as a cv::Mat variable in a Graph object. And I can't figure out how to do it.
Any help would be appreciated, and I really hope this wasn't just gibberish.
Yes callback functions in C++ are a joy, aren't they? You actually have to give OpenCV a function (not a class method) as you've already found out. However, you can hack around this awfulness using the following technique:
class MyClass
{
public:
void realOnMouse(int event, int x, int y, int flags)
{
// Do your real processing here, "this" works fine.
}
};
// This is a function, not a class method
void wrappedOnMouse(int event, int x, int y, int flags, void* ptr)
{
MyClass* mcPtr = (MyClass*)ptr;
if(mcPtr != NULL)
mcPtr->realOnMouse(event, x, y, flags);
}
int main(int argv, char** argc)
{
// OpenCV setup stuff...
MyClass processor;
cv::setMouseCallback(windowName, wrappedOnMouse, (void*)&processor);
// Main program logic
return 0;
}
That last parameter on setMouseCallback is quite useful for overcoming some of the problems you usually encounter like this.
You can also use the onMouse method as a static method.
class Graph
{
public:
static void onMouse(int event, int x, int y, void* param)
{
//Your code here
}
//Everything else you may need
}
Now you should be able to call the onMouse method with:
cv::setMouseCallback(windowName, onMouse, (void*) param);
The param can be NULL or whatever you want to pass as parameter to the method, but you'll need to make a type-cast to the desired type.
Hope this was useful.
Bye.
Currently my app uses just Direct3D9 for graphics, however in the future I' m planning to extend this to D3D10 and possibly OpenGL. The question is how can I do this in a tidy way?
At present there are various Render methods in my code
void Render(boost::function<void()> &Call)
{
D3dDevice->BeginScene();
Call();
D3dDevice->EndScene();
D3dDevice->Present(0,0,0,0);
}
The function passed then depends on the exact state, eg MainMenu->Render, Loading->Render, etc. These will then oftern call the methods of other objects.
void RenderGame()
{
for(entity::iterator it = entity::instances.begin();it != entity::instance.end(); ++it)
(*it)->Render();
UI->Render();
}
And a sample class derived from entity::Base
class Sprite: public Base
{
IDirect3DTexture9 *Tex;
Point2 Pos;
Size2 Size;
public:
Sprite(IDirect3DTexture9 *Tex, const Point2 &Pos, const Size2 &Size);
virtual void Render();
};
Each method then takes care of how best to render given the more detailed settings (eg are pixel shaders supported or not).
The problem is I'm really not sure how to extend this to be able to use one of, what may be somewhat different (D3D v OpenGL) render modes...
Define an interface that is sufficient for your application's graphic output demands. Then implement this interface for every renderer you want to support.
class IRenderer {
public:
virtual ~IRenderer() {}
virtual void RenderModel(CModel* model) = 0;
virtual void DrawScreenQuad(int x1, int y1, int x2, int y2) = 0;
// ...etc...
};
class COpenGLRenderer : public IRenderer {
public:
virtual void RenderModel(CModel* model) {
// render model using OpenGL
}
virtual void DrawScreenQuad(int x1, int y1, int x2, int y2) {
// draw screen aligned quad using OpenGL
}
};
class CDirect3DRenderer : public IRenderer {
// similar, but render using Direct3D
};
Properly designing and maintaining these interfaces can be very challenging though.
In case you also operate with render driver dependent objects like textures, you can use a factory pattern to have the separate renderers each create their own implementation of e.g. ITexture using a factory method in IRenderer:
class IRenderer {
//...
virtual ITexture* CreateTexture(const char* filename) = 0;
//...
};
class COpenGLRenderer : public IRenderer {
//...
virtual ITexture* CreateTexture(const char* filename) {
// COpenGLTexture is the OpenGL specific ITexture implementation
return new COpenGLTexture(filename);
}
//...
};
Isn't it an idea to look at existing (3d) engines though? In my experience designing this kind of interfaces really distracts from what you actually want to make :)
I'd say if you want a really complete the answer, go look at the source code for Ogre3D. They have both D3D and OpenGL back ends. Look at : http://www.ogre3d.org
Basically their API kind of forces you into working in a D3D-ish way, creating buffer objects and stuffing them with data, then issuing draw calls on those buffers. That's the way the hardware likes it anyway, so it's not a bad way to go.
And then once you see how they do things, you might as well just just go ahead and use it and save yourself the trouble of having to re-implement all that it already provides. :-)