These are some of my thoughts on the direction and state of C++ development in the modern era. These are my thoughts alone, and I could be wrong or unaware of some existing solutions so please bear with me.
The direction of C++
It seems the standards committee views the biggest challenge of C++ is to make it more easily accessible to beginners, but I would say the biggest issue is maintenance of large legacy code bases. C++ is not a language for someone starting out in programming; it's one for someone who has mastered other languages (such as Java or Python), and has a solid understanding of programming concepts. It is very easy to mistype a single character and generate a massive performance hit or generate an inscrutable compile error. However, there are billions of lines in existing code bases that need maintaining for decades and making this process easier would be much more productive for developers.
Typing time is a non-existent problem for programmers; I have always spent much more time contemplating algorithms, debugging, and considering the ramifications of the code than any actual typing. I think any attempt to reduce the typing - especially at the expense of readability - is a wasted effort. I'm looking at you 'auto'.
Readability of code is critical to maintain understanding of the code base; the comments can lie but the C++ never does. To this end, being as explicit as possible is a boon to you and any poor sap who has to maintain your code in the future. Quite often, the auto keyword will be used when the developer doesn't know the proper type to use - this is bad and dangerous. If the original developer doesn't know the type, how is anyone designated to support the code supposed to? Always specify the exact type if at all practical. (I'll allow certain uses of auto when the iterator type can become excessive).
Attributes are a great way to decorate functions and show the intent of the code. These do not not seem to be in widespread use yet. Check this article.
C# has some great features that would be very useful in C++. Many are not viable, but that doesn't mean they shouldn't be desired.
General goals
Specific items
These are some examples of what I think may help.-
Warning for C++XX features
-
Enable a low level warning for when a new C++ is used.
Warning C5xxx: C++26 feature used; wibble without wobble.
-
This could be extended for each new feature.
Warning C5xxx: C++11 feature used; 'auto' keyword used.
-
Enable a low level warning for when a new C++ is used.
-
Warning for non-explicit lambda captures
- The generic [&] lambda capture that makes a complete copy of everything is not readable and can be a performance landmine.
-
Emit warnings if keywords are not specified, even if redundant.
-
As an example, all derived virtual functions should have the override keyword. This would enforce the idea that the function is overriding another function.
ReSharperC++ helps find these instances, but I'm not sure if that's custom code or leveraging an existing compiler.
-
Emit a warning when braces are automatically added.
-
Old school programmers used to not add the {} for a single line statement. This is regarded as bad practice and should not be done. Let's find that dangerous code
and fix it up!
-
Improved reflection.
- RTTI just doesn't cut it. Having more generic attributes would be a good start, and C++20 seems to be going in this direction.
-
A to_string method for enum classes.
- Having a strongly typed enum type is great, but it's very tedious generating the to_string boilerplate code.
-
Add a flags class type.
- This would be similar to the enum class, but enforcing some additional restrictions on flag operations.
-
Overridable return types.
- The ability to have a function of the same name and parameters, but a different return type.
The direction of STL
Although tightly coupled with C++, I still believe STL is a separate entity and distinct from C++.
Specific items
These are some examples of what I think may help.-
STL compile errors are often very difficult to interpret.
- Work should be done to highlight the actual error rather than the entire call stack. It is off putting to see several pages of compile errors for a single issue in my code.
-
A std::switch/std::case implementation.
- This would work like a standard switch/case clause, but work with any class with an equals operator.
-
Better control of memory
-
Allocation of memory in STL is done under the hood and can generate many performance pitfalls if the developer doesn't know what he is doing. Having a way to measure this in a detailed fashion would be very helpful. Quite
often a profiler will show there are a lot of allocations in std::vector - this is not useful. Secondly, have some known rules/compile time estimations to gauge memory usage and operation count. The documentation shows
the complexity of most operations, but that can be difficult to convert into actual timings, especially for something that is implementation dependent.
-
Make mimalloc the default allocator.
-
This type of allocator (like tbbmalloc) mitigates the vast majority of memory allocation problems in multithreaded environments. As multithreading is critical for modern development, why is it not the default?
This is especially problematic with heavy use of STL as the vast majority of memory operations are hidden from the programmer.
-
Debug STL performance is 30 to 50 fold slower than release (Microsoft specific).
-
Even with _HAS_ITERATOR_DEBUGGING, debug STL in Visual Studio is unusably slow. The entire point of debug is to have additional validity checks and increased debuggabilty, so disabling this to get performance
back would seem counter-productive. I'd expect there to be a 30 to 50 percent decrease in performance, but nothing like what we see.
-
std::filesystem::path::c_str() doesn't work (Microsoft specific).
-
The path is stored in the native Windows format (widechar), but when you wish to print it will be silently converted to std::string. Calling c_str() on this results in a widechar string being printed as a non widechar
string - which doesn't work. This resulted in me dumping the very useful helper functions and going back to std::string for all use cases.
-
Widechar and MBCS - what are they? (Microsoft specific).
-
According to MSDN, widechar has followed the UTF-16 specification since Windows 2000. This means each character is 2 or 4 bytes in length; it does NOT mean a fixed width of 2 bytes. This calls the W version of API calls. The
A version of API calls refers to ANSI (not even ASCII) which is a fixed width of a single byte, but the actual characters rely on host system's code page. I'm not sure where this leaves MBCS (Multibyte character set).
What would be a step forward is to have a U variant that takes UTF-8.
MSDN Notes
The direction of C# and C++
C# is my favorite language by far, it is perfect for scripting and the vast majority of tools that need to be written. Although it is now being supported on many more platforms, it still doesn't have the reach or the bare metal performance of C++. Calling C++ API calls from within C# is an OK process (P/Invoke), but the ability to call C# functions from C++ is not practical. I think making this easy would enable C# to be the go to scripting language for the foreseeable future.
My first thoughts on this would be have an option to generate a lib and header file when compiling an assembly. The header would be relatively straightforward using the reflection API (for the features C++ supports anyway). The lib would be more complicated, possibly using the abomination of managed C++. I am really not knowledgeable enough about these internals to make a detailed plan at the moment. Here's an article on the process of using Managed C++ aka C++/CLI aka Mixed assemblies.
One of my pet peeves is the overriding of << and >> operators as print and input. As an old school programmer, bit shifting is a very useful operation and using it on strings seems very unintuitive. String interpolation would be much more readable (e.g. $"Value: {Value}"). The operation '<< 1' means double this value and it makes no sense to use it on a string.
Packaging Utilities
Packaging nuget packages is a great way to distribute utilities that don't have any native dependencies, but it just won't allow packaging of utilities with a DLL. I do hope this will be addressed in Net7.