When I write about C++, I’m generally writing about some cool feature it has, or why I like it, or some other positive context for using C++ to solve a problem. But C++ isn’t the only language I use, and – while it’s intended as a general-purpose tool – I don’t think anyone seriously proposes it as the panacea for all software projects.
Having said that, when you like using a particular tool (and I do like C++, don’t judge me), it’s easy to see how it can be used to solve lots of different problems. C++ is usually the thing I think of first, when some new problem or idea is presented to me, and I find it tempting to plow ahead from that point, and ignore the possibility of other solutions.
So I thought it would be helpful to outline some situations where C++ may not be the right tool for the job, or where better solutions exist.
One thing that’s hard to ignore about C++ is that getting a new project up and going is kind of laborious, compared with scripting languages like python or ruby. You have to sort out a build system and where your executable will run from, and so on. IDEs can solve some of this, but if you don’t already have the IDE set up on your system, then you can write off an hour or so, just to get that installed and configured.
Contrast this with simply opening a text editor, and writing a few lines of shell script. Even if you have to install interpreters like python or ruby, this is still less trouble than a C++ tool-chain (usually).
It’s really an issue of scale. If you have a large, complex thing to achieve, then a well set up project and build system isn’t such a large comparative burden, but if all you want to do (for example) is some filtering on a text file, then the C++ project set up is likely to be more complex than the actual code. Contrast that with the few lines you’d likely write in a single python, ruby or perl file, and the choice is obvious.
Frequent Incremental Changes
The traditional compile-link-run cycle in C++ development places a definite gap between development and run-time. Some (older) schools of thought suggest that this distinction is good discipline, promoting separation of concerns and clearer thinking in the software development life-cycle.
It’s probably worth recognising that this thinking was probably the result of the longer build process that is characteristic of compiled languages. This forced developers to to think in terms of separate development and run-time phases, but there are times when that distinction is undesirable, and simply gets in the way.
The first instance of this that comes to mind is prototyping and Rapid Application Development (RAD). The C++ build cycle can be quite time-consuming, which makes it more difficult to try something out and quickly see what it does.
Another example is when the system needs to support significant changes to behaviour after building and deploying (i.e. at run-time), using plugins, or scripts, or pretty much anything beyond what can be reasonably included in a config file. Plugins and run-time scripts can certainly be implemented in compiled languages like C++, but they’re much easier in interpreted languages because the interpreter can simply be invoked over the introduced code. Compiled languages need more complex systems of APIs and binary interfaces (Windows DLLs, anyone?)
Having said that, it’s worth noting that there are ways to mitigate lengthy C++ build times. Support for incremental builds and precompiled headers are good examples. C++ RAD environments like Borland C++ Builder were all the rage in the 90s, and they made extensive use of these techniques to reduce the impact of small code changes on build times, encouraging a ‘small iterations’ approach. Visual Studio’s C++ compiler also implements techniques like this.
The “Pointer to Implementation” (PImpl) idiom is another example. It’s a way of breaking the compile-time dependency of an interface on its implementation, so that implementation don’t have a compile-time impact on the rest of the code.
Neither of these ideas really address the second case (changes to run-time behaviour), though.
Speed or Resource Efficiency Less Important
In most people’s minds, he overriding advantages of C++ over other languages are its speed and resource efficiency. This may or may not have some basis in reality, depending on the problem you’re trying to solve, but what if speed and efficiency are not significant concerns for that problem?
Setting aside the fact that there are other fast, efficient languages out there, and also that C++ has other strengths, if you don’t care about speed and efficiency, then you probably care about something else (e.g. the run-time flexibility alluded to earlier) in which C++ might be a bit weak.
The underlying message here, of course, is to use C++ for its strengths, rather than its weaknesses, as with any tool, and the corollary is that more tools – and more diverse tools – give you more options.
I hesitate to include this case, because there’s nothing implicit in C++ that makes it difficult to create web-based applications. The reason I have included it is that there are so many excellent frameworks for web-based languages in other languages and not so many in C++. It’s not so much a deficiency in the language itself as in the ecosystem of libraries around it.
In fact, this is an exception to the more generic strengths of C++: the wealth of libraries and code that are available to use with it. C++ has been around for a while, and – setting aside the glaring lack of standardised package management (hopefully conan.io is the answer here) – you can find excellent C++ code components and libraries for just about anything. Web application frameworks for C++ do exist (examples include Wt, Rude and CppCMS), but none have emerged as clear winners comparable to the popular choices like Django or Rails.
Finally, there are several contexts for which there seems to be a lot of false information and FUD suggesting that C++ isn’t the best solution for one reason or another. Because C++ is seen by some a something of a ‘black art’, and therefore intimidating, it’s tempting to endorse any plausible reason not to use it, even if those ideas aren’t really supported by facts. Some of these ‘non-contraindications’ are:
- Portability – C++ is often viewed as ‘non-portable’, especially in comparison to Java, python or ruby.
- Support for non-OO paradigms – C++ is often overlooked when developers want to use non-OO techniques, like functional programming
- “C++ is hard” – The general perception that C++ is more difficult to learn and use than other general-purpose languages
I don’t actually propose to delve into these (rather specious) ideas deeply – this post is already too long. I’ll merely assert that C and C++ were conceived as portable languages from the beginning, and their portability problems are fundamentally no worse that any other language.
Likewise, C++ was always intended to support multiple programming paradigms (e.g. functional programming, generic programming) and support for non-OO paradigms has grown dramatically since C++11. The association of C++ with OO is more a questions of perception and culture rather than a fact of capability.
As for the idea that C++ is difficult to learn and use: again, that’s more of a perception than a reality. I’ll concede that the ‘barriers to entry’ may be a little higher with C++, but this has, in fact, lessened with time. I think C++ is easier to use now, and has far fewer pitfalls, than when I first started using it in the early ’90s. It’s also worth pointing out that all powerful tools require learning effort to use effectively.
As a generic observation, we solve problems most effectively when we use tools that fit the needs of those problems. Software development is no different. These contraindications against using C++ aren’t so much failings in the tool as situations where other tools are a better fit.
It’s important to realise that the issues I’ve raised might not have been important or even recognised as problems at all in the past. Issues like compile / build times were simply accepted as part of development until different tools became available. I imagine the future will reveal new limitations and trade-offs with contemporary tools as new technology emerges.