If for example I have a builder set up so I can create objects like so:
Node node = NodeBuilder()
.withName(someName)
.withDescription(someDesc)
.withData(someData)
.build();
How can I make sure that all variables used to build the object have been set before the build method?
Eg:
Node node = NodeBuilder()
.withName(someName)
.build();
Isn't a useful node because the description and data haven't been set.
The reason I'm using the builder pattern is because without it, I'd need a lot of combination of constructors. For example the name and description can be set by taking a Field object, and the data can be set using a filename:
Node node = NodeBuilder()
.withField(someField) //Sets name and description
.withData(someData) //or withFile(filename)
.build(); //can be built as all variables are set
Otherwise 4 constructors would be needed (Field, Data), (Field, Filename), (Name, Description, Data), (Name, Description, Filename). Which gets much worse when more parameters are needed.
The reason for these "convenience" methods, is because multiple nodes have to be built, so it saves a lot of repeated lines like:
Node(modelField.name, modelField.description, Data(modelFile)),
Node(dateField.name, dateField.description, Data(dateFile)),
//etc
But there are some cases when a node needs to be built with data that isn't from a file, and/or the name and description are not based on a field. Also there may be multiple nodes that share the same values, so instead of:
Node(modelField, modelFilename, AlignLeft),
Node(dateField, someData, AlignLeft),
//Node(..., AlignLeft) etc
You can have:
LeftNode = NodeBuilder().with(AlignLeft);
LeftNode.withField(modelField).withFile(modelFilename).build(),
LeftNode.withField(dateField).withData(someData).build()
So I think my needs match the builder pattern pretty well, except for the ability to build incomplete objects. The normal recommendation of "put required parameters in the constructor and have the builder methods for the optional parameters" doesn't apply here for the reasons above.
The actual question: How can I make sure all the parameters have been set before build is called at compile time? I'm using C++11.
(At runtime I can just set a flag bits for each parameter and assert that all the flags are set in build)
Alternatively is there some other pattern to deal with a large number of combinations of constructors?
Disclaimer: This is just a quick shot, but I hope it gets you an idea of what you need.
If you want this to be a compiler time error, the compiler needs to know about the currently set parameters at every stage of the construction. You can achieve this by having a distinct type for every combination of currently set parameters.
template <unsigned CurrentSet>
class NodeBuilderTemplate
This makes the set parameters a part of the NodeBuilder type; CurrentSet is used as a bit field. Now you need a bit for every available parameter:
enum
{
Description = (1 << 0),
Name = (1 << 1),
Value = (1 << 2)
};
You start with a NodeBuilder that has no parameters set:
typedef NodeBuilderTemplate<0> NodeBuilder;
And every setter has to return a new NodeBuilder with the respective bit added to the bitfield:
NodeBuilderTemplate<CurrentSet | BuildBits::Description> withDescription(std::string description)
{
NodeBuilderTemplate nextBuilder = *this;
nextBuilder.m_description = std::move(description);
return nextBuilder;
}
Now you can use a static_assert in your build function to make sure CurrentSet shows a valid combination of set parameters:
Node build()
{
static_assert(
((CurrentSet & (BuildBits::Description | BuildBits::Name)) == (BuildBits::Description | BuildBits::Name)) ||
(CurrentSet & BuildBits::Value),
"build is not allowed yet"
);
// build a node
}
This will trigger a compile time error whenever someone tries to call build() on a NodeBuilder that is missing some parameters.
Running example: http://coliru.stacked-crooked.com/a/8ea8eeb7c359afc5
I ended up using templates to return different types and only have the build method on the final type. However it does make copies every time you set a parameter:
(using the code from Horstling, but modified to how I did it)
template<int flags = 0>
class NodeBuilder {
template<int anyflags>
friend class NodeBuilder;
enum Flags {
Description,
Name,
Value,
TotalFlags
};
public:
template<int anyflags>
NodeBuilder(const NodeBuilder<anyflags>& cpy) : m_buildingNode(cpy.m_buildingNode) {};
template<int pos>
using NextBuilder = NodeBuilder<flags | (1 << pos)>;
//The && at the end is import so you can't do b.withDescription() where b is a lvalue.
NextBuilder<Description> withDescription( string desc ) && {
m_buildingNode.description = desc;
return *this;
}
//other with* functions etc...
//needed so that if you store an incomplete builder in a variable,
//you can easily create a copy of it. This isn't really a problem
//unless you have optional values
NodeBuilder<flags> operator()() & {
return NodeBuilder<flags>(*this);
}
//Implicit cast from node builder to node, but only when building is complete
operator typename std::conditional<flags == (1 << TotalFlags) - 1, Node, void>::type() {
return m_buildingNode;
}
private:
Node m_buildingNode;
};
So for example:
NodeBuilder BaseNodeBuilder = NodeBuilder().withDescription(" hello world");
Node n1 = BaseNodeBuilder().withName("Foo"); //won't compile
Node n2 = BaseNodeBuilder().withValue("Bar").withName("Bob"); //will compile
Disclaimer: this is an idea. I'm not sure it even works. Just sharing.
You might try to:
remove build() method from NodeBuilder
regroup your mandatory fields into a single builder method of NodeBuilder, say NodeBuilder::withFieldData(bla, bli, blu) and/or NodeBuilder::withFieldData(structBliBlaBLU).
make withFieldData() to return a builder of a different type, say NodeBuilderFinal. Only this type of builder has build() method. You may inherit non-mandatory methods from NodeBuilder. (Strictly speaking, NodeBuilderFinal is a "Proxy" object)
This will enforce user to call withFieldData() before build(), while allowing to call other builder methods in arbitrary order. Any attempt to call build() on non-final builder will trigger compiler error. build() method will not show up in autocompletion until final builder is made ;).
If you don't want monolithic withFieldData method, you may return different proxies from each "field" method, like NodeBuilderWithName, NodeBuilderWithFile, and from those, you can return NodeBuilderWithNameAndFile, etc. until final builder will be built. This is quite hairy and will require many classes to be introduced to cover different orders of "field" calls. Similarly to what #ClaasBontus proposed in comments, you can probably generalize and simplify this with templates.
In theory, you may try to enforce more sophisticated constraints by introducing more proxy objects into the chain.
The only way I can imagine would be to have a number of static builder methods (or constructors) one for each set of required parameters that would return a builder instance, and then simple instance methods to set (or overwrite) parameters and that return the instance.
It will allow compile time checking, but at the price of a much more complex API, so I strongly advise you not to use it unless you really have good reasons to do.
This question can not be outdated. Let me share my solution to this problem.
class Car; //object of this class should be constructed
struct CarParams{
protected:
std::string name_;
std::string model_;
int numWheels_;
int color_;
struct Setter_model;
struct Setter_numWheels;
struct Setter_color;
public:
class Builder;
};
struct CarBuilder : CarParams{ //starts the construction
Setter_model& set_name(const std::string& name){
name_ = name;
return reinterpret_cast<Setter_model&>(*this);
}
};
struct CarParams::Setter_model : CarParams{
Setter_numWheels& set_model(const std::string& model){
model_ = model;
return reinterpret_cast<Setter_numWheels&>(*this);
}
};
struct CarParams::Setter_numWheels : CarParams{
Setter_color& set_numWheels(int numWheels){
numWheels_ = numWheels;
return reinterpret_cast<Setter_color&>(*this);
}
};
struct CarParams::Setter_color : CarParams{
Builder& set_color(int color){
color_ = color;
return reinterpret_cast<Builder&>(*this);
}
};
class CarParams::Builder : CarParams{
private:
//private functions
public:
Car* build();
// optional parameters
};
The class Car is defined bellow:
class Car{
private:
std::string name_;
std::string model_;
int numWheels_;
int color_;
public:
friend class CarParams::Builder;
//other functions
};
And build function in .cpp:
Car* CarParams::Builder::build(){
Car* obj = new Car;
obj->name_ = std::move(name_);
obj->model_ = std::move(model_);
obj->numWheels_ = numWheels_;
obj->color_ = color_;
return obj;
}
Maybe it is a little bit complicated, but looks nice on client side:
std::string name = "Name";
std::string model = "Model";
Car* newCar = CarBuilder()
.set_name(name)
.set_model(model)
.set_numWheels(3)
.set_color(0x00ffffff)
.build();
The error will occur in compile-time, if you miss something before build(). One more disadvantage is the strict order of arguments.
It can be combined with optional parameters.
Related
I'd like to make a class Car extendable by allowing injection of a user-made subclass of Engine.
So one user might want a diesel car:
DieselEngine *de = new DieselEngine;
de->setGlowplugTemperature(1200); // something specific for a Diesel
car->setEngine(de);
car->drive();
While the other wants something else:
FluxCapacitorEngine *fce = new FluxCapacitorEngine;
fce->setDestinationYear(1985);
car->setEngine(fce);
car->drive();
Internally, Car calls (pure) virtual methods of its instance of Engine in order to do its business. The issue now is, if at a later time the user wishes to do some more configuring on the engine, he would either have to keep a pointer e.g. of type DieselEngine* externally in order to access it, or use a dynamic cast:
if (DieselEngine *de = dynamic_cast<DieselEngine*>(car->engine()))
de->setMixRatio(2.1);
I don't find either variants particularily nice. Are there alternatives to achieve this kind of customizability/extendability?
A solution that I find lacking (current state): one could leave the implementation of the engine part inside Car, and make the user subclass the whole thing like class Delorean: public Car, so he could directly call the specific methods:
delorean->setDestinationYear(1985); // introduced method with the Delorean class
delorean->drive(); // inherited method of Car
However (and this is where the analogy becomes shaky, bear with me) I want to preserve the option of hot-swapping the engine while the Autobahn and the InsuranceCompany hold pointers to the car. This wouldn't be possible if we subclassed Car because we can't transform a car to a delorean instance without changing its pointer.
Another complication:
The current implementation of my Car doesn't have this extensible, externally settable engine. Instead it's like in the previous paragraph: all the engine parts are in the Car implementation in the form how 80% of my users need their engine. The configuration setters of the engine part of the car are directly and easily accessible via the car's public interface.
So if I now switch to the external engine concept, I'll be upsetting 80% of my users who are happy with the default engine, because instead of
car->setSparkVoltage(1000);
they would then need to write
if (DefaultGasolineEngine *dge = dynamic_cast<DefaultGasolineEngine*>(car->engine()))
dge->setSparkVoltage(1000);
ugh. They don't care that it's a DefaultGasolineEngine, they just want to set their familiar spark voltage.
In Summary: Are there alternatives to achieve this kind of customizability/extendability while maintaining a nice interface for the user to his custom class as well as to the default implementation which will be used by the majority of users?
I feel your pain on this. In my experience, your best option is to add support for a more dynamic way of setting properties: by strings. You can continue to support your existing Engine class interface, but add to it some generic property setters (and getters if you like) that will be implemented by each engine. The same property setters can be exposed by the Car class for convenience, which just call the same on its engine object.
////// base class declarations
class Engine
{
public:
// your existing API here
// support as many value types as you need
virtual bool setProperty(const std::string & name, const std::string & value) = 0;
virtual bool setProperty(const std::string & name, int value) = 0;
virtual bool setProperty(const std::string & name, float value) = 0;
};
class Car
{
public:
// your existing API here
// optional for convenience... replicate from Engine API
bool setEngineProperty(const std::string & name, const std::string & value);
bool setEngineProperty(const std::string & name, int value);
bool setEngineProperty(const std::string & name, float value);
};
////// DieselEngine implementation
// these should be declared in the DieselEngine as public static const, and defined here
const std::string DieselEngine::PROP_SPARK_VOLTAGE = "SparkVoltage";
const std::string DieselEngine::PROP_MIX_RATIO = "MixRatio";
bool DieselEngine::setProperty(const std::string & name, const std::string & value)
{
// definitely do some input validation first!
if (name == PROP_SPARK_VOLTAGE)
{
this->setSparkVoltage(atol(value.c_str()));
return true;
}
else if (name == PROP_MIX_RATIO)
{
this->setMixRatio(atof(value.c_str()));
return true;
}
return false;
}
////// Car implementation
bool Car::setEngineProperty(const std::string & name, const std::string & value)
{
return this->engine->setProperty(name, value);
}
////// Example usage
Car car;
car.setEngine(new DieselEngine());
car.setEngineProperty(DieselEngine::PROP_SPARK_VOLTAGE, "1000");
// OR
car.getEngine().setProperty(DieselEngine::PROP_SPARK_VOLTAGE, "1000");
Another added benefit of this approach is that you can easily begin configuring your engines from a configuration file, since you now support loading properties from human readable strings. A simple name / value pair or JSON file can be loaded in and the property setters can be called with the values.
The reason I am returning bool from the setters is so that you can know if the property was recognized. You should obviously do some input validation on the value itself as well. You could return an int error code instead to indicate various types of errors, or use exceptions if you prefer.
It would also be acceptable to put the property name fields (e.g. DieselEngine::PROP_SPARK_VOLTAGE) in a single namespace (e.g. EngineProps::SPARK_VOLTAGE) and then clearly document which engine types support which engine properties. In that way, your example code would look like:
////// Example usage
Car car;
car.setEngine(new DieselEngine());
car.setEngineProperty(EngineProps::SPARK_VOLTAGE, "1000");
// OR
car.getEngine().setProperty(EngineProps::SPARK_VOLTAGE, "1000");
Let's say I have a family of classes which all implement the same interface, perhaps for scheduling:
class Foo : public IScheduler {
public:
Foo (Descriptor d) : IScheduler (d) {}
/* methods */
};
class Bar : public IScheduler {
public:
Bar (Descriptor d) : IScheduler (d) {}
/* methods */
};
Now let's say I have a Scheduler class, which you can ask for an IScheduler-derived class to be started for a given descriptor. If it already exists, you'll be given a reference to it. If one doesn't exist, then it creates a new one.
One hypothetical invocation would be something like:
Foo & foo = scheduler->findOrCreate<Foo>(descriptor);
Implementing that would require a map whose keys were (descriptor, RTTI) mapped to base class pointers. Then you'd have to dynamic_cast. Something along these lines, I guess:
template<class ItemType>
ItemType & Scheduler::findOrCreate(Descriptor d)
{
auto it = _map.find(SchedulerKey (d, typeid(ItemType)));
if (it == _map.end()) {
ItemType * newItem = new ItemType (d);
_map[SchedulerKey (d, typeid(ItemType))] = newItem;
return *newItem;
}
ItemType * existingItem = dynamic_cast<ItemType>(it->second);
assert(existingItem != nullptr);
return *existingItem;
}
Wondering if anyone has a way to achieve a similar result without leaning on RTTI like this. Perhaps a way that each scheduled item type could have its own map instance? A design pattern, or... ?
The address of a function, or class static member, is guaranteed to be unique (as far as < can see), so you could use such an address as key.
template <typename T>
struct Id { static void Addressed(); };
template <typename ItemType>
ItemType const& Scheduler::Get(Descriptor d) {
using Identifier = std::pair<Descriptor, void(*)()>;
Identifier const key = std::make_pair(d, &Id<ItemType>::Addressed);
IScheduler*& s = _map[key];
if (s == nullptr) { s = new ItemType{d}; }
return static_cast<ItemType&>(*s);
}
Note the use of operator[] to avoid a double look-up and simplify the function body.
Here's one way.
Add a pure virtual method to IScheduler:
virtual const char *getId() const =0;
Then put every subclass to it's own .h or .cpp file, and define the function:
virtual const char *getId() const { return __FILE__; }
Additionally, for use from templates where you do have the exact type at compile time, in the same file define static method you can use without having class instance (AKA static polymorphism):
static const char *staticId() { return __FILE__; }
Then use this as cache map key. __FILE__ is in the C++ standard, so this is portable too.
Important note: use proper string compare instead of just comparing pointers. Perhaps return std::string instead of char* to avoid accidents. On the plus side, you can then compare with any string values, save them to file etc, you don't have to use only values returned by these methods.
If you want to compare pointers (like for efficiency), you need a bit more code to ensure you have exactly one pointer value per class (add private static member variable declaration in .h and definition+initialization with FILE in corresponding .cpp, and then return that), and only use the values returned by these methods.
Note about class hierarchy, if you have something like
A inherits IScheduler, must override getId()
A2 inherits A, compiler does not complain about forgetting getId()
Then if you want to make sure you don't accidentally forget to override getId(), you should instead have
abstract Abase inherits IScheduler, without defining getId()
final A inherits Abase, and must add getId()
final A2 inherits Abase, and must add getId(), in addition to changes to A
(Note: final keyword identifier with special meaning is C++11 feature, for earlier versions just leave it out...)
If Scheduler is a singleton this would work.
template<typename T>
T& Scheduler::findOrCreate(Descriptor d) {
static map<Descriptor, unique_ptr<T>> m;
auto& p = m[d];
if (!p) p = make_unique<T>(d);
return *p;
}
If Scheduler is not a singleton you could have a central registry using the same technique but mapping a Scheduler* / Descriptor pair to the unique_ptr.
If you know all your different subtypes of IsScheduler, then yes absolutely. Check out Boost.Fusion, it let's you create a map whose key is really a type. Thus for your example, we might do something like:
typedef boost::fusion::map<
boost::fusion::pair<Foo, std::map<Descriptor, Foo*>>,
boost::fusion::pair<Bar, std::map<Descriptor, Bar*>>,
....
> FullMap;
FullMap map_;
And we will use that map thuslly:
template <class ItemType>
ItemType& Scheduler::findOrCreate(Descriptor d)
{
// first, we get the map based on ItemType
std::map<Descriptor, ItemType*>& itemMap = boost::fusion::at_key<ItemType>(map_);
// then, we look it up in there as normal
ItemType*& item = itemMap[d];
if (!item) item = new ItemType(d);
return item;
}
If you try to findOrCreate an item that you didn't define in your FullMap, then at_key will fail to compile. So if you need something truly dynamic where you can ad hoc add new schedulers, this won't work. But if that's not a requirement, this works great.
static_cast the ItemType* to void* and store that in the map.
Then, in findOrCreate, just get the void* and static_cast it back to ItemType*.
static_casting T* -> void* -> T* is guaranteed to get you back the original pointer. You already use typeid(ItemType) as part of your key, so it's guaranteed that the lookup will only succeed if the exact same type is requested. So that should be safe.
If you also need the IScheduler* in the scheduler map just store both pointers.
I'm currently working on a college project with C++ and one of my assignments is to make a social network using inheritance and polymorphism. Currently I have a Node class that is used on a Map and Multimap (both are created manually and not used from the std). The node can hold two variables (key and data for example) and where I'm using it, the first variable can either be a pointer or a string (they let us use std::string).
The problem I'm having is that when I inherit from the "root" class (Object) and use "Object" as a data type for "key", I'm unable to pass a string created with the std as parameter to its constructor, because it doesn't inherit from my Object class. One solution is to implement my own string class and make it inherit from Object, but I was searching for other workarounds.
If there's any problem with the logic above, please tell me as I'm just beginning with C++.
EDIT 1 (some code for my Node):
class TempNode
{
private:
TempNode* next;
Key key;
T value;
public:
TempNode();
explicit TempNode(const Key thisKey, const T thisValue, TempNode* thisNext = NULL)
: key(thisKey)
, value(thisValue)
, next(thisNext)
{
}
inline Key getKey() { return key; }
inline T getValue() { return value; }
inline TempNode* getNext() { return next; }
inline void setNext(TempNode* thisNext) { next = thisNext; }
};
The string or Person types are currently used only in key, but that is with another implementation using templates (which works fine), but my teacher now requires us to apply inheritance to the entire project (to get used to it I guess).
To implement this using inheritance, you think of Key as a data type specifically designed as a key in your map/multimap implementation. Key inherits from Object, but it may provide its own, key-specific functions, such as – for example – a function repr() which generates a representation used by the map for some map-specific operations (maybe as a basis for hashing, or sorting or whatever).
The map/multimap must be used in such a way that the Key objects are stored as pointers (or std::unique_ptr, or std::shared_ptr, or whatever is appropriate), but not as copies of Key objects.
So we have:
struct Object
{
virtual ~Object()
{ }
};
/* Key class. Pointers of this type are inserted
into the map. */
class Key : public Object
{
public:
/* Must be supported by all keys: */
virtual std::string repr() const = 0;
};
We also assume there is a separate definition of Person objects:
struct Person : Object
{
Person(const std::string &name)
: name_(name)
{ }
std::string name_;
};
According to your specification, there are two flavours of Key: One that represents strings and must be initialized using a string, and another one that represents persons and must be initialized by a person pointer (I'll assume that the person-keys do not actually own these pointers, so you need to make sure the person objects they point to stay alive as long as the person-key exists).
We implement this by specializing Key into two derived classes, a PersonKey and a StringKey:
class PersonKey : public Key
{
public:
PersonKey(Person *person_ptr)
: Key() , person_ptr_(person_ptr)
{ }
virtual std::string repr() const
{
if (person_ptr_ != 0)
return std::string("Person/") + person_ptr_->name_;
else
return "<NUL>";
}
private:
Person *person_ptr_;
};
class StringKey : public Key
{
public:
StringKey(const std::string &str)
: Key() , str_(str)
{ }
virtual std::string repr() const
{
return str_;
}
private:
std::string str_;
};
When you make insertions into your map/multimap, you generate Key objects (which you represent as Key* or Key& or std::unique_ptr<Key>). When you want to insert a string, you generate them as StringKey objects, and when you want to insert them as person-pointers, you use PersonKey – but the data type of the key you insert will not reflect the specialization.
Here is an example of a general Key object (implemented as std::unique_ptr<Key>, but you may just use Key* if you are not afraid of memory leaks):
int main()
{
/* General key object: */
std::unique_ptr<Key> mykey;
/* Now it points to a string-key, initialized using
a string, as required: */
mykey.reset(new StringKey("hello"));
std::cout << "repr(mykey) == \""
<< mykey->repr()
<< '"'
<< std::endl;
/* Now the same key object is made to refer to
a person object: */
Person person("me");
mykey.reset(new PersonKey(&person));
std::cout << "repr(mykey) == \""
<< mykey->repr()
<< '"'
<< std::endl;
return 0;
}
Necessary headers for the code above are:
#include <iostream>
#include <memory>
#include <string>
(But memory is only required for my use of std::unique_ptr, which is not actually necessary to solve your problem.)
I think what you are really looking for are templates. Your solution with "root object" won't work as you can see with standard objects and external libraries but also you will not be able to use your containers with primitives (for example person id(as int) as key, and Person class as value).
With templates you can say what type you are going to work with at compile time and compiler will help you to obey your own rules. It can be declared like this:
template<class T1, class T2>
class Map{
T1 key;
T2 value;
(...)
}
Then you can use it more or less like this:
Map<std::String, int> phoneBook;
And compiler will guard you and warn, if you try to add, for example float instead of int, to you Map. But before you start coding I advice you to read some tutorials first, or maybe even some book on c++ in general. But if you want to start with generic right now, you can start her:
http://www.cplusplus.com/doc/tutorial/templates/
The only way you'd be able to store a string in your Object variable was if the string class inherited from your Object class, so you will have to implement your own String class unfortunately.
The real flaw here is that you are taking a Java/C# approach to design, where an Object variable can hold anything. In C++ the proper way to handle such things is through the use of templates, supposing your Map/Multimap/Node only need to hold one specific data type.
If your container needs to be able to hold any arbitrary data type, I would recommend using type erasure, although that can be a bit complicated for a beginner.
Is there any efficient way in C++ of generating an ID unique to the class, not to the instance? I'm looking for something of this level of simplicity (this generates an ID for every instance, not for every class type):
MyClass::MyClass()
{
static unsigned int i = 0;
id_ = i++;
}
Edit: Why I want unique IDs.
I'm writing a game. All entities in my game will have different states they can be in (walking left, jumping, standing, etc); these states are defined in classes. Each state needs to have its own ID so I can identify it.
You can try this, but it's not-deterministic.
int id_count = 0;
template <typename T>
int get_id()
{
static int id = id_count++;
return id;
}
Then just use:
get_id<int>(); // etc.
Of course, this isn't thread safe.
Again, it's not deterministic: the IDs are generated the first time you call the function for each type. So, if on one run you call get_id<int>() before get_id<float>() then on another run you call them the other way round then they'll have different IDs. However, they will always be unique for each type in a single run.
Basically you are asking for a custom rolled RTTI solution, that you can selectively apply to classes.
This can start from very crude preprocessor stuff like :
#define DECLARE_RTTI_CLASS(a) class a { \
inline const char * class_id() { return #a };
.. to a more sophisticated solutions that track inheritance etc, essentially partially duplicating compiler RTTI functionality. For an example, see Game Programming Gems #2, Dynamic Type Information
Previous discussions on gamedev on the same subject are also worth reading
Use your MyClass as a primitive, and incorporate a static instance of one into each class you want to ID.
class MyOtherClass1 {
static MyClass id;
};
class MyOtherClass2 {
static MyClass id;
};
[etc.]
I'm applying the Factory design pattern in my C++ project, and below you can see how I am doing it. I try to improve my code by following the "anti-if" campaign, thus want to remove the if statements that I am having. Any idea how can I do it?
typedef std::map<std::string, Chip*> ChipList;
Chip* ChipFactory::createChip(const std::string& type) {
MCList::iterator existing = Chips.find(type);
if (existing != Chips.end()) {
return (existing->second);
}
if (type == "R500") {
return Chips[type] = new ChipR500();
}
if (type == "PIC32F42") {
return Chips[type] = new ChipPIC32F42();
}
if (type == "34HC22") {
return Chips[type] = new Chip34HC22();
}
return 0;
}
I would imagine creating a map, with string as the key, and the constructor (or something to create the object). After that, I can just get the constructor from the map using the type (type are strings) and create my object without any if. (I know I'm being a bit paranoid, but I want to know if it can be done or not.)
You are right, you should use a map from key to creation-function.
In your case it would be
typedef Chip* tCreationFunc();
std::map<std::string, tCreationFunc*> microcontrollers;
for each new chip-drived class ChipXXX add a static function:
static Chip* CreateInstance()
{
return new ChipXXX();
}
and also register this function into the map.
Your factory function should be somethink like this:
Chip* ChipFactory::createChip(std::string& type)
{
ChipList::iterator existing = microcontrollers.find(type);
if (existing != microcontrollers.end())
return existing->second();
return NULL;
}
Note that copy constructor is not needed, as in your example.
The point of the factory is not to get rid of the ifs, but to put them in a separate place of your real business logic code and not to pollute it. It is just a separation of concerns.
If you're desperate, you could write a jump table/clone() combo that would do this job with no if statements.
class Factory {
struct ChipFunctorBase {
virtual Chip* Create();
};
template<typename T> struct CreateChipFunctor : ChipFunctorBase {
Chip* Create() { return new T; }
};
std::unordered_map<std::string, std::unique_ptr<ChipFunctorBase>> jumptable;
Factory() {
jumptable["R500"] = new CreateChipFunctor<ChipR500>();
jumptable["PIC32F42"] = new CreateChipFunctor<ChipPIC32F42>();
jumptable["34HC22"] = new CreateChipFunctor<Chip34HC22>();
}
Chip* CreateNewChip(const std::string& type) {
if(jumptable[type].get())
return jumptable[type]->Create();
else
return null;
}
};
However, this kind of approach only becomes valuable when you have large numbers of different Chip types. For just a few, it's more useful just to write a couple of ifs.
Quick note: I've used std::unordered_map and std::unique_ptr, which may not be part of your STL, depending on how new your compiler is. Replace with std::map/boost::unordered_map, and std::/boost::shared_ptr.
No you cannot get rid of the ifs. the createChip method creats a new instance depending on constant (type name )you pass as argument.
but you may optimaze yuor code a little removing those 2 line out of if statment.
microcontrollers[type] = newController;
return microcontrollers[type];
To answer your question: Yes, you should make a factory with a map to functions that construct the objects you want. The objects constructed should supply and register that function with the factory themselves.
There is some reading on the subject in several other SO questions as well, so I'll let you read that instead of explaining it all here.
Generic factory in C++
Is there a way to instantiate objects from a string holding their class name?
You can have ifs in a factory - just don't have them littered throughout your code.
struct Chip{
};
struct ChipR500 : Chip{};
struct PIC32F42 : Chip{};
struct ChipCreator{
virtual Chip *make() = 0;
};
struct ChipR500Creator : ChipCreator{
Chip *make(){return new ChipR500();}
};
struct PIC32F42Creator : ChipCreator{
Chip *make(){return new PIC32F42();}
};
int main(){
ChipR500Creator m; // client code knows only the factory method interface, not the actuall concrete products
Chip *p = m.make();
}
What you are asking for, essentially, is called Virtual Construction, ie the ability the build an object whose type is only known at runtime.
Of course C++ doesn't allow constructors to be virtual, so this requires a bit of trickery. The common OO-approach is to use the Prototype pattern:
class Chip
{
public:
virtual Chip* clone() const = 0;
};
class ChipA: public Chip
{
public:
virtual ChipA* clone() const { return new ChipA(*this); }
};
And then instantiate a map of these prototypes and use it to build your objects (std::map<std::string,Chip*>). Typically, the map is instantiated as a singleton.
The other approach, as has been illustrated so far, is similar and consists in registering directly methods rather than an object. It might or might not be your personal preference, but it's generally slightly faster (not much, you just avoid a virtual dispatch) and the memory is easier to handle (you don't have to do delete on pointers to functions).
What you should pay attention however is the memory management aspect. You don't want to go leaking so make sure to use RAII idioms.