Dependency Injection in C++

#1

In my day job as a C# developer, using dependency injection and IOC containers is an important part of the design of the applications we write. Almost everything is written against an interface which affords some flexibility, loose coupling and makes code easier to test.

I’m wondering if this a common approach when writing in C++? I have to think it might be since it is a language that supports OOP after all.

0 Likes

#2

You can try doing it in your own code, but usually when you are using larger code frameworks like Juce or Qt, you will be involving yourself a lot with the framework classes. (And sometimes they will involve nasty things like static or global variables…Juce is pretty good about that, though, because it is aimed a lot towards audio plugin development where statics and globals are a big no-no.) Those larger frameworks also often do not have a lot of “interfaces”, that is C++ classes which only have pure virtual methods.

0 Likes

#3

Static and global variables are frowned upon in C++ too I see :smile:. So, the Juce framework makes it difficult to work this way but if you were writing something from the bottom up you would prefer to do it?

0 Likes

#4

If I was doing a framework from scratch, I probably wouldn’t prefer writing a lot of “interfaces”, because I would just get users complaining the framework doesn’t do enough for them. Overall, Juce seems to be pretty good in that regard, the classes that simply need to do a lot, do a lot, and the classes that don’t need to do a lot, don’t do a lot.

1 Like

#5

These days in C++, if a class needs some behavior customization point, a std::function member often works well and doesn’t require additional classes or dependencies. C++ also has function templates which can be used for injecting dependencies in some situations.

As an example of using std::function, here’s a Component that paints itself using std::function :

class DrawClass : public Component
{
public:
	DrawClass() {}
	void paint(Graphics& g) override
	{
		if (DrawFunction) // check the callback function has been set
			DrawFunction(g,getWidth(),getHeight());
	}
	std::function<void(Graphics&, int,int)> DrawFunction;
};

Then another class can use it with something like :

    m_draw_a.DrawFunction = [](Graphics& g, int w, int h)
	{
		g.setColour(Colours::white);
		g.drawLine(0, 0, w, h);
	};
	m_draw_b.DrawFunction = [](Graphics& g, int w, int h)
	{
		g.setColour(Colours::yellow);
		g.fillEllipse(0, 0, w, h);
	};
    m_message = "Hello!";
	m_draw_c.DrawFunction = [this](Graphics& g, int w, int h)
	{
		g.setColour(Colours::white);
		g.drawText(this->m_message, 0, 0, w, h, Justification::centred);
	};
	

So, in that case, no separate new classes are needed to draw the white diagonal line, the yellow filled ellipse or the text message. m_draw_a, m_draw_b and draw_c are all DrawClass instances, potentially reducing class dependencies. Especially the draw_c case is interesting in terms of reducing class coupling because it captures information from the current class instance (“this”) in scope. The DrawClass doesn’t need to know anything about the other class involved.

2 Likes

#6

Nice :wink: You always give me great advice and avenues to investigate.

0 Likes

#7

I’m also a C# developer and a TDD guy. I’m sure there exists IOC’s for C++ but i think you don’t need it for a plugin or other small JUCE projects. For the few testable classes you will write, you can do Inversion Of Control by yourself without an IO Container. Most plugin and UI code is strongly coupled to the framework and the implementation and it does not make sense to test it in my opinion. I’m also not sure if it makes sense to test the DSP code.
It’s not possible to bootstrap the whole JUCE framework with an IOC as far as i know.

0 Likes