I wonder what is the C++ way of using dependency injection? Is that using templates or polymorphic classes? Consider the following code,
class AbstractReader
{
public:
virtual void Read() = 0;
};
class XMLReader : public AbstractReader
{
public:
void Read() { std::cout << "Reading with a XML reader" << std::endl; }
};
class TextFileReader : public AbstractReader
{
public:
void Read() { std::cout << "Reading with a Text file reader" << std::endl; }
};
class Parser
{
public:
Parser(AbstractReader* p_reader) : reader(p_reader) { }
void StartParsing() { reader->Read();
// other parsing logic
}
private:
AbstractReader* reader;
};
template<class T>
class GenericParser
{
public:
GenericParser(T* p_reader) : reader(p_reader) { }
void StartParsing()
{
reader->Read();
}
private:
T* reader;
};
1 - Which is the best method? GenericParser or Parser? I know if it is GenericParser, inheritance can be removed.
2 - If templates is the way to go, is it OK to write all the code in header files? I have seen many classes using templates writes all the code in header files rather than .h/.cpp combination. Is there any problems in doing so, something like inlining etc?
Any thoughts?
I personally prefer to use the template solution if I know the type of the reader at the compile time itself as I feel there is no run time decision to be taken here hence the polymorphism will be of no use. As far as writing the templates in header files is concerned, you have to do that to avoid getting the linker error. This is because if you write the template method in a cpp, the compiler will not be able to instantiate the template class and hence the linker will give error. Although, a couple of workarounds exist, most of the template code is written in header files.
GenericParser or Parser?
Depends on the rest of the code, the problem with the generic parser is that class you are going to inject also has to be a template.
But there is a 3rd more generic way ... boost::function and boost::lambda. All you have to ask for is a
function
with the correct (from the view of the user of the class) return type and parameters.boost::function< void ()> reader = bind( &TextFile::read, reader );
Now the user class is independent of the reader class and doesn't have to be a template.Writes all the code in header files rather than .h/.cpp combination.
That is called the seperation model is there is only one compiler that supports it (Comeau compiler). Start reading "Export" Restriction part 1 and "Export" Restrictions part 2
@CiscoIPPhone Comment on: the problem with the generic parser is that class you are going to inject also has to be a template.
As to 1. "Best" is relative. Both methods have their pluses and minuses. Templates offer raw speed, but more code is inevitably inlined (yielding more coupling), and the error messages are hard to read. Inheritance is slower and makes objects larger, but it doesn't require inlining (less coupled). It also has relatively better error messages.
For a small library, coupling matters less, and templates can be a good choice. However, as complexity of your library increases, you need to move towards a less coupled approach. If you are unsure of how large your library will grow, or don't need the speed templating would provide (or don't want to deal with the error messages), go with inheritance.
My answer to 2 follows up on 1. Inlining is needed for some consumer templates, therefor requiring code placed in the header. It's a question of coupling. Inlining increases coupling between components and can drastically increase compile times; avoid it unless you want the speed and are sure your library will remain small.
You don't have a free choice in this, based on how you want to structure your code or header files. The answer is dictated to you by the requirements of your application.
It depends on whether the coupling can be be decided at compile time or must be delayed until runtime.
If the coupling between a component and its dependencies is decided permanently at compile time, you can use templates. The compiler will then be able to perform inlining.
If however the coupling needs to be decided at runtime (e.g. the user chooses which other component will supply the dependency, perhaps through a configuration file) then you can't use templates for that, and you must use a runtime polymorphic mechanism. If so, your choices include virtual functions, function pointers or
std::function
.