Bi Direction association in C++ - c++

I've been trying to define two classes
class state03: public state
{
public:
std::string name;
state03(bool inAcceptState, bool inRejectState)
{
setRejectState(inRejectState);
setAcceptState(inAcceptState);
name="State 03";
}
state * evalNextState(char input)
{
if(input==' ')
{
return this;
}
if(isalpha(input) || input=='_')
{
return new state04(false,false);
}
return new RejectState();
}
};
class state04: public state
{
public:
std::string name;
state04(bool inAcceptState, bool inRejectState)
{
setRejectState(inRejectState);
setAcceptState(inAcceptState);
name="State 04";
}
state * evalNextState(char input)
{
if(isalnum(input) || input=='_')
{
return this;
}
if(input==',')
{
return new state03(false,false);
}
return new RejectState();
}
};
and since the compiler scans the code top to bottom i receive this compilation error that class state04 is undefined as it's defined after class state03...
so how can I achieve the bi direction relation?

You should move your implementation details in a separate file, which is the best and easiest way in that case:
state03.h
class state03 : public state {
// ...
state * evalNextState(char input);
}
state04.h
class state04 : public state {
// ...
}
state03.cpp
#include "state03.h"
#include "state04.h"
state * state03::evalNextState(char input) {
// your code here
}

Take the implementations out of the class definitions and move them to an implementation file.

Related

C++,runtime error: member call on null pointer of type

