graphic drawing class organisation - c++

I have a basic 4x20 character LCD that I would like to use for displaying a menu with buttons using an Arduino as the driver (limited standard library support).
Example LCD menu and buttons
I'm thinking of producing an interface class GraphicObject that all graphical objects then inherit from (such as Button, MenuItem etc.). It seems logical that the GraphicObject class should have a draw method which can be overridden.
At the moment, I have a class called Lcd which covers the low level drawing of text and character positioning. In order to draw anything, I will need access to one of these Lcd objects. If I include a pointer to the Lcd object within my GraphicObject or derived objects, I couple them and make them Lcd only objects. If I change the type of display device these classes aren't suitable any more.
How can the classes be organised to keep them loosely coupled and allow for a change in display types at a later date? Should I have LCDButton LCDMenuItem which then inherit from a Button and MenuItem interface, then create additional objects for the other display device (OtherDisplayButton OtherDisplayMenuItem)?
Any suggested reading? I've looked at many examples, but none of them seem to go into details about the function of a draw method and whether the device should be accessed directly or through another controlling object.
Thanks
Edit 1
Brief code idea overview
#include "Arduino.h"
class Lcd {
public:
struct Parameters {
uint_fast8_t numberLines;
uint_fast8_t numberCharacters;
// uint_fast8_t* lineOffsets;
Print* serial; // Can be any Serial device (all inherit from Print).
};
protected:
Parameters parameters_;
const uint_fast8_t escapeCode_ = 0x1B;
const uint_fast8_t directCode_ = 0xFE;
void sendCommand_(uint_fast8_t command, uint_fast8_t parameter = 0);
void sendCommandDirect_(uint_fast8_t command);
public:
Lcd(Parameters parameters);
void clearDisplay(void);
void moveCursor(uint_fast8_t line, uint_fast8_t character);
void resetDisplay(void);
void customCharacter(const uint_fast8_t address,
const uint_fast8_t characterMap[8]);
void write(uint8_t character);
// Boilerplate print forwarders.
void print(const char character);
void print(const String &string);
void print(const char string[]);
// Boilerplate println forwarders.
void println(const char character);
void println(const String &string);
void println(const char string[]);
void println(void);
};
class GraphicObject {
virtual void draw(void)=0;
};
class Button: public GraphicObject {
public:
typedef void (*buttonAction)(void);
virtual void setText(const String text)=0;
virtual const String getText() =0;
virtual bool isActive()=0;
virtual void setActive(bool)=0;
virtual void setAction(buttonAction action)=0;
};
class MenuItem: public Button {
public:
typedef void (*menuAction)(void);
virtual MenuItem* parentItem()=0;
virtual const MenuItem* addItem(String text, menuAction action)=0;
};
class VScrollbar: public GraphicObject {
public:
virtual void setAtTop(bool atTop);
virtual void setAtBottom(bool atBottom);
};
class LcdButton: public Button {
private:
Lcd* lcd_;
String text_;bool active_;
public:
LcdButton(Lcd* lcd);
void draw(void);
void setText(String text);
const String getText();bool isActive();
void setActive(bool);
void setAction(Button::buttonAction action);
};
class LcdWindow: public GraphicObject {
private:
LcdButton* lcdButtons_ = nullptr;
public:
enum class Position
: uint_fast8_t {
LEFT,
RIGHT
};
bool addButton(LcdButton* lcdButton, uint_fast8_t line, uint_fast8_t offset);
bool addVScrollbar(VScrollbar* vScrollbar, Position position);
void draw();
};
int main(void) {
Lcd::Parameters lcdParameters;
lcdParameters.numberCharacters = 20;
lcdParameters.numberLines = 4;
lcdParameters.serial = &Serial1;
Lcd lcd = Lcd(lcdParameters);
LcdButton myButton(&lcd);
myButton.setText("Select");
myButton.setActive(true);
LcdWindow lcdWindow;
lcdWindow.addButton(&myButton, 1, 1);
lcdWindow.draw();
while (1){}
return 0;
}

