I've looked around but couldn't find what I'm looking for (not sure what keywords to use). I'm working on a C++ project myself. This project has windows, pages and fields. A window can contain one or more pages, and a page can contain one or more fields. What's shared across the window, page and field is they have an InputManager which takes care of all the IO of the user. The InputManager can act on the window, the page and the field (for example, some inputs act directly on the page, while some act on the field). Also, a field can generate status messages to be displayed at a location of the containing window.
Right now I have the following structure, but it doesn't seem optimal. There's a lot of pointer passing down, and I'm wondering if there's a better way to do so.
class InputManager
{
//do stuff to return input
}
class StatusManager
{
//do stuff to display statuses
}
class Field
{
friend class Page;
protected:
InputManager *input_manager;
StatusManager *status_manager;
/*
other methods that do stuff that
may recieve from input_manager and
send statuses through the status_manager
*/
}
class Page
{
friend class Window;
protected:
vector<Field* > fields;
InputManager *input_manager;
StatusManager *status_manager;
public:
AddField(Field *field)
{
field->input_manager = this->input_manager;
field->status_manager = this->status_manager;
}
}
class Window
{
protected:
vector<Page *> pages;
InputManager *input_manager;
StatusManager *status_manager;
public:
Window()
{
input_manager = new InputManager();
status_manager = new StatusManager();
}
AddPage(Page *page)
{
page->input_manager = this->input_manager;
page->status_manager = this->status_manager;
}
}
If I want to learn more about these types of problem, what keywords or fields should I be looking into?
Related
I'm sorry if I don't know the right word for what I'm trying to accomplish.
Basically I have an event handler object which only has a single member. The member is a Stage object.
When the event handler receives an event, I want it to simply use the stage object to call the relevant method. For example:
Event event; //this event is not part of my code, but rather the library I'm using.
Stage s; // my custom class object
EventHandler event_handler; //also my custom class object
event_handler.stage = &s;
if(event == SHUTDOWN) {
event_handler.stage->handle_shutdown();
}
So what I'm trying to accomplish is that, there will be seperate scopes that my program goes into over time, and I want each scope to have access to the event_handler such that they can do something like:
void some_other_scope(EventHandler* eh) {
Stage* some_new_stage = new Stage(...);
eh->stage = some_new_stage;
}
This way, the original event code stays the same, and the event handler will be calling handle_shutdown on a different object than it was originally going to.
So what I want to do is to overload the handle_shutdown method so that there can be different implementations of it. I know how basic overloading works, it can be done by specifying different parameters, but is there any way to have different definitions of the same class method based on the file that the object was created in?
I was hoping to have several files, each with their own some_other_scope() function, and each file can redefine the handle_shutdown method to do different things based on what that file needs.
I'm sure there's a way to do what I want, I just don't know the right words to use.
It seems you want to use polymorphism:
class IStage
{
public:
virtual ~IStage() = default;
virtual void handle_shutdown() = 0;
// ...
};
class Stage1 : public IStage
{
public:
void handle_shutdown() override { /*Implementation1*/ }
// ...
};
class Stage2 : public IStage
{
public:
void handle_shutdown() override { /*Implementation1*/ }
// ...
};
And then
struct EventHandler
{
std::unique_ptr<IStage> stage;
// ...
};
EventHandler event_handler;
event_handler.stage = std::make_unique<Stage1>();
if (event == SHUTDOWN) {
event_handler.stage->handle_shutdown();
}
// Later
event_handler.stage = std::make_unique<Stage2>();
if (event == SHUTDOWN) {
event_handler.stage->handle_shutdown();
}
I have a code similar to the following:
template<class ObjType>
class jsonable
{
private:
static map<string, jsonElem> config;
protected:
virtual void setConfig() = 0;
//other fields and methods in public/private
}
class user : public jsonable<user>
{
protected:
virtual void setConfig();
//other fields and methods in public/private
}
class client : user
{
protected:
virtual void setConfig() {user::setConfig(); /* more config */}
//other fields and methods in public/private
}
The main idea of this code is to save in static variables data related to the class referenced in the template. The problem comes when I want to inherit from the user class: the static variable is shared between user and client classes, instead of one static variable for each class.
I've tried to do something like:
class client : user, jsonable<client>
But a bunch of problems appeared (many methods with same name, and some other related to inherit 2 times the same class). I don't know if there is an elegant way of do this, or even if there is a way at all. (I'm a bit newbie in c++)
Any idea would be welcome! :). And of course, I can "copy" all the contents of user into client but... I would like to do not do that until there are no more options.
Edit:
In order to add context and details to the question, I'm going to explain a bit what I'm doing (or want to do).
Jsonable is a class that provides the ability to serialize into Json another class (helped by https://github.com/nlohmann/json).
To achive this, it uses a static map to store each jsonable-field name and its info (type and position relative to the start of the class in memory, so it can be serialized and deserialized).
The problem comes if a class inherits from another class that inherits from jsonable. Both shares that map, so only the baseclass data is consider when serializing/deserializing. Hope this explanation helps to understand...
Edit2: Giving a full code in a question seems very overkilling to me. If someone wants something to compile, I've uploaded a git repo: https://github.com/HandBe/jsontests
Really thanks to all the people who have put interest on this question!.
A possible solution can be derive client from both user (because it is a user) and jsonable<client> as (private/public apart)
class user : public jsonable<user>
{
protected:
virtual void setConfig();
//other fields and methods in public/private
};
class client: public user, public jsonable<client>
{
virtual void setConfig()
{
user::setConfig();
// more config, referred to jsonable<client>::map
}
}
because it has to implement jsonable for itself (regardless of user).
This is the so-called "stacked parallelogram" inhertiance pattern very common in multiple interface implementations as modular behavior.
Now user and client have each their own configuration
If I understand your problem correctly: you want client to be a user, but also have all the per-class statics defined in jsonable?
Have you considered composition over inheritance? This could work either way:
1) make user a component of client
class client : public jsonable<client>
{
user parent; // could also be a pointer
void setConfig() {parent.setConfig(); /* more config */}
/* ... */
}
2) make jsonable a component:
class user
{
jsonable<user> userjson; // public, private, whatever is appropriate for your design
/* ... */
}
class client : public user
{
jsonable<client> clientjson;
/* ... */
}
I have a singleton class named GameManager.
GameManager.h
#include "cocos2d.h"
using namespace cocos2d;
class GameManager : private CCObject
{
public:
GameManager(void);
virtual ~GameManager(void);
virtual bool init(void);
static GameManager* sharedGameManager(void);
CC_SYNTHESIZE(CCString*, email, Email);
CC_SYNTHESIZE(CCString*, nickName, NickName);
CC_SYNTHESIZE(int, currentGame, CurrentGame);
CCArray* gamesArray;
};
GameManager.cpp
#include "GameManager.h"
static GameManager* _sharedGameManager = NULL;
GameManager* GameManager::sharedGameManager(void)
{
if (!_sharedGameManager)
{
_sharedGameManager = new GameManager;
_sharedGameManager->init();
}
return _sharedGameManager;
}
GameManager::GameManager(void)
{
}
GameManager::~GameManager(void)
{
email->release();
nickName->release();
gamesArray->release();
}
bool GameManager::init()
{
CCLOG("GameManager Created");
email = CCString::create("");
email->retain();
nickName = CCString::create("");
nickName->retain();
currentGame = 0;
gamesArray = CCArray::create();
gamesArray->retain();
return true;
}
and I create the GameManager in my login page with
GameManager::sharedGameManager();
the flow of my app goes:
Login.cpp -> GameList.cpp -> GameScreen.cpp
In the login page I store the account email and nickname and also the gamesArray which is created within that page:
//store user info to gamemanager
GameManager::sharedGameManager()->setEmail((CCString*) parseOne->objectAtIndex(3));
GameManager::sharedGameManager()->setNickName((CCString*) parseOne->objectAtIndex(7));
GameManager::sharedGameManager()->gamesArray = gameObjectArray;
once the user has logged in, the GameList is created using the GameManager info.
so the info from the singleton exists at this point.
the GameList page creates a GameObject for each game in the gamesArray. each GameObject has a child button attached to it which is used to goto the GameScreen and saves that GameObjects id to the GameManager
GameManager::sharedGameManager()->setCurrentGame(gameNumber);
CCDirector * pDirector = CCDirector::sharedDirector();
CCScene* pScene = GameScreenScene::scene();
pDirector->replaceScene(pScene);
Now the weird part, when I get to the GameScreenScene the data from the GameManager is gone, except for the currentGame which is still showing the right values.
I have put in some logs to diagnose the problem but I can't seem to figure it out.
I can read the values right before the GameListScene changes to the GameScreenScene, but once it changes the values are gone.
I also tried reading the data in the GameObject button call, but the data also doesn't exist there also.
I can add more code if it can help figure this out, I just didn't want to flood this if it was something easy.
Any help would be awesome.
You are not retaining your autorelease objects (i.e, email and name strings) resulting in deletion of its data when it is released. The reason currentGame is still there is that it is an int (not an autorelease pointer).
You can use CC_SYNTHESIZE_RETAIN instead of CC_SYNTHESIZE to retain these two string to automatically retain them.
If i am right,
Constructor of CCObject (maybe Init() function) calls autorelease() and instances which are not attached to scenes automately destroy after one frame even if it is singleton instance.
I think your GameManager class have no reason to inherit from CCObject.
I have a collection of related classes, call them
class Manager {
private:
std::vector<Setting> m_settings;
}
class Setting {
private:
std::vector<Action> m_actions;
}
class Action {
private:
Keybind m_keybind;
}
class Keybind {
public:
UpdateKeybind;
private:
TYPE keybind;
}
As you can see from the pseudo-C++ code, Settings have actions, and actions have exactly one key binding. Now, as a user of my application you want to update the Keybind potentially, yes?
I currently have buttons in a keybind-type dialog associated with each action, so the action can handle updating it's own keybind.
My Problem:
How do I ensure that the Keybinding isn't bound to another object?
Possible solutions:
Move UpdateKeybind itself to the Manager class, then have Manager query all the settings.
Have a parent pointer in Action/Setting/Keybind so the Action can query the manager for updated keybind.
Have the Action query other Actions (not great conceptually as far as I can tell).
What I need from you:
What is the most rigorous approach, in terms of maintainability, speed, ease of understanding, and OOP appropriateness, to implementing checking if a Keybind is already found, whether out of my suggested solutions or something else entirely. I have already tried number 1 -- it works, but I feel like it could be better, ya dig?
I was unable to find similar questions on StackOverflow, but if you do I'd love to see them!
Any other pro tips, things to improve are helpful.
Just like #Amardeep says, you can try creating a class managing the mapping between actions and keybindings. Following is an example. It will automatically remove the keybind to the action if the there is new binding to that keybind.
class Manager {
private:
std::vector<Setting*> m_settings;
KeybindPool *m_keybindPool;
};
class Setting {
private:
std::vector<Action*> m_actions;
};
class Action {
public:
void Bind(Keybind* keybind) {
m_manager->m_keybindPool->Update(this, keybind)
}
Keybind* getKeybind() const {
return m_manager->m_keybindPool->getKeybind(this);
}
private:
Manager *m_manager;
};
class KeybindPool {
public:
void Update(Action* action, Keybind* keybind) {
if (m_keybindActionMap.find(keybind) != m_keybindActionMap.end()) {
m_actionKeybindMap.erase(action);
}
m_keybindActionMap[keybind] = action;
m_actionKeybindMap[action] = keybind;
}
Keybind* getKeybind(Action* action) {
return m_actionKeybindMap[action];
}
private:
map<Keybind*, Action*> m_keybindActionMap;
map<Action*, Keybind*> m_actionKeybindMap;
};
class Keybind {
private:
TYPE keybind;
}
Since you have an exactly 1:1 relationship between key bindings and actions, you could start with a pool of key binding objects and draw down from the pool as you configure actions. So when offering up available keys for configuration, any keys already bound would not be in the available pool.
Suppose I have a C++ application for a pet store that started out just with Cats. The app has a class that does some data handling for a Cat dialog box:
CatDlg.cpp:
CatDlg::CatDlg() {//ctor stuff}
CatDlg::onInitDialog() {
DBAccess db;
db.open();
CatRec cat;
cat = db.findRec(m_catID);
CString name = cat.getName();
NameField.SetWindowText(name);
// other catty dialogy stuff
}
Now the store owner wants to add Dogs to the application. Dog data is essentially the same as Cat data, except for data types. I could just duplicate the CatDlg class, change a few types and handle it this way, but that leads to maintenance issues and code bloat with 99% duplicated code:
DogDlg.cpp:
DogDlg::onInitDialog() {
DBAccess db;
db.open();
DogRec dog;
dog = db.findRec(m_dogID);
CString name = dog.getName();
NameField.SetWindowText(name);
// other doggy dialogy stuff
}
Or, I could change the dialog class to handle multiple animals, by passing in a flag that indicates the type of animal, then process it accordingly. THis also gets uglier as the owner add new animals, because the method gets longer, with lots of duplicated code blocks:
AnimalDlg.cpp:
AnimalDlg::onInitDialog(AnimalType type) {
DBAccess db;
db.open();
if(type == CAT)
{
CatRec cat;
cat = db.findRec(m_catID);
CString name = cat.getName();
}
else if(type == DOG)
{
DogRec dog;
dog = db.findRec(m_dogID);
CString name = dog.getName();
}
NameField.SetWindowText(name);
}
What's the best way to refactor the class to make it more general? Is there a design pattern that I can apply? Is there some way to pull the common code into a base class, then make animal-specific subclasses? I had a quick look through Fowler's book on Refactoring, but nothing leapt out at me.
Assume there is lots more code in the class, at the moment specific to the Cat type, but applicable to any type of animal.
Thanks for your help!
Philip
This screams for inheritance. Define a base class for animal. Then derive cat and dog and striped beaver and whatever else the store wants to sell.
Assuming that CatRec and DogRec are unrelated types that can't be made part of an inheritance structure, I think you need to use templates.
AnimalDlg would be a parent of CatDlg and DogDlg. AnimalDlg would have a new protected template findRec function whose return type would be the template type (so you could return CatRec or DogRec). The onInitDialog would exist only in AnimalDlg and call a virtual function getName instead of the db-specific code. The child versions of getName would then use the appropriate version (for example findRec<CatDlg>) of findRec to get the record and return the type back to onInitDialog.
I'll try to sketch the code later if I have a chance.
EDIT (code sketch):
class AnimalDlg
{
public:
void onInitDialog()
{
NameField.SetWindowText(getName());
}
protected:
template <class T>
T findRec(IdType id) const
{
DBAccess db;
db.open();
return T(db.findRec(id));
}
virtual std::string getName() const = 0;
};
class CatDlg : public AnimalDlg
{
protected:
virtual std::string getName() const
{
return findRec<CatRec>(m_catID).getName();
}
};
Tying your DB and data so directly to the GUI is not so good. If all you want is CRUD (Create Read Update Delete) then there are many tools available to do that with a DB already.
Drive the GUI around the purpose of your app not how it stores the data.
I would go along the lines of template specialization:
template <class AnimalType>
AnimalDlg::onInitDialog(AnimalType type, int m_animalId) {
DBAccess db;
db.open();
name = db.findRec<AnimalType>(m_animalId).getName();
NameField.SetWindowText(name);
}