First of all, I am new here to c++ and I'm trying to learn it. Also new to stackoverflow.
Find it quite hard to be honest.
If you have additional comments in terms of my code and how i can improve it, please let me know as I am still in the learning process.
ok I was I just creating a online booking system using object oriented programming.
Ok so the main issue is that I dont understand why system.setDisplay(1234); isn't printing anything. I have tried everything and just isn't adding up.
OnlineBookingSystem is the class is being used to call setDisplay(id) which then invoked the display class.
If you can help it would mean the world to me and the error i`m getting is:
runtime error: member call on null pointer of type 'User' (solution.cpp)
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior prog_joined.cpp:179:54
#include <vector>
#include <string>
#include <iostream>
#include <memory>
#include <queue>
using namespace std;
enum class BookGenre
{
Horror,Adventure,Romance,Comic
};
class Book
{
private:
BookGenre genre;
string title;
size_t id;
public:
Book(string title,size_t id,BookGenre genre):title(title),id(id),genre(genre){}
string getTitle(){return title;}
size_t getId(){return id;}
BookGenre getGenre(){return genre; }
};
class Library
{
private:
vector<shared_ptr<Book>> listOfBooks;
public:
Library(){};
void addBook(string title,size_t id,BookGenre genre)
{
listOfBooks.push_back(make_shared<Book>(title,id,genre));
}
shared_ptr<Book> getBook(size_t id)
{
for(auto&x:listOfBooks)
{
if(x->getId()==id)
{
return x;
}
}
return nullptr;
}
void removeBook(size_t id)
{
for(auto it=listOfBooks.begin();it!=listOfBooks.end();it++)
{
if((*it)->getId()==id)
{
listOfBooks.erase(it);
}
}
}
};
class User
{
protected:
size_t id;
string username;
public:
User(size_t id,string username):id(id),username(username)
{
}
virtual ~User(){}
size_t getId(){return id;}
string getUsername(){return username;}
};
class Employee:public User{
private:
double salary;
public:
Employee(size_t id,string username,double salary):User(id,username),salary(salary)
{
}
void setSalary(double salary)
{
this->salary=salary;
}
double getSalary(){return salary;}
};
class Customer:public User{
private:
bool membership;
public:
Customer(size_t id,string username):User(id,username)
{
membership=false;
}
void setMemberActive()
{
membership=true;
}
bool isMemberActive()
{
return membership;
}
};
class UserManager
{
private:
vector<shared_ptr<User>>listOfUsers;
queue<shared_ptr<Customer>>queue;
public:
UserManager()
{
}
void addCustomer(size_t id,string username)
{
listOfUsers.push_back(make_shared<Customer>(id,username));
}
void removeCustomer(string username)
{
for(auto it=listOfUsers.begin();it!=listOfUsers.end();it++)
{
if(dynamic_pointer_cast<Customer>(*it))
{
if((*it)->getUsername()==username)
{
listOfUsers.erase(it);
}
}
}
}
shared_ptr<Customer> getCustomer(string username)
{
for(auto it=listOfUsers.begin();it!=listOfUsers.end();it++)
{
if(dynamic_pointer_cast<Customer>(*it))
{
if((*it)->getUsername()==username)
{
return dynamic_pointer_cast<Customer>(*it);
}
}
}
return nullptr;
}
void addToQueue(string username)
{
queue.push(getCustomer(username));
}
void removeCurrentCustomer()
{
queue.pop();
}
shared_ptr<Customer> getNextCustomer()
{
if(queue.empty())
{
return nullptr;
}
return queue.front();
}
/*
same process for user;
*/
};
class Display
{
private:
shared_ptr<Customer> m_customer;
shared_ptr<Book> m_book;
public:
Display(shared_ptr<Customer> _customer,shared_ptr<Book> _book ):m_customer(_customer),m_book(_book)
{
}
shared_ptr<Customer> getUser(){return m_customer;}
shared_ptr<Book> getBook(){return m_book;}
void displayInfo()
{
cout<<"Customer username: "<<m_customer->getUsername()<<endl;
cout<<"Member Active: "<<m_customer->isMemberActive();
cout<<"book id: "<<m_book->getId()<<endl;
cout<<"book title: "<< m_book->getTitle()<<endl;
}
};
class OnlineBookingSystem
{
private:
UserManager manager;
Library library;
shared_ptr<Display>display;
public:
OnlineBookingSystem()
{
UserManager manager;
Library library;
this->manager=manager;
this->library=library;
this->display=nullptr;
}
Library getLibrary()
{
return library;
}
UserManager getUserManager()
{
return manager;
}
void setDisplay(size_t id)
{
display=make_shared<Display>( manager.getNextCustomer(),library.getBook(id));
display->displayInfo();
}
shared_ptr<Display> getDisplay()
{
return this->display;
}
};
int main()
{
OnlineBookingSystem system;
auto lib=system.getLibrary();
lib.addBook("Adventure of Pablo",1234,BookGenre::Adventure);
auto manager=system.getUserManager();
manager.addCustomer(2020,"Michael");
auto _customer= manager.getCustomer("Michael");
_customer->setMemberActive();
manager.addToQueue("Michael");
system.setDisplay(1234);
return 0;
}
Problems I see:
Problem 1
Since the return type of OnlineBookingSystem::getLibrary() is Library, the line
auto lib=system.getLibrary();
construct lib as a copy of the object in system. Any changes made to lib are changes to the copy, not to the Library object in system.
To fix the problem, change the return type to a reference:
Library& getLibrary()
{
return library;
}
and capture the return value also as a reference in main.
auto& lib=system.getLibrary();
Problem 2
Similar to Problem 1 but this time it is in OnlineBookingSystem::getUserManager. Change its return type to be a reference:
UserManager& getUserManager()
{
return manager;
}
and capture the return value also as a reference in main.
auto& manager=system.getUserManager();
Problem 3
Adopt defensive programming at every step until something is a performanced bottleneck. If a return value of a function can be nullptr, check the return value at the point of invocation and deal with the case when the return value is indeed nullptr.
Update OnlineBookingSystem::setDisplay to:
void setDisplay(size_t id)
{
display=make_shared<Display>( manager.getNextCustomer(),library.getBook(id));
if ( display )
{
display->displayInfo();
}
else
{
// Deal with the nullptr case
std::cout << "Unable to find a book with id " << id << std::endl;
}
}
The problem
It comes down to your call to system.getUserManager() returning a copy of OnlineBookingSystem's manager field, instead of a pointer to it.
As a result, when you call manager.addToQueue("Michael"), it is adding Michael to the UserManager instance that is local to int main(), instead of the instance that's held in system.
Your function OnlineBookingSystem::setDisplay(size_t) makes a call to manager.getNextCustomer(). Because you've added Michael to a different instance of manager, this returns nullptr.
How to fix it
You simply need to modify OnlineBookingSystem::getUserManager() to return a pointer to manager instead of a copy of it:
UserManager* getUserManager()
{
return &manager;
}
Then modify the calling code to use the pointer dereference operator (->) instead of a period to access methods on manager.

How to use polymorphism to execute command on objects, which have no common base class?

I am receiveing commands through json, which I insert in to a pipe. For this reason thye must have the same base class.
The pipe is read by a pipe handler, some commands are consumed by the pipe handler, others have to be passed down to a device, which is a member of the pipe handler. I could simply do this:
class Command{};
class HandlerCommand : public Command {
void execute(Handler* h);
};
class DeviceCommand : public Command {
void execute(Device* d);
};
Command* c = pipe.receive();
if (const auto hc = dynamic_cast<const HandlerCommand*>(c)) { hc.execute( **handlerptr** ); }
else if (const auto dc = dynamic_cast<const DeviceCommand*>(c)) { dc.execute( **deviceptr** );}
Device and pipehandler should not have the same base, since they have no common methods, fields, they are conceptually different.
Is there a way to avoid using dynamic cast here. I was thinking maybe there is some neat design pattern for this, but couldn`t quit come up with a better solution.
EDIT: did not derive DeviceCommand and HandlerCommand from command, fixed this.
You cannot use polymorphism of two things which have nothing in common. You will need the same base class/interface: in your case Command. As mentioned above your base class requires a pure virtual function that must be implemented by the derived classes. I will utilize a Command * clone()const prototype, which could be very useful later on. Please introduce a virtual destructor of your base class, otherwise, to track down this memory error could be a pain in the ass. Note, regarding your dynamic_cast the member function execute, must be const. You may try this:
#include <iostream>
#include <vector>
class Handler
{
public:
Handler(){}
};
class Device
{
public:
Device(){}
};
enum class CommandType{Handler,Devise};
class Command
{
public:
virtual ~Command(){}
virtual Command*clone()const = 0;
virtual CommandType getType()const = 0;
};
class HandlerCommand : public Command {
public:
HandlerCommand():Command(){}
void execute(Handler* h) const
{
std::cout << __FUNCTION__<<"\n";
}
HandlerCommand*clone()const { return new HandlerCommand(*this); }
CommandType getType()const { return CommandType::Handler; }
};
class DeviceCommand : public Command{
public:
DeviceCommand():Command(){}
void execute(Device* d)const
{
std::cout << __FUNCTION__<<"\n";
}
DeviceCommand*clone()const { return new DeviceCommand(*this); }
CommandType getType()const { return CommandType::Devise; }
};
int main()
{
Device dev;
Handler handler;
std::vector<Command*> pipe{ new HandlerCommand(), new DeviceCommand() };
while (!pipe.empty())
{
Command* c = pipe.back();
if (c->getType() == CommandType::Handler) { static_cast<const HandlerCommand*>(c)->execute(&handler); }
else if (c->getType() == CommandType::Devise ) { static_cast<const DeviceCommand*>(c)->execute(&dev); }
delete c;
pipe.pop_back();
}
std::cin.get();
}
outputs:
DeviceCommand::execute
HandlerCommand::execute
Version 2.0 using std::variant. You will need at least C++17 to compile this. Note, a single pipe container can exclusively comprise one of the mentioned classes within the variant. So there is no casting anymore, but you will need two pipes. Because of that, I introduced a time stamp variable.
#include <iostream>
#include <vector>
#include <variant>
class Handler
{
public:
Handler() {}
};
class Device
{
public:
Device() {}
};
class HandlerCommand {
int ts;
public:
HandlerCommand(int _ts):ts(_ts) {}
void execute(Handler* h) const
{
std::cout << ts << ": "<< __FUNCTION__ << "\n";
}
int timeStamp()const { return ts; }
};
class DeviceCommand {
int ts;
public:
DeviceCommand(int _ts) :ts(_ts) {}
void execute(Device* d)const
{
std::cout << ts << ": " << __FUNCTION__ << "\n";
}
int timeStamp()const { return ts; }
};
using Command = std::variant<HandlerCommand, DeviceCommand>;
int main()
{
Device dev;
Handler handler;
std::vector<Command> hcPipe{HandlerCommand(2),HandlerCommand(5)};
std::vector<Command> dcPipe{DeviceCommand(1),DeviceCommand(4)};
Command single = DeviceCommand(0);
if (single.index() == 0)
{
std::get<HandlerCommand>(single).execute(&handler);
}
else
{
std::get<DeviceCommand>(single).execute(&dev);
}
while (!hcPipe.empty() || !dcPipe.empty())
{
if (!hcPipe.empty() && (dcPipe.empty() || std::get<HandlerCommand>(hcPipe.front()).timeStamp() < std::get<DeviceCommand>(dcPipe.front()).timeStamp()))
{
std::get<HandlerCommand>(hcPipe.front()).execute(&handler);
hcPipe.erase(hcPipe.begin());
}
else
{
std::get<DeviceCommand>(dcPipe.front()).execute(&dev);
dcPipe.erase(dcPipe.begin());
}
}
std::cin.get();
}
outputs:
0: DeviceCommand::execute
1: DeviceCommand::execute
2: HandlerCommand::execute
4: DeviceCommand::execute
5: HandlerCommand::execute

