I have a question about best practices for dependency injection with polymorphic classes. I'm new to C++, so please forgive me if this is an obvious question. Say I have a class Runner, which needs to take in two objects, a Logger and a Worker. Logger is an abstract class with two children, say FileLogger and SocketLogger. Similarly, Worker is an abstract class with two children, say ApproximateWorker and CompleteWorker.
The Runner class will be created from main() and will create the Logger and Worker based on a config file or something similar. I've done a lot of reading on SO and other places, and the general sentiment seems to be to prefer stack allocated objects and pass them in by reference. I'm not quite sure how to manage dynamically creating the objects like this, however. If using heap-allocated objects, I could do something like:
Logger* log;
Worker* worker;
if (/*user wants a file logger*/ {
log = new FileLogger();
} else {
log = new SocketLogger();
}
if (/* user wants an approximate worker*/) {
worker = new ApproximateWorker();
} else {
worker = new CompleteWorker();
}
Runner runner = Runner(log, worker);
runner.run();
Because I'm just storing the pointers on the stack, I can handle the different cases for Logger and Worker independently. If using stack-allocated objects, the only thing I can think would be do to something like:
if (/*file logger and approx worker*/) {
FileLogger log();
ApproximateWorker worker();
Runner runner = Runner(log, worker);
} else if (/*file logger and complete worker*/) {
FileLogger log();
CompleteWorker worker();
Runner runner = Runner(log, worker);
} else if (/*socket logger and approx worker*/) {
SocketLogger log();
ApproximateWorker worker();
Runner runner = Runner(log, worker);
} else {
SocketLogger log();
CompleteWorker worker();
Runner runner = Runner(log, worker);
}
Obviously, with more than two objects to pass in, or more than two subclasses per object, this quickly becomes ridiculous. My understanding is that object slicing will prevent you from doing something similar to the first snippet.
Am I missing something obvious here? Or is this a case for using dynamic memory (with smart pointers of course)?
If Runner will use these objects in a polymorphic way (access derived objects via base class interfaces), you should pass pointers or references to it. There are pros and cons of variables on stack and on heap. There is no universal rule that one is preferred over the other.
One thing more, abstract factory pattern may suit your case. It separates WHAT(exact types of objects are used) from HOW(these objects are used). It's all about encapsulating the change.
// Factory.h
class tAbstractFactory
{
public:
virtual Logger* getLogger() = 0;
virtual Worker* getWorker() = 0;
};
template<typename loggerClass, typename workerClass>
class tConcreteFactory: public tAbstractFactory
{
public:
loggerClass* getLogger() { return new loggerClass; }
workerClass* getWorker() { return new workerClass; }
};
// Runner.h
class Runner
{
public:
Runner(tAbstractFactory &fa)
{
m_logger = fa.getLogger();
m_worker = fa.getWorker();
}
private:
Logger *m_logger;
Worker *m_worker;
};
// Factory.cpp
tAbstractFactory &getFactory(int sel)
{
if (sel == 1)
{
static tConcreteFactory<FileLogger, ApproximateWorker> fa;
return fa;
}
else if (sel == 2)
{
static tConcreteFactory<FileLogger, CompleteWorker> fa;
return fa;
}
else if (sel == 3)
{
static tConcreteFactory<SocketLogger, ApproximateWorker> fa;
return fa;
}
else
{
static tConcreteFactory<SocketLogger, CompleteWorker> fa;
return fa;
}
}
// Client.cpp
Runner runner(fac);
Edit:
At least two benefits I can see:
When you add a new case or change the type of concrete Logger/Worker, Client.cpp won't be affected. That said, you limit the change inside Factory.cpp so that the client logic(which actually uses the created objects) is unchanged.
Runner is programmed to only the factory interface. Clients depending on the Runner interface won't be affected by the change of Logger, Worker, etc.
Personally, it's totally OK not to use this pattern for a small code base. In a large project where there are lots of dependencies among classes/files, it will make a difference, both to the compilation time and scalability.
Shared or unique pointers can help, but you can still take references to the object as dependency injected variables.
You do need to make sure that you don't destroy the objects (logger, worker) before the runner. Dependency injection asks for factories. In this case I use a unique_ptr not for passing around ownership, but as a RAII safe handle to the abstract type.
#include <iostream>
#include <memory>
#include <exception>
struct Logger{
virtual void log() =0;
};
struct Logger1 : Logger {
void log() override { std::cout << " l1 " << std::endl;}
};
struct Logger2 : Logger {
void log() override { std::cout << " l2 " << std::endl;}
};
struct Logger3 : Logger {
void log() override { std::cout << " l3 " << std::endl;}
};
struct Worker{
virtual void work() =0;
};
struct Worker1 : Worker{
void work() override { std::cout << " w1 " << std::endl;}
};
struct Worker2 : Worker{
void work() override { std::cout << " w2 " << std::endl;}
};
struct Worker3 : Worker{
void work() override { std::cout << " w3 " << std::endl;}
};
struct Runner{
Runner(Worker& worker, Logger& logger): worker(worker),logger(logger) {};
Worker& worker;
Logger& logger;
void run(){
worker.work();
logger.log();
}
};
std::unique_ptr<Worker> mkUniqueWorker(int i){
switch (i) {
case 1: return std::make_unique<Worker1>() ;
case 2: return std::make_unique<Worker2>() ;
case 3: return std::make_unique<Worker3>() ;
case 4: throw std::runtime_error("unknown worker");
}
};
std::unique_ptr<Logger> mkUniqueLogger(int i){
switch (i) {
case 1: return std::make_unique<Logger1>() ;
case 2: return std::make_unique<Logger2>() ;
case 3: return std::make_unique<Logger3>() ;
case 4: throw std::runtime_error("unknown logger");
}
};
int main() {
auto worker = mkUniqueWorker(2);
auto logger = mkUniqueLogger(3);
Runner runner = Runner(*worker, *logger);
runner.run();
return 0;
}
Related
I am working on an application that parses user input (command) and tries to process the command. There are various types of command. So I created one Base class, which all commands will inherit from. Now I am trying to figure out how to correctly process the command. What I would like to do is to create an instance of the Base class and then somehow recast it to the correct descendant. Ideally, this would be done at the level of the command classes, eg Base, Der1, Der2, no in main function as in example.
main.cpp
#include "class1.hpp"
int main(int argc, const char * argv[]) {
Base com("some input");
com.process(); //based on the input I want to distinguish which of the derived classes should be used
}
class1.hpp
#include <iostream>
class Base {
std::string input;
public:
Base(std::string input) {
this->input = input;
}
virtual void process(){}
};
class Der1 {
public:
Der1(std::string input);
void process() {
std::cout << "Der1 process" << std::endl;
}
};
class Der2 {
public:
Der2(std::string input);
void process() {
std::cout << "Der2 process" << std::endl;
}
};
Is this correct attitude when parsing user input? Or maybe is there some different approach that I might try? I prefer to use polymorphism as part of learning c++.
Thank you for any answer.
Is this correct attitude when parsing user input?
In C++, we usually try to keep the full type instead of erasing it to a base class: then we need no virtual dispatch, can fully use templates/overloads etc.
Or maybe is there some different approach that I might try?
Definitely: try to use static polymorphism instead. I think in your example something like this would be enough:
struct Der1 {
Der1(std::string_view); // TODO
auto process() { return "processed by Der1"sv; }
};
struct Der2 {
Der2(std::string_view); // TODO
auto process() { return "processed by Der2"sv; }
};
auto process(std::string_view input) {
switch (input.at(0)) { // I don't know how you actually want to choose DerN
case '1': return Der1{input}.process();
case '2': return Der2{input}.process();
default: throw std::runtime_error{"couldn't decide how to process"};
}
}
int main() { std::cout << process("1some input") << '\n'; }
So what I'm trying to achieve here is casting a derived subclass into another subclass derived from the same subclass. This far it's looking like it's not possible to actually be done but I'm still believing.
My example code is:
#include <iostream>
#include <vector>
class BaseClass
{
public:
virtual void printFunction() = 0;
};
class printOne : public BaseClass
{
public:
void printFunction() override
{
std::cout << "One\n";
}
};
class printTwo : public BaseClass
{
public:
void printFunction() override
{
std::cout << "Two\n";
}
};
int main()
{
std::vector<BaseClass *> baseClassVector;
printOne * one = new printOne;
baseClassVector.push_back(one);
printTwo * two = new printTwo;
baseClassVector.push_back(two);
}
So what i want to actually do here with this vector is that I want to change the "one" object on index zero, to a "two" object. Now this can be done through the code
delete baseClassVector[0];
printTwo * two = new printTwo;
baseClassVector[0] = two;
However as far as I know, this is extremely costly, especially if it has to be done at runtime. I was wondering if there's another way to go about doing this or if the costs are worth it compared to other alternatives.
Thanks in advance!
With the simplified example in the question, use a std::variant which is simpler and just avoid the base class altogether:
class printOne
{
public:
void printFunction() const
{
std::cout << "One\n";
}
};
class printTwo
{
public:
void printFunction() const
{
std::cout << "Two\n";
}
};
using printEither = std::variant<printOne, printTwo>;
void printFunction(const printEither& e)
{
std::visit([](auto& p) { p.printFunction(); }, e);
}
int main()
{
std::vector<printEither> eitherVector;
printOne one;
eitherVector.push_back(one);
printTwo two;
eitherVector.push_back(two);
eitherVector[0] = two;
for (auto& e: eitherVector)
printFunction(e);
}
Re-using an allocation for effectively unrelated types in C++ is a pain to write correctly. It is easier and preferable to incur an allocation.
It is technically possible to "rebuild" an object in place as a different type, though the following should be taken as just a proof of concept, not a recommendation for design or practice. First price to pay is giving up the convenience of new/delete for manually managed placement new and explicit destructors used with malloc/free.
const size_t sz = max(sizeof(printOne), sizeof(printTwo));
BaseClass *either = (BaseClass *)malloc(sz); // allocate memory for objects
new(either) printOne(); // construct printOne object
printOne *one = dynamic_cast<printOne *>(either); // ... use printOne object
one->~printOne(); // destruct printOne object
new(either) printTwo(); // construct printTwo object
printTwo *two = dynamic_cast<printTwo *>(either); // ... use printTwo object
two->~printTwo(); // destruct printTwo object
free(either); // free memory used by objects
I have a large class with many methods. This class has a subclass that manages a different situation.
Just to clear it up with an example the actual situation is the following:
class Logic {
public:
virtual void method()
{
Something::getInstance()->doSomething();
}
};
class ArrayLogic : public Logic {
private:
Something** array;
public:
void method() override
{
for (int i = 0; i < AMOUNT; ++i)
array[i]->doSomething();
}
};
Now this pattern repeats itself in multiple methods and I'd like to have just one implementation without trading for performance (since some of this methods are actually already proven to require efficiency).
I was thinking if it's possible with C++11 to have a template solution approach which is able to manage this situation at compile time without the necessity to duplicate the code.
Mind that the array doesn't make sense to exist for Logic so having a Something*[1] is not a viable option.
An additional problem is that at the moment Something** array is not directly contained in ArrayLogic but resides in another class, so it's more like
class ArrayLogic : public Logic {
private:
Container* container;
public:
void method() override {
for (int i = 0; i < AMOUNT; ++i)
if (container->array[i])
container->array[i]->doSomething();
}
}
While having to check for container->array[i] != nullptr may seems strange the fact is that the position is relevant, so an element removed from the array doesn't cause a shift of the successive element but leaves a hole.
I'd try and create separate classes for single and multiplayer games. Base both of these on a base class LogicBase that has a method(Something*) function that calls doSomething() on its parameter. This is what #Pradhan was referring to.
In your main game, you can use a LogicBase* to refer to either a SinglePlayerLogic or a MultiPlayerLogic object and call the relevant method() using a virtual function call.
I'm passing what is stored in Container to the constructor of MultiPlayerLogic. But it could be in a separate class and accessed that way. Similarly, it may be cleaner to pass a Something to the constructor of SinglePlayerLogic, but I wanted to keep the code structure close to your original, so didn't do this.
It initially looks funny for LogicBase to call to a subclass, then have those subclasses call the protected method(Something*) back in the super class. I've seen it elsewhere as a design pattern, but can't recall it's name.
#include <iostream>
#include <vector>
const int AMOUNT = 5;
struct Something {
void doSomething() { std::cout << "Something::doSomething\n"; }
static Something* getInstance() { static Something s; return &s; }
};
class LogicBase {
public:
virtual void method() = 0;
protected:
void method(Something* s) { s->doSomething(); }
};
class SinglePlayerLogic : public LogicBase {
public:
void method() override
{
std::cout << "SinglePlayer::method\n";
LogicBase::method(Something::getInstance());
}
};
class MultiPlayerLogic : public LogicBase {
public:
MultiPlayerLogic(Something **s) : players(s) {}
void method() override
{
std::cout << "MultiPlayer::method\n";
for (int i = 0; i < AMOUNT; ++i) {
if (players[i] == nullptr) {
continue;
}
std::cout << i << " ";
LogicBase::method(players[i]);
}
}
private:
Something** players;
};
int main() {
LogicBase* lb;
SinglePlayerLogic spl;
lb = &spl;
lb->method();
std::vector<Something*> players{AMOUNT};
MultiPlayerLogic mpl(players.data());
lb = &mpl;
lb->method();
}
Pretty much the title: Can objects be created based on type_info? The purpose of this would be to defer the creation of objects. For instance, here's the original "undeferred" code:
Foo* a = new Foo();
Bar* b = new Bar();
And here's the deferred one:
// Store type indices into a vector
std::vector<std::type_index> types;
types.push_back(std::type_index(typeid(Foo)));
types.push_back(std::type_index(typeid(Bar)));
// Iterate through vector, create objects? Is it possible?
If this is not possible, is there any other way to "defer" the construction of objects?
In c++ there is no equivalent of creating objects based on runtime-known types. Languages such as C# and Java can do this precisely because of their extensive reflection support, which is mostly lacking in c++.
One interesting side effect of all this is that c++ developers are never able to slide into the tempting area of reflecting everything. Because of the sheer convenience of reflection-based development lots of core functionality in enterprise applications built with C# and Java revolves around reflection. I'm thinking in particular of OR/M software and libraries such as AutoMapper for C# which make such extensive use of reflection that the overall performance of applications that use them suffers significantly (speaking from personal experience). It is actually refreshing to me to be prevented from that in c++.
The good news is that plugin architectures are very possible using a double inversion of control architecture. Following is a very simple example showing how a separate dll or so could dynamically register types using basic polymorphism.
#include <string>
#include <list>
#include <unordered_map>
#include <iostream>
// ------------------------------------------------------------
// Host application
class Vehicle
{
public:
virtual ~Vehicle() {} // Allow proper inheritance
virtual void Start() = 0; // Start the vehicle
};
class VehicleFactory
{
public:
virtual ~VehicleFactory() {} // Allow proper inheritance
virtual Vehicle* Create() = 0;
};
class VehicleTypeFactory
{
public:
void RegisterFactory(std::string vehicleType, VehicleFactory* vehicleFactory)
{
_factories.insert(std::pair<std::string, VehicleFactory*>(vehicleType, vehicleFactory));
}
Vehicle* Create(std::string vehicleType)
{
return _factories.at(vehicleType)->Create();
}
std::list<std::string> GetTypes()
{
std::list<std::string> result;
for(auto& item: _factories)
{
result.push_back(item.first);
}
return result;
}
private:
std::unordered_map<std::string, VehicleFactory*> _factories;
};
class Tractor: public Vehicle
{
public:
virtual void Start()
{
std::cout << "Starting Tractor..." << std::endl;
std::cout << "Turning on hydraulics..." << std::endl;
}
};
class TractorFactory: public VehicleFactory
{
public:
virtual Vehicle* Create()
{
return new Tractor();
}
};
// ------------------------------------------------------------
// Plugin library (.dll, .so)
// plugin introduces brand new type of vehicle
class Limousine: public Vehicle
{
public:
virtual void Start()
{
std::cout << "Starting Limousine..." << std::endl;
std::cout << "Turning on limo accessories..." << std::endl;
}
};
class LimousineFactory: public VehicleFactory
{
public:
virtual Vehicle* Create()
{
return new Limousine();
}
};
// ------------------------------------------------------------
// Host startup: register tractor factory
int main()
{
VehicleTypeFactory vehicleTypeFactory;
TractorFactory tractorFactory;
vehicleTypeFactory.RegisterFactory("tractor", &tractorFactory);
// ... load plugin(s) which will register other types of factories
// (
LimousineFactory limousineFactory;
vehicleTypeFactory.RegisterFactory("limousine", &limousineFactory);
// )
// Now create one of each type of vehicle
// and tell it to start itself
for(auto& vehicleType: vehicleTypeFactory.GetTypes())
{
auto vehicle = vehicleTypeFactory.Create(vehicleType);
vehicle->Start();
}
return 0;
}
Expected output:
Starting Limousine...
Turning on limo accessories...
Starting Tractor...
Turning on hydraulics...
I'm trying to make a sanely-usable implementation of my events system. In order to identify event types,
I have what I call a "type path,' which identifies the path to an event type through the hierarchy.
This way, I can handle, for example, all InputEvents at one place whether they're key presses, mouse input,
or whatever else. A sticky issue, though, has been giving event types their identities. What I've most
recently done is do this by having each instance retrieve a leaf identity from a static member function
of the Event class, which serves simply as an interface other than performing this function.
However, the simplest way to ensure that each type has exactly one identity within this structure
seemed to be to use maps based on type paths (up to but excluding the leaf identity/identifier)
and typeid().hash_code().
Specifically, what I want to have is a system to which events can be added easily without
having to look up a bunch of information or perform a lot of silly boilerplate crap. Considering this
(and possibly things I'm not realizing I should want?),
Is this design flawed in any obvious ways?
Is there a Betterâ„¢ way than to use typeid()? I've read a bit about it and it seems to be considered something that, depending on the person whose opinion is being asked, should either never be used or be used almost never. As it's possible that I'm just rusty or being stupid, I'd like to know if anyone knows of a solution that is, if nothing else, less uncertain (apparently some implementations of typeid() are pretty bad, though I don't know which or if it's bad enough to seriously matter).
Fairly simple example of what I have now:
#include <iostream>
#include <vector>
#include <typeinfo>
#include <map>
void spew(std::vector<unsigned int> vect) { for (unsigned int i=0;i<vect.size();++i) std::cout << vect.at(i) << ","; std::cout << std::endl; }
class Foo
{
public:
Foo() {}
virtual ~Foo() {}
static unsigned int getSubtype(std::vector<unsigned int> typePath, Foo *evt)
{
static std::map<std::vector<unsigned int>, std::map<std::size_t, unsigned int> > typeMap;
std::size_t typehash = typeid(*evt).hash_code();
if (typeMap.find(typePath) == typeMap.end())
{
unsigned int val = typeMap[typePath].size();
typeMap[typePath][typehash] = val;
return val;
}
else
{
if (typeMap[typePath].find(typehash) == typeMap[typePath].end())
{
unsigned int val = typeMap[typePath].size();
typeMap[typePath][typehash] = val;
return val;
}
return typeMap[typePath][typehash];
}
}
virtual void test() { std::cout << "Foo" << std::endl; }
protected:
std::vector<unsigned int> m_typePath;
};
class Bar : public Foo
{
public:
Bar()
{
m_typePath.push_back(Foo::getSubtype(m_typePath, this));
test();
}
virtual ~Bar() {}
virtual void test() { std::cout << "Bar: "; spew(m_typePath);}
};
class Baz : public Foo
{
public:
Baz()
{
m_typePath.push_back(Foo::getSubtype(m_typePath, this));
test();
}
virtual ~Baz() {}
virtual void test() { std::cout << "Baz: "; spew(m_typePath);}
};
class Qux : public Baz
{
public:
Qux()
{
m_typePath.push_back(Foo::getSubtype(m_typePath, this));
test();
}
virtual ~Qux() {}
virtual void test() { std::cout << "Qux: "; spew(m_typePath);}
};
int main()
{
Foo foo0;
std::cout << "----" << std::endl;
Bar bar0;
std::cout << "----" << std::endl;
Baz baz0;
std::cout << "----" << std::endl;
Qux qux0;
}
Output:
----
Bar: 0,
----
Baz: 1,
----
Baz: 1,
Qux: 1,0,
This and other tests exhibit the desired behavior, to be clear.
Edit: Previous title didn't really match what I mean to ask.
Possibly relevant notes: This is meant for part of a library, and a highly parallel one at that. I've omitted code relevant to concurrency for simplicity of representing the design, but it may be that such information would be useful for design purposes as well. Also note that I'm still only asking for help with creating/assigning type identifiers; I mention these because some designs may not be applicable given their implied constraints.
Win edit:
Well, I have an implementation that's ridiculously fast and does exactly what I need. With a few derived classes, I can instantiate ten million per ~thread(I added in TBB for some other tests; it may or may not use exactly eight threads however it pleases) spread across the derived classes, each having two or more elements in its path, in typically well under .02s. Original implementation managed about four or five seconds depending on containers and such and was just silly. Result (enough to get the idea, anyway):
template<typename T> class EventID
{
public:
static const std::size_t typeID;
};
template<typename T> const std::size_t EventID<T>::typeID = typeid(T).hash_code();
class Foo
{
public:
Foo()
{
m_typePath.push_back(EventID<Foo>::typeID);
}
protected:
neolib::vecarray<std::size_t, 100, neolib::nocheck> m_typePath;
};
class Bar : public Foo
{
public:
Bar()
{
m_typePath.push_back(EventID<Bar>::typeID);
}
};
I would rely on the class hierarchy that the compiler maintains, plus a list of type codes:
typedef enum { EVENT, INPUT, MOUSE, MOUSEDOWN, MOUSEUP, MOUSEMOVE, KEYBOARD, KEYDOWN, KEYUP } EventType;
typedef std::vector<EventType> ETVector;
class Event
{
public:
virtual void appendType(ETVector& v) { v.push_back(EVENT); }
};
class InputEvent : public Event
{
public:
virtual void appendType(ETVector& v) { v.push_back(INPUT); Event::appendType(v); }
};
class MouseEvent : public InputEvent
{
public:
virtual void appendType(ETVector& v) { v.push_back(MOUSE); InputEvent::appendType(v); }
};
class MouseDownEvent : public MouseEvent
{
public:
virtual void appendType(ETVector& v) { v.push_back(MOUSEDOWN); MouseEvent::appendType(v); }
};
class MouseUpEvent : public MouseEvent
{
public:
virtual void appendType(ETVector& v) { v.push_back(MOUSEUP); MouseEvent::appendType(v); }
};
class MouseMoveEvent : public MouseEvent
// . . .
class KeyboardEvent : public InputEvent
// . . .
class KeyDownEvent : public KeyboardEvent
// . . .
class KeyUpEvent : public KeyboardEvent
// . . .
Then to do your test, you would have something like this:
KeyUpEvent kue;
EventTypeVector type_path;
kue.appendType(type_path);
for (EventTypeVector::const_iterator i = type_path.begin(); i != type_path.end(); i++)
{
cout << *i << endl;
}
The vector stores your type path. There is no runtime storage cost, and no static variables. It might be possible to use typeid instead of a manually maintained enum, but with an enum everything is under your control, and you can easily avoid conflicts with other types in your program. Alternatively, it might be possible to imbue a class with the appendType() method by means of a template, to reduce the code even further.
It is a little tedious to have to explicitly name the parent class in appendType(), but I know of no other way in C++, thanks to multiple inheritance. In Java I could have used super, though reflection would probably be a better approach.
Is this simpler than what you have, or am I missing something? Good luck.
See final edit to original post for solution.