C++ has come a long way since the late 1990’s. The transition hasn’t been smooth, and young programming languages have made huge strides in the minds of new programmers.
The biggest issue facing C++ is new programmers aren’t seeking it out and the schools aren’t teaching it by default. C++ is being avoided, and honestly it probably should be. When learning how to code, understand segfaults and fighting dragons is unacceptable. C++ has become clunky and cumbersome to work with. Complexity has increased with recent changes. The only usable STL is boost or QT and the learning curve for either is large. Relevant up to date information is difficult to find, and the language still allows the user to easily do unsafe things with memory. The result is annoying crashes, constantly being forced to lookup documentation, confusion, frustration, and eventually seeking out other language solutions.
The bottom line is this, if C++ doesn’t get with the times in the next 5 years, young programmers should learn Go/rust and skip C++. This statement kills me because I love C++
There are a few core issues causing this:
Why should we care about C++ in 2017?
C++ in the late 90’s was the best language. At the time, it provided wonderful control with unmatched syntax. No on considered memory safe languages to be a requirement, life was good. In the last 20 years computer scientist have learned a lot about libraries that lend themselves to writing faster and safer code. New players in the language wars have showed a different take on memory management. Ruby/python taught us how flexible languages can be. Javascript taught us how forgiving code can be. Rust taught us memory allocation can be safe and monitored by the compiler. The list of functional languages have taught us new ways to think about state. Today C++ isn’t the best language, there are many others which can do all the things C++ does, and in many ways, they do it better. So why do I care? Why should we care? We should care because C++ provides the performance tuning nobs that most languages hide. We should care because C++ is for elite programmers that are pushing the bounds of current hardware. We should care because C++ isn’t full of annoying annotations or vendor/platform lock-in. We should care because C++ is powerful.
C++ is worth fighting for, below are the glaring issues I see.
Breaking old code compatibility IS okay
“Changes that don’t break compatibility with existing code” is well intentioned and incorrect. Code written using C++17 or older standards is welcome to continue doing so. New code written using new standards can leverage updated behavior from existing key words. Which is worse, upset the coder with expectations that 20 year old code acts exactly the same with the newest compiler flags? Or is it worse to have droves of 20 year old programmers choose a more modern language for their new project?
Header files
Compilers have come a long way in the last 20 years. Requiring the user to write header files by hand is antiquated. There are almost no cases that should require custom written headers. The compiler should auto generate header files for each cpp file on the fly. Perhaps a new pre processor keyword needs to be created? #import “xxx.cpp” which causes the header file to be generated? With this new pre-processor, please also provide a way to explicitly state which items to include from the remote file. #import “xxxx.cpp” with foo, bar, Klass. This would include function foo, bar, and class Klass.
Overly complex or long winded syntax
No one likes auto. Var/let are well established, and don’t require the extra typing, pick one of those two and move on. I prefer var over let because I can type var with just my left hand, it feels better. C++’s lambda’s are great, but they are way too much typing. By default (x) → { code; } should translate to = → var { a thing }. Function pointers need a nicer syntax to pass themselves around. I’m not sure what is, but std:func<> isn’t right. Template syntax should be reduced to <T> added to the function or class. Users just want to write: class Dog<T> {}; or Dog<T> → int () one time and then use T inside that class/function.
Revamp of memory allocation syntax
Garbage collection is a hack, its horrible, its slow, we don’t want it. I love stack allocation. I love scoped heap allocation. I love scoped heap allocation that can transfer its ownership to another scope. I love shared pointers. In C++x11 and above, only stack allocation is a first class citizen, and all methods listed contain dragons. The “new” keyword needs to replace unique_ptr. When new is called, a scoped based unique_ptr should be created. No more make_unique<T>(xx), just var dog = new Dog(); If a unique pointer is passed to a function, the life-cycle of that pointer cannot extend the function class.
If a unique pointer is passed to a function and the life-cycle IS extended, then that pointer’s scope is extended to the scope of that new life-cycle. Perhaps there is some syntax required in the function’s parameters to make this rust like borrow behavior clearly stated? Pointer syntax is great, but its not safe. I want to use pointers, but the compiler needs to monitor my usage and ensure the pointer is treated safely. shared_ptr needs its own keyword. Perhaps “shared” which will create shared pointers. Var dog = shared Dog(); When shared pointers are used in functions, their reference count should be pinned to the lifecycle of the function they are used to avoid possible premature cleanup. Herb Sutter has a wonderful video covering a work around for this “pinning” behavior. Delete keyword should be deprecated, it would have the same effective behavior as assigning nullptr; to the pointer.
Stack safe destructors
Memory deconstruction shouldn’t happen through stack allocated function calls. As many of you know, using unique_ptr in a nested tree structure class can easily cause “random” crashing when your destructors chain together and overflow the stack buffer. Herb Sutter has a wonderful video on this behavior.
The compiler needs to come up with a different system for handling destructors that is heap based allocation and unable to crash in any reasonable programming situation. Perhaps tail recursion? Whatever the solution is, slay the dragons
co-routines (yield) and a proper foreach
Begin()/End() are very clunky to use and are not standard among other programming languages these days. The industry has decided co-routines are the preferable method to handle foreach iterators. Simply put, a “yield” like keyword, and a foreach ( var x : co-routine ) to handle it would go a long way. Python nailed this behavior. The yield/for interaction are exactly the correct model C++ needs.
Named and variable length parameters
In some cases, named parameters make reading code much more verbose. There are also times when parameter order doesn’t matter as much as name grouping: action(slope=0.5,offset=2,foo=9) is much easier to read and understand over: action( 0.5, 2, 9 ); In addition to named parameters, capturing variable length parameter lists and a hash of named parameters allows the coder a lot of flexibility. Python nails this behavior. Anyone that has written frameworks, especially web based frameworks knows this type of behavior is invaluable. When naming parameters, OR DOESN’T MATTER! The compiler is welcome to reorder my parameters for me. Named class member initialization When allocating classes, there isn’t a great way to assign member values, without extra typing by the user. Var dog = new Dog () { age: 17, color: “yellow” }; Making the class initialization compatibility with Json format, and allowing the implementation to be extendable through operatorXX is ideal.
Order of named variables doesn’t matter. The compiler should reorder as needed.
Dictionaries as first class citizens
Json rules the world. C++ should support the json format of data, which is stored into a dictionary.
Safe arrays as first class citizens
Allow the user two types of array allocation, stack and heap allocation. Stack allocation should work with variable length one time initialization (does not require a compile time constant). Many compilers already do this (g++), all of them should. Arrays is C++ should be safe by providing bounds checking and all the typical helper functions. When a user creates an array, it should be a STL class, not pointer math. Stack and heap arrays should have feature parity with each other. Arrays should provide all the functionality a vector does, except dynamically changing sizes. Strings, localization, and formats as first class citizens Var x = “something great”; should create a string class. Char arrays are accent, the default behavior shouldn’t be char arrays, their interaction should be the expection. I should be able to set string values in class member: class Dog { var Name = “Woofy”; }; Switch statements also need to support strings natively. The user should find it easy to deal with string literals. Built in string localization should be a standard feature of the compiler. When I write var name = “dog”, the compiler should auto gen a keyword lookup file. I can then go through and specify all the different languages I want to support, and when selected by the STL, those keywords instances of “dog” will be replaced with the localized word. Built in string formats. Cout is great, printf is greater, what about general string construction???? Allowing the user to write var str = “%d Bananas” << (5) is really handy. Allowing the user to write var str = “Run “ << 5 << “ times”; is also very handy.
Enums revamp
When dealing with enums, the type name should be included to give context to the reader. Reading code that says var x = COUNT is much more ambiguous than var x = Apples.COUNT;
Enums should easily be convertible into a string and from an int back into the enum. Nothing is worse than writing a switch statement of enumeration values to output nice string names. Var e = EnumVal.VALUE; (string)e; // Return “VALUE” (int)e; //Return 6?
Keep C style overload syntax with updated modern behavior
Programmers enjoy C-Style syntax casts var x = (int)a; Don’t get rid of them. Allow this syntax to play nice and have compiler checks. If that is impossible, then have the new style double { x } be more aggressive and work the way a coder would want. C++ cast syntax of dynamic_cast<XXX>(x) is way too much typing.
Compile time reflections and meta classes. sorry for saying run time, I meant compile time. Run time reflections make no sense with a language like cpp, sorry
C++ needs to provide detailed reflections. The user should be able to iterate through parameters of classes, get names of functions etc. The user should also be able to get values of parameters in classes, or even set values through reflects. Meta classes are needed, not sure what else to say there. Class initialization through a string name also needs to be implemented. Initialize.Create<BaseClass>(“MyClass”) where MyClass extends BaseClass. Hopefully it would go without saying, the syntax to accomplish this needs to be brief. The compiler should create this info at compile time.
Friendly tuples
I know C++ has tuples now, but their implementation is cumbersome. Tuples should be created by syntax akin to: var tup = (1,2,4,5) Accessing those tuples: var a = tup[0]; Tuples should have feature parity with arrays, except for being immutable.
Function decorators
Provide a way to attach symbols to the reflection system which can be accessed during run-time.
Allow the user a way to pass the calling function and its parameters into a user defined function, where any action can be taken. Wrapper function decorators are extremely helpful when writing frameworks. Anyone that has written framework libraries using python know the behavior I’m talking about.
Primitive helpers
Pay to play programming is awesome. Provide helper primitive function calls like 1.toString(); They read nice and provide a wonderful feel to the language. When implemented correctly, such a call could compile down to itoa(1);
Common names for similar STLv2 actions
Provide common names for all similar actions inside the STL. If dictionaries, arrays, and vectors all provide a “search” functionality, ensure it is called search or find in all different implements.
Functional STL, none or extremely rare use of try catch
The new STL should hopefully never throw exceptions. Return values can contain error information, requiring a user to wrap try/catch around the basic STL logic makes for ugly hard to trace code. Deep STLv2 that plays nice together
Array stack allocated
Array heap allocated
Vector
Friendly tuples
Dictionaries (Json)
String
Stack
Queue
File
Memcache
Sort
Regex
Random
MD5/Sha256
Tcp
Http/Https
Mutexs
Guids
There are many many more features the STLv2 needs to provide but here is an example. The user should feel that whatever data interaction is required, there is an easy to use library that’ll provide that functionality and do so with common names so they can guess how to use it without constantly looking up documentation.
The libraries need to smoothly work together, it should be trivial to convert data from one data structure into another.
Community owned package manager
Python has pip, ruby has gems, javascript has node, rust has cargo. C++ needs a well managed, community ran system for exchanging code.
Community owned ORM and web framework
The young kids play in the web. After all these changes are made, the C++ community needs a well designed ORM, code first migrations, and Restful web framework. The framework needs to lend itself well to working with Angular/React web calls. The web framework also needs to provide a fantastic admin interface into the database that is extremely extendable. Think rails or django.
Slay the dragons!
The user should find it difficult to crash a C++ program. The language shouldn’t be full of pitfalls, it should be a safe and joyful to use.
What can we do?
As a community we need to do one of two things: 1. Speak out and be heard. Provide solutions, contribute and get the C++ we need. 2. Speak out and be ignored, learn rust/go and continue our work.