Override virtual function with more parameters

I have an array of happy people.
Each happy person has a virtual function called updateHappiness() which is used to update their happiness attribute.
Each person likes their own thing.
Rain lovers are happy persons who really like hearing the sound of the rain and it increases their happiness level. They inherit from the happy person class.
As a consequence, they need to know when it is raining while updating their happiness by overloading the updateHappiness() function with updateHappiness(bool isRaining) as in this post : overload virtual function with different parameters in c++, however, this is a problem because there are many kinds of people and we would like to update them all by calling the same function for every person.
We could have the parameter stored inside of the person class and pass it in the class constructor as in this post : Override number of parameters of pure virtual functions however rain is not a constant state and we would have to call a function updateRainState(bool isRaining) which would cause the same problem as before.
We could pass the parameter bool isRaining to every person even though they don't care about the rain but it would also be a problem because some people like the rain, some people like seeing the daylight, some people like it when their friends are happy... so it would add many useless parameters and it seems like a waste.
Finally, the best solution I could think of is to have a static function in the weather class to get the rain state without passing it as a parameter but it would look like a global variable and some people say that it is really bad!
What would you do to solve this problem ?
Here is an example code of what the classes are like :
class HappyPerson
{
public:
HappyPerson(): m_happinness(0) {}
virtual void updateHappinness() { m_happinness++; }
protected:
int m_happinness;
};
class Weather
{
public:
static int isRaining() { return raining; }
private:
static bool raining;
};
bool Weather::raining(0);
class RainLover : public HappyPerson
{
public:
RainLover() : HappyPerson() {}
void updateHappinness() { m_happinness++; if (Weather::isRaining()) m_happinness++; }
};
int main()
{
std::vector<HappyPerson*> happyPeople;
happyPeople.push_back(new RainLover);
// ... add many other persons
std::vector<HappyPerson*>::iterator it;
for (it = happyPeople.begin(); it != happyPeople.end(); it++)
{
(*it)->updateHappinness();
}
}
You should consider taking a completely different approach - use event callbacks instead.
When something in particular changes, only interested people are affected, so you should not waste time and effect trying to passing around the change to everyone else.
If a person's happiness depends on the Weather, then have the person register for Weather change events.
If a person's happiness depends on another person's happiness, then have the person register for the other person's happiness change events.
And so on.
For example:
class HappyPerson;
class HappinessChangeListener
{
public:
void happinessChanged(HappyPerson *person, bool isHappier) = 0;
};
class HappyPerson
{
public:
HappyPerson();
virtual ~HappyPerson() {}
void updateHappiness(int amount);
void registerHappinessChangeListener(HappinessChangeListener *listener);
void unregisterHappinessChangeListener(HappinessChangeListener *listener);
);
protected:
int m_happinness;
std::vector<HappinessChangeListener*> happinessChangeListeners;
void happinessChanged(bool isHappier);
};
...
HappyPerson::HappyPerson()
: m_happinness(0)
{
}
void HappyPerson::updateHappiness(int amount)
{
if (amount != 0)
{
m_happinness += amount;
happinessChanged(amount > 0);
}
}
void HappyPerson::registerHappinessChangeListener(HappinessChangeListener *listener)
{
happinessChangeListeners.push_back(listener);
}
void HappyPerson::unregisterHappinessChangeListener(HappinessChangeListener *listener)
{
std::vector<HappinessChangeListener*>::iterator i = std::find(happinessChangeListeners.begin(), happinessChangeListeners.end(), listener);
if (i != happinessChangeListeners.end())
happinessChangeListeners.erase(i);
}
void HappyPerson::happinessChanged(bool isHappier)
{
for(std::vector<HappinessChangeListener*>::iterator i = happinessChangeListeners.begin(); i != happinessChangeListeners.end(); ++i)
i->happinessChanged(this, isHappier);
}
class Weather;
class WeatherChangeListener
{
public:
void weatherChanged(Weather *weather) = 0;
};
class Weather
{
public:
Weather();
void rainStarted();
void rainStopped();
bool isRaining();
void registerWeatherChangeListener(WeatherChangeListener *listener);
void unregisterWeatherChangeListener(WeatherChangeListener *listener);
protected:
bool m_raining;
std::vector<WeatherChangeListener*> weatherChangeListeners;
void weatherChanged();
};
...
Weather::Weather()
: m_raining(false)
{
}
void Weather::rainStarted()
{
if (!m_rRaining)
{
m_rRaining = true;
weatherChanged();
}
}
void Weather::rainStopped()
{
if (m_rRaining)
{
m_rRaining = false;
weatherChanged();
}
}
bool Weather::isRaining()
{
return m_raining;
}
void Weather::registerWeatherChangeListener(WeatherChangeListener *listener)
{
weatherChangeListeners.push_back(listener);
}
void Weather::unregisterWeatherChangeListener(WeatherChangeListener *listener)
{
std::vector<WeatherChangeListener*>::iterator i = std::find(weatherChangeListeners.begin(), weatherChangeListeners.end(), listener);
if (i != weatherChangeListeners.end())
weatherChangeListeners.erase(i);
}
void Weather::weatherChanged()
{
for(std::vector<WeatherChangeListener*>::iterator i = weatherChangeListeners.begin(); i != weatherChangeListeners.end(); ++i)
i->weatherChanged(this);
}
class RainLover : public HappyPerson, public WeatherChangeListener
{
public:
RainLover(std::shared_ptr<Weather> &weather);
~RainLover();
void weatherChanged(Weather *weather);
protected:
std::shared_ptr<Weather> m_weather;
};
...
RainLover::RainLover(std::shared_ptr<Weather> &weather)
: HappyPerson(), m_weather(weather)
{
m_weather->registerWeatherChangeListener(this);
}
RainLover::~RainLover()
{
m_weather->unregisterWeatherChangeListener(this);
}
void RainLover::weatherChanged(Weather *weather)
{
updateHappiness(weather->isRaining() ? 1 : -1);
}
class HappyLover : public HappyPerson, public HappinessChangeListener
{
public:
HappyLover(std::shared_ptr<HappyPerson> &person);
~HappyLover();
void happinessChanged(HappyPerson *person, bool isHappier);
protected:
std::shared_ptr<HappyPerson> m_person;
};
...
HappyLover::HappyLover(std::shared_ptr<HappyPerson> &person)
: HappyPerson(), m_person(person)
{
m_person->registerHappinessChangeListener(this);
}
HappyLover::~HappyLover()
{
m_person->unregisterHappinessChangeListener(this);
}
void HappyLover::happinessChanged(HappyPerson *person, bool isHappier)
{
updateHappiness(isHappier ? 1 : -1);
}
int main()
{
std::shared_ptr<Weather> weather(new Weather);
std::vector<std::shared_ptr<HappyPerson>> happyPeople;
happyPeople.push_back(std::shared_ptr<HappyPerson>(new RainLover(weather)));
// or: happyPeople.push_back(std::make_shared<RainLover>(weather));
happyPeople.push_back(std::shared_ptr<HappyPerson>(new HappyLover(happyPeople[0])));
// or: happyPeople.push_back(std::make_shared_ptr<HappyLover>(happyPeople[0]));
// ... add many other persons
weather->rainStarted();
...
weather->rainStopped();
...
}