There are different ways to do this. In principle, you should define an interface (API) to your low-level LCD driver module(s) and call only functions of your low level api to draw something. The implementations of this api can then exchanged without the need to change the high level code.
The simplest way to do this is to define an abstract c++ base class where all lcd driver implementations have to be derived from. The base class should have virtual methods that need to be overloaded by the derived implementations.
But a litte information: virtual methods in c++ classes requires the compiler to generate a method pointer table for every object that is created when the object is instantiated; this needs some more memory. Also, all function calls to objects of these classes are indirect (the compiler generates code that first lookup the real function pointer and then calls the function using this pointer), which makes the resulting code slightly slower.

Related

Create a temporary interface into an object?

I have an object "World obj;" that has a normal interface of methods for it's typical funcitonality, but I want to have an additional interface of methods specifically for initializing that should only be visible when I specifically need them.
An example might be like this:
class World{
public:
void draw();
void update();
void normalStuff();
void addATree(); // this should not be ordinarily available or visible,
void addACar(); // calling this might break the object
void addAClown();// if it's not in a ready state for it
private:
int m_data;
};
Is there a way to relatively hide addATree(); etc in a way that makes sense? Ideally the mechanism for revealing those methods would also put the object into a ready state for them, or at least fault if it's not possible.
Different approaches would be possible:
Don't change the code, just change the spec
No need to change the code. Change the API specification and if the caller throws garbage in he gets garbage out.
Make the functions check if they are allowed
Always safe.
class World{
public:
...
void addAClown() {
if(not allowed)
throw error or crash or output error message or just return;
else {
do the work;
}
}
private:
int m_data;
};
Write a function that only exposes the Interface if allowed
You can't protect against someone getting the interface early and use it longer than allowed.
You could extract the interface functions into a separate class.
class WorldInterfaceToProtect {
public:
void addATree() = 0; // this should not be ordinarily available or visible,
void addACar() = 0; // calling this might break the object
void addAClown() = 0;// if it's not in a ready state for it
};
then the main class can protect these functions.
class World : protected WorldInterfaceToProtect {
public:
void draw();
void update();
void normalStuff();
protected:
void addATree(); // this should not be ordinarily available or visible,
void addACar(); // calling this might break the object
void addAClown();// if it's not in a ready state for it
private:
int m_data;
};
You then need to add a function that exposes the interface.
class World ... {
public:
WorldInterfaceToProtect *GetInterface() { return allowed_cond ? this : nullptr; }
...
}
Separate the class itself and the builder
This only helps if the functions to be called are only allowed during construction and not later. Depending on the design of the builder you can get a good protection.
class World{
friend class WorldBuilder;
public:
void draw();
void update();
void normalStuff();
protected:
void addATree(); // this should not be ordinarily available or visible,
void addACar(); // calling this might break the object
void addAClown();// if it's not in a ready state for it
private:
int m_data;
};
class WorldBuilder {
static World *Build(...);
}
Perhaps split the world into more composable parts:
struct WorldInterface
{
virtual void draw() = 0;
virtual void update() = 0;
virtual void normalStuff() = 0;
};
class World : public WorldInterface
{
public:
void draw() override { /* actual drawing here */};
void update() override {};
void normalStuff() override {};
private:
int m_data;
};
class TreeWorld : public WorldInterface
{
public:
// takes a reference to the actual world engine and defers work to
// that
TreeWorld(World& worldEngine) : worldEngine_(worldEngine) {}
void draw() override { worldEngine_.get().draw(); };
void update() override { worldEngine_.get().update(); };
void normalStuff() override { worldEngine_.get().normalStuff(); };
void addATree() {
//do tree/world interaction here
}
private:
std::reference_wrapper<World> worldEngine_;
};
class CarWorld : public WorldInterface
{
public:
// takes a reference to the actual world engine and defers work to
// that
CarWorld(World& worldEngine) : worldEngine_(worldEngine) {}
void draw() override { worldEngine_.get().draw(); };
void update() override { worldEngine_.get().update(); };
void normalStuff() override { worldEngine_.get().normalStuff(); };
void addACar() {
//do car/world interaction here
}
private:
std::reference_wrapper<World> worldEngine_;
};
extern void play_tree_game(TreeWorld world);
extern void play_car_game(CarWorld world);
int main()
{
World worldEngine;
// initialise engine here
// play tree-phase of game
play_tree_game(TreeWorld(worldEngine));
// play car phase of game
play_car_game(CarWorld(worldEngine));
}
Good answers all around, I'll just add this because it was missing(?)
class World{
public:
void draw();
void update();
void normalStuff();
private:
int m_data;
};
class BuildableWorld : public World
{
public:
void addATree();
void addACar();
void addAClown();
};
Use the BuildableWorld at initialization phase and then just give a pointer to the base class type for others to use.
Sure, you need some way to give the "built" data for the base class to access, but that was not the issue here, right?
an alternative approach that has not been mentioned so far, may be to let addX() functions take parameters whose existence implies that World is in a valid state. Say, if you cannot add trees to a world without water, let World return an (optional) water object to pass to addTree ... in other words, you need to properly formalize World invariants:
class World{
public:
void normalStuff();
auto getAvaliableWaterBuckets() -> optional<WaterBuckets>;
auto getAvaliableSoil() -> optional<SoilPack>;
//...
void addATree( WaterBuckets&&, SoilPack&& );
//...
};
// in the meanwhile, in user land:
if( auto water = world->getAvaliableWaterBuckets() )
if( auto soil = world->getAvaliableSoil() )
world->addTree( std::move(*water), std::move(*soil) );
else
world->recycleWater( std::move(*water) );
the benefit of this approach is that the user is not forced to think about world state validity ( an error prone task ), he just thinks about what he needs in order to add a tree ( simpler, hard to use incorrectly ). Moreover, this scales well because addX() functions can share different objects ( addFlowers needs water, ... ) enabling the correct management of a possibly complex internal world state.
Of course, IMHO, if you need to use addX() strictly on world construction only ( and you don't plan to add trees later ), then the factory approach already mentioned in the comments seems the way to go ...

Template type restriction based on inheritance

Say I have such classes:
class Scene {
public:
Scene(void);
~Scene(void);
virtual void update(void) = 0;
virtual void construct(void) = 0;
virtual void destroy(void) = 0;
};
class KillTheHedgehogScene : public Scene {
public:
virtual void update(void) override;
virtual void construct(void) override;
virtual void destroy(void) override;
EntitySceneGraph sceneGraph;
};
and I have a SceneManager class that goes something like this:
class SceneManager {
public:
SceneManager(void);
void loadNewScene(Scene* scene);
Scene* getCurrentScene(void);
void update(void);
private:
Scene* _currentScene;
};
The way my code functions now, I would load a new scene by doing the following:
_sceneManager->loadNewScene(new KillTheHedgehogScene());
My issue with this is that whatever scope this is in has made the allocation, and now _sceneManager has to be in charge of it's deallocation. I want the SceneManager to handle both allocation and deallocation. I want to be able to call my loadNewScene() function in this way:
_sceneManager->loadNewScene<KillTheHedgehogScene>();
This way the scene manager can handle both the allocation and deallocation of the object, and gives it all control. My question however is how can I have templates restrict an input type based on inheritance. For example, I wouldn't want to be able to call:
_sceneManager->loadNewScene<SomeRandomOtherClass>();
SomeRandomOtherClass is not a child class of Scene. Is there a way to restrict the type?
To have a better error message when providing invalid template parameter, you may add static_assert, something like:
template <typename T>
void loadNewScene()
{
static_assert(std::is_base_of<Scene, T>::value, "Type should inherit from class Scene");
// Your implementation
}

Visitor pattern and constness

I am running into a problem concerning the visitor pattern and constness.
Assume a visitor pattern implementation in C++ for a small game in which you draw stuff on the screen (depending on the internal state of the drawable objects) and at the same time run a logic that can change their internal state (pretty much any game you can imagine). Please, forgive any errors in code as this is done on the fly.
//Forward declaration of classes and visitor...
//class Game_actor; This one will be abstract, virtual, whatever.
class Game_actor_item;
class Game_actor_player;
class Game_actor_invisible;
class Visitor
{
public:
// virtual void visit(Game_actor& r)=0;
virtual void visit(Game_actor_player& r)=0;
virtual void visit(Game_actor_item& r)=0;
virtual void visit(Game_actor_invisible& r)=0;
};
Then, some interface-like basics:
//This one defines stuff that can be on the screen.
class Drawable
{
public:
virtual void draw(Screen&)=0;
};
//This one defines stuff that changes its state. Let's assume that do_logic
//returns an integer that means something to the controller and can be
//interpreted via a long list of "message_codes" (1=Add score, 2=Substract
//score, 3=Something else...). Each actor will run its logic and return its
//message, that will be stored and interpreted later. This is mostly crap,
//I know, but makes for a quick example.
class Game_actor
{
private:
float x;
float y;
public:
virtual int do_logic()=0;
void accept_visitor(Visitor& v)=0;
};
Next, our object hierarchy: Items and the player are derived from the actor, that defines part of their internal state. They can, of course, be drawn to the screen. There's a particular actor that won't be drawn, because it is invisible and controls something else.
class Game_actor_item: public Drawable, public Game_actor
{
//Lines and lines of code.
virtual void draw(Screen& s) {/* [...] */}
virtual int do_logic() {/* [...] */}
};
class Game_actor_player: public Drawable, public Game_actor
{
//Lines and lines of code.
virtual void draw(Screen& s) {/* [...] */}
virtual int do_logic() {/* [...] */}
};
class Game_actor_invisible: public Game_actor
{
//Lines and lines of code.
virtual int do_logic() {/* [...] */}
};
Finally, visitor specialization. We're gonna define two visitors, one is going to collect all drawable actors and other is going to dispatch messages to the game controller. Drawable actors will be collected from
a vector of its base class
class Visitor_drawing:public Visitor
{
private:
std::vector<Drawable *> draw_all_these;
public:
// virtual void visit(Game_actor& r)
virtual void visit(Game_actor_player& r) {draw_all_these.push_back(&r);}
virtual void visit(Game_actor_item& r) {draw_all_these.push_back(&r);}
//This one won't be drawn.
virtual void visit(Game_actor_invisible&) {}
std::vector<Drawable *> get_me_the_drawables() {return draw_all_these;}
}
class Visitor_logic:public Visitor
{
private:
std::vector<int> messages;
public:
// virtual void visit(Game_actor& r)
virtual void visit(Game_actor_player& r) {messages.push_back(r.do_logic());}
virtual void visit(Game_actor_item& r) {messages.push_back(r.do_logic());}
virtual void visit(Game_actor_invisible&) {messages.push_back(r.do_logic());}
std::vector<int> fetch_me_the_messages() {return messages;}
}
So, that's our setup. It is missing the "accept_visitor" methods, but I'm sure you get the idea here. Every final branch of the hierarchy just does void accept_visitor(Visitor& v) {v.visit(*this);}.
At any given time in our loop we collect drawable things and run logic:
std::vector<Game_actor*> actors;
while(loop)
{
Visitor_drawing dw;
Visitor_logic dl;
for(Game_actor * g : actors)
{
dw.visit(g);
dl.visit(g);
}
std::vector<Drawable *> draw_these=dw.get_me_the_drawables();
std::vector<int> messages=dl.fetch_me_the_messages();
for(Drawable * d : draw_these) d->draw(screen);
for(int * m : messages) interpret_message(m);
};
And here's my problem: I really want to keep my objects const where they should be. One requisite for this is that drawing them is never going to change their internal state (save a mutable int times_drawn, for example) so they could (and should (??)) be const. Doing their logic may change their state at each given turn (for example, moving them around the screen).
Given this particular setup, how would you manage having a const and non const visitor?. I have tried splitting the base visitor into const and non const base classes so they do
class Const_visitor
{
virtual void visit(const Thing&)=0;
};
class Non_const_visitor
{
virtual void visit(Thing&)=0;
};
class Visitor_drawing:public Const_visitor
{
virtual void visit(const Thing&)=0;
};
class Visitor_logic:public Non_const_visitor
{
virtual void visit(Thing&)=0;
};
But it seems that I would have to implement separate "accept_visitor" methods as the compiler doesn't distinguish between calls:
void accept_visitor_const(Const_visitor& v)=0;
void accept_visitor(Non_const_visitor& v)=0;
This leads to all kinds of duplication in the base visitor classes (basically writing everything twice, const and non const versions) and then separating the calls in the main loop: there's no single accept_visitor anymore and you have to know in advance what kind of visitor are you expecting (the side effect is that it really reads "I will accept this visitor that promises not to change my internals", which is somewhat desirable).
Anyway, Am I missing any other option that does not radically change this setup?. Is this a suitable and desirable option?
As always, many thanks in advance.

C++: what is the best way to handle this multi-inheritance?

A page is basically a fixed size array of a type - but it provides other functionality which isn't important for this question. Specifically, each page has a recordOffset which implies that the record IDs for the page are sequential and begin at this index (a page can be viewed as a discreet arbitrary segment of a larger array)
class AbstractPage
{
protected:
unsigned int recordOffset;
public:
AbstractPage(unsigned int recordOffset);
virtual ~AbstractPage();
// a mixture of pure and non-pure virtual functions
virtual string toString() const;
virtual unsigned int getCount() const = 0;
virtual PageType pageType() const = 0;
};
class IntegerPage : public AbstractPage
{
public:
vector<int> data;
IntegerPage(const vector<int>& data);
virtual ~IntegerPage();
// our concrete implementation
virtual unsigned int getCount() const;
virtual PageType pageType() const;
};
class FloatPage : public AbstractPage
{
public:
vector<float> data;
FloatPage(const vector<float>& data);
virtual ~FloatPage();
// our concrete implementation
virtual unsigned int getCount() const;
virtual PageType pageType() const;
};
I don't want to use templates for this because these pages get used liked this;
LinkedList<AbstractPage> pages;
I will use the interface methods provided by AbstractPage to interact with the page in most cases. When I need to read/write the data directly I will know the type separately and use:
dynamic_cast<FloatPage>(abstractPage).data[0] = 12.34;
So far so good, but here is the dilemma; I need to extend every type to create an indexed version of the page:
class AbstractIndexedPage
{
public:
// this is instead of the recordOffset of AbstractPage
vector<unsigned int> recordIds;
};
class IndexedIntegerPage : public AbstractIndexedPage, public IntegerPage
{
};
Now I want to be able to do this:
AbstractIndexedPage sort(const AbstractPage& page)
{
// Sorting will swap around the data and therefore we need to keep track of the
// record IDs in an Indexed page.
// If the incoming concrete type is IntegerPage, the output will be
// IndexedIntegerPage
}
The problem is the returned object will not have the interface to AbstractPage:
AbstractIndexedPage sortedPage = sort(originalPage);
sortedPage.getCount(); // can't do this!
dynamic_cast<AbstractPage>(sortedPage).getCount() // can do this, but pretty messy
From what I've read multiple inheritance in all but select cases means your code is basically designed badly. In this case there is multiple inheritance from two non-interfaces, both of the classes will have a constructor (and virtual destructor) but will only ever directly deal with the instance variables they provide.
My options are:
AbstractIndexedPage extends AbstractPage and use virtual inheritance since there will now be two AbstractPage. But that will give me the complete interface. But isn't this a naughty hack?
Just duplicate data or recordIds instance variables in IndexedIntegerPage and IntegerPage to give the same functionality without the need for inheritance.
Design the architecture differently?
Thanks
You can use delegation to do this work. For example, at AbstractIndexedPage:
class AbstractIndexedPage
{
public:
// this is instead of the recordOffset of AbstractPage
vector<unsigned int> recordIds;
AbstractClass* getPage() { return page;};
private:
AbstractClass *page;
};
and do something like that:
AbstractIndexedPage sortedPage = sort(originalPage);
sortedPage.getPage()->getCount(); // can't do this!
of course, verifying all possible errors or exeptions.
P.S. Someone will tell you to use smart pointer and I will agree with them, but, for simplicity, I just use plain pointer either
I was going to post this in comments, but the code will look terrible. You might consider abstracting at a lower level: create an AnyType, and just one Page Type based on it:
union AnyType {
float f;
int i;
};
class AnyPage : public AbstractPage
{
public:
enum PageDataType {FloatPage, IntPage};
vector<AnyType> data;
AnyPage(const vector<int>& data); //creates an 'IntPage'
AnyPage(const vector<float>& data); //creates a 'FloatPage'
virtual ~AnyPage();
// our concrete implementation
virtual unsigned int getCount() const;
virtual PageType pageType() const;
private:
PageDataType myType;
};
Then you can create your linked lists of AnyPage, and as you said, you already know which kind of page you are dealing with when you want to access the data:
anyPage.data[0].f = 12.34;
Then, for the indexed variety, it is no longer multiple inheritance:
class AnyIndexedPage : public AnyPage
{
public:
// this is instead of the recordOffset of AnyPage
vector<unsigned int> recordIds;
};

Aid with code organization?

I have been working on the server code for my game. It works really well, but 90% of the functions are in 1 class.
Usually I find it pretty easy to know how to divide things up. Either each object is a class or each dialog is a class and handles logic for its children.
In this case, the server receives a request, and then either tells the other players or processes and notifies the appropriate players.
Here is the header file:
class ServerCore : public ServerHost, public NetEventListener,
public ServerEventProvider
{
NetEventDecoder m_dec;
NetEventEncoder m_enc;
LoginManager m_login;
ServerPlayerManager m_playerMan;
ServerTableManager m_tableMan;
void sendToTarget();
void sendToAllActive();
void sendToActiveNotTarget();
void sendToTable(int tableNo);
public:
ServerCore();
virtual void onConnect (Lacewing::Server::Client &client);
virtual void onDisconnect (Lacewing::Server::Client &client);
virtual void onError (Lacewing::Error &error);
virtual void onReceive (Lacewing::Server::Client &client, char * data, int size);
virtual void sendChatMessageC(const std::string& message,ChatAreaEnum area);
virtual void requestTableC(int tableNo, int seatNo);
virtual void playerRequestSitC(int tableNumber, int seatNumber);
virtual void playerStoodUpC();
virtual void hostGameC(const SpadesGameInfo& info);
virtual void gameMessageC(SpadesCSMessageEnum message, const std::vector<int>& params);
virtual void modifyListC(const std::string& player, GroupTypeEnum group, bool add);
virtual void canceledHostingRequestC();
virtual void sendInviteC(int tableNo, const std::string& player);
virtual void loginResult(const std::string& name, ServerPlayer* player,
const std::string& sessionID, bool success, bool newSession);
virtual void readyToReceiveLobbyDataC();
virtual void playerAbandonedGameC();
virtual void watchTableC(int tableNo);
virtual void gameCompleted(int tableId);
virtual void playerWantsPlayAgainC();
virtual ~ServerCore(void);
};
}
I do not really know how I could break this down into smaller classes. The tasks they perform are handed to other objects, such as authentication, and data retrieval from database, but all events are currently processed in this 1 file.
The net event decoder calls the appropriate method after converting it from the string received over the wire.
Doing it this way works very well, but the game is going to get bigger and I want to stay clean and modular.
Is this design a good idea? If not, what type of patterns are usually used in game server development to keep things more object oriented?
Thanks
Looking at your class, the command pattern comes to mind. Indeed, the methods ending in C look very much like commands. If you have a command interface:
class Command {
virtual void execute() = 0;
}
and a separate subclass for each command, then the NetEventDecoder could decode the received string into a specific command.
Also, anytime you find yourself passing the same data to multiple methods, that's a hint that there's a class hiding in there. For example, you could pull all the methods taking a tableNumber into a Table class. I would also get rid of the Manager classes and model the associations more directly.
For example:
// A game server contains players and tables
class GameServer {
map<string, Player *> players;
map<int, Table *> tables;
Player *findPlayer(const string &name);
Table *findTable(int tableNumber);
...
}
// A player can perform various actions
class Player {
string name;
bool requestSeat(Table *table);
void standUp();
...
}
// A table maintains the status of seats
class Table {
int tableNumber;
bool requestSeat(int seatNumber);
...
}
The GameServer class above simply models the game and does not deal with networking at all. The NetEventDecoder and ServerCore can then coordinate to provide networking functionality and invoke commands on the GameServer. An example command might look like:
class PlayerStandUpCommand : public Command {
GameServer *server;
string playerName;
void execute() {
Player *player = server->findPlayer(playerName);
player->standUp();
}
}