Inheritance, defining a function for creating an object of a specified type?

Title does not help im sure.
Anyway, at the moment i'm working with the following
http://puu.sh/7wJed.png
Everything's fine and inherited correctly, however, in order to create an object of say 'aircraftCarrier' i'd need to pass the 12 values + the two inherited values every-time i want to use a function such as
generateAirCraftCarrier(1,2,3,4,5,6,7,8,9,10,11,12);
I could simply pass in a navalVessel instance into the function instead, such that
generateAirCraftCarrier(myNavalVessel, inherit var 1, inherit var 2);
BUT this would not be entirely a solution because what happens when the aircraft carrier has a different 'Speed' for example?
can i have option parameters, which if null use the myNavalVessel object? Looking for some guidance here, sorry about the gibberish.
Why do you need one function to define all 12 values on an AircraftCarrier? Why not build it up with a number of setters on AircraftCarrier and NavalVessel? e.g:
class NavalVessel {
float speed_;
public:
void setSpeed(float speed) { speed_ = speed; }
};
class AircraftCarrier : public NavalVessel {
int noHeliPads_;
int noRunways_;
public:
void setNoHeliPads(int noHeliPads) { noHeliPads_ = noHeliPads; }
void setNoRunways(int noRunways) { noRunways_ = noRunways; }
};
int main() {
AircraftCarrier aircraftCarrier;
aircraftCarrier.setSpeed(25.3);
aircraftCarrier.setNoHeliPads(3);
aircraftCarrier.setNoRunways(2);
}
Could named parameters idiom be useful for you?
class AircraftCarrierParameters;
class AircraftCarrier
{
private:
AircraftCarrierParameters _params;
public:
AircraftCarrier(const AircraftCarrierParameters& params)
: _params(params) {}
AircraftCarrierParameters params() const { return _params;}
};
class AircraftCarrierParameters
{
private:
double _speed;
int _someOtherStuff;
public:
AircraftCarrierParameters()
: _speed(0) //default parameters
, _someOtherStuff(0)
{
}
double speed() const { return _speed; }
double someOtherStuff() const { return _someOtherStuff; }
AircraftCarrierParameters& setSpeed(double speed) { _speed = speed; return *this; }
AircraftCarrierParameters& setSomeOtherStuff(double stuff) { _someOtherStuff = stuff; return *this; }
};
AirCraftCarrier generateAirCraftCarrier(const AircraftCarrierParameters& params)
{
//...
}
void main()
{
AircraftCarrier c1(AircraftCarrierParameters());
AircraftCarrier c2(c1.params().setSpeed(30));
}

What is the right way to switch on the actual type of an object?

I'm writing an xml parser and I need to add objects to a class generically, switching on the actual type of the object. Problem is, I'd like to keep to an interface which is simply addElement(BaseClass*) then place the object correctly.
void E_TableType::addElement(Element *e)
{
QString label = e->getName();
if (label == "state") {
state = qobject_cast<E_TableEvent*>(e);
}
else if (label == "showPaytable") {
showPaytable = qobject_cast<E_VisibleType*>(e);
}
else if (label == "sessionTip") {
sessionTip = qobject_cast<E_SessionTip*>(e);
}
else if (label == "logoffmedia") {
logoffMedia = qobject_cast<E_UrlType*>(e);
}
else {
this->errorMessage(e);
}
}
This is the calling class, an object factory. myElement is an instance of E_TableType.
F_TableTypeFactory::F_TableTypeFactory()
{
this->myElement = myTable = 0;
}
void F_TableTypeFactory::start(QString qname)
{
this->myElement = myTable = new E_TableType(qname);
}
void F_TableTypeFactory::fill(const QString& string)
{
// don't fill complex types.
}
void F_TableTypeFactory::addChild(Element* child)
{
myTable->addElement(child);
}
Element* F_TableTypeFactory::finish()
{
return myElement;
}
void F_TableTypeFactory::addAttributes(const QXmlAttributes &attribs) {
QString tName = attribs.value(QString("id"));
myTable->setTableName(tName);
}
Have you considered using polymorphism here? If a common interface can be implemented by each of your concrete classes then all of this code goes away and things become simple and easy to change in the future. For example:
class Camera {
public:
virtual void Init() = 0;
virtual void TakeSnapshot() = 0;
}
class KodakCamera : Camera {
public:
void Init() { /* initialize a Kodak camera */ };
void TakeSnapshot() { std::cout << "Kodak snapshot"; }
}
class SonyCamera : Camera {
public:
void Init() { /* initialize a Sony camera */ };
void TakeSnapshot() { std::cout << "Sony snapshot"; }
}
So, let's assume we have a system which contains a hardware device, in this case, a camera. Each device requires different logic to take a picture, but the code has to support a system with any supported camera, so we don't want switch statements littered throughout our code. So, we have created an abstract class Camera.
Each concrete class (i.e., SonyCamera, KodakCamera) implementation will incluse different headers, link to different libraries, etc., but they all share a common interface; we just have to decide which one to create up front. So...
std::unique_ptr<Camera> InitCamera(CameraType type) {
std::unique_ptr<Camera> ret;
Camera *cam;
switch(type) {
case Kodak:
cam = new KodakCamera();
break;
case Sony:
cam = new SonyCamera();
break;
default:
// throw an error, whatever
return;
}
ret.reset(cam);
ret->Init();
return ret;
}
int main(...) {
// get system camera type
std::unique_ptr<Camera> cam = InitCamera(cameraType);
// now we can call cam->TakeSnapshot
// and know that the correct version will be called.
}
So now we have a concrete instance that implements Camera. We can call TakeSnapshot without checking for the correct type anywhere in code because it doesn't matter; we know the correct version for the correct hardware will be called. Hope this helped.
Per your comment below:
I've been trying to use polymorphism, but I think the elements differ too much. For example, E_SessionTip has an amount and status element where E_Url just has a url. I could unify this under a property system but then I lose all the nice typing entirely. If you know of a way this can work though, I'm open to suggestions.
I would propose passing the responsibility for writing the XML data to your types which share a common interface. For example, instead of something like this:
void WriteXml(Entity *entity) {
switch(/* type of entity */) {
// get data from entity depending
// on its type and format
}
// write data to XML
}
Do something like this:
class SomeEntity : EntityBase {
public:
void WriteToXml(XmlStream &stream) {
// write xml to the data stream.
// the entity knows how to do this,
// you don't have to worry about what data
// there is to be written from the outside
}
private:
// your internal data
}
void WriteXml(Entity *entity) {
XmlStream str = GetStream();
entity->WriteToXml(stream);
}
Does that work for you? I've done exactly this before and it worked for me. Let me know.
Double-dispatch may be of interest. The table (in your case) would call a virtual method of the base element, which in turns calls back into the table. This second call is made with the dynamic type of the object, so the appropriate overloaded method is found in the Table class.
#include <iostream>
class Table; //forward declare
class BaseElement
{
public:
virtual void addTo(Table* t);
};
class DerivedElement1 : public BaseElement
{
virtual void addTo(Table* t);
};
class DerivedElement2 : public BaseElement
{
virtual void addTo(Table* t);
};
class Table
{
public:
void addElement(BaseElement* e){ e->addTo(this); }
void addSpecific(DerivedElement1* e){ std::cout<<"D1"; }
void addSpecific(DerivedElement2* e){ std::cout<<"D2"; }
void addSpecific(BaseElement* e){ std::cout<<"B"; }
};
void BaseElement::addTo(Table* t){ t->addSpecific(this); }
void DerivedElement1::addTo(Table* t){ t->addSpecific(this); }
void DerivedElement2::addTo(Table* t){ t->addSpecific(this); }
int main()
{
Table t;
DerivedElement1 d1;
DerivedElement2 d2;
BaseElement b;
t.addElement(&d1);
t.addElement(&d2);
t.addElement(&b);
}
output: D1D2B
Have a Look at the Visitor Pattern, it might help you