I know - why would you learn another C type language? If I were you I’d be thinking the same thing because there’s no getting around the fact that learning a language is a huge effort so the benefits need to outweigh the cost. Here are some of the main benefits C-UP brings.
Let’s start with the big one – parallelism. Everyone knows multi-core is the future, right? Actually, it’s been the present for about 7 years now, but we don’t seem to be any closer to figuring out how to do it in a way that mere mortals can cope with. C-UP efficiently handles parallelism with automatic dependency checking - you get to write code in the imperative style you know and love (and can debug) and get all the parallelism your memory bandwidth can handle without ever worrying about threads, locks, races, or any kind of non-determinism.
It’s hard to believe that mainstream CPUs have had SIMD for over 14 years but you can still only utilise it by delving into processor specific intrinsics, writing back to front code like add(sub(mul(a, b), c), d) instead a * b – c + d. You’re smart though and already have classes that wrap this stuff for you but can your classes do arbitrary swizzling and write masking of vector components? When you compile without inlining does your SIMD add compile to a single instruction or is it a call to a 20 instruction function? Maybe that’s why your game runs at 5fps in debug builds.
If you could combine the power of all those processor cores with all the goodness of SIMD in a machine independent way, surely that would be worth something to you? C-UP doesn’t give vague promises of auto parallelisation using SIMD or make it really easy to allocate new task threads from a pool without handling the actual problem of dependencies between those tasks – it provides simple practical tools that work today.
What if at the same time as getting world beating performance you could be guaranteed not to have any memory corruption, double free errors or dangling pointers to freed memory. “He’s going to say garbage collection”, and you’re right that GC is the default in C-UP. But if you are worried about using GC would it interest you to know that you can get all those benefits while still using manual memory management as and when you choose?
Even better, what if that memory management came with other benefits like no allocation block headers (your allocation uses exactly as much memory as you request), built-in support for multiple memory heaps, alignment control without implementation specific pragma’s, platform independent control over virtual memory reserve and commit levels?
What else … strings; awful in C++ but they work pretty well in languages like C# - it’s nice to only have one string type but then they’re seriously inefficient(*) because every time you do anything with them loads of little heap allocations occur. And that just slows down the GC even more. And for a game programmer on a console with 512MB all those UTF-16 strings with zeros in the upper 8 bits represent a massive waste of memory. In C-UP a single string type represents both 8 and 16 bit character strings and they can be seamlessly mixed and matched. And you can also perform most string operations on the stack to avoid those pesky allocations, and you can make sub-strings in-place using array slicing. You can even get under the hood of strings with a bit of explicit casting so you can operate on them in place if needs be.
Array slicing is great for strings but in C-UP all arrays can be sliced. If you haven’t heard of array slicing, it allows you to make a new array which references a sub-section of an existing array by aliasing over the same memory. Let’s say you’re parsing some text in memory and need to store some of the words found in it – slicing lets you store those words as separate arrays aliased over the same memory (no allocations or copying). Other languages like D let you do this but in C-UP when you throw away the original reference to the entire text the garbage collector can still collect all the parts of that text that are no longer referenced while keeping the sub-strings you stored safe and sound. Sounds ridiculously efficient, doesn’t it?
Obviously these arrays carry their length around with them and are bounds checked and of course you can disable those bounds checks in a release build or use the foreach statement to avoid them in the first place. Oh, and 2d arrays are supported to with full 2d slicing, which handles all the stride vs width and indexing pain for you to make handling images rather convenient.
Languages like C# and D are great and all but you have to decide up front if a particular type is a value type or a reference type. That’s usually okay but some things aren’t so easily categorised and it prevents you doing a lot of efficient stuff like making values on the stack if you know they’re only needed temporarily, or making a pointer to a value type, or embedding a type inside another type if that works better for you in a particular case. I guess the problem with all of those things is that they’re really unsafe because how could you know that you’re not storing away a pointer to something on the stack that will be destroyed any second? And how can you store a reference to something in the middle of an object in the presence of precise garbage collection? Well in C-UP you can do all of this and more because it differentiates a reference to stack data from a reference to heap data and because the memory manager has no block headers pointers can point anywhere including the inside of another object and the garbage collector can still collect the other parts of the same object if they’re no longer referenced.
I’m going on a bit now, but virtual functions are irritating; the vtable embedded in the object messes up the size and alignment of structures so you can’t use virtual functions in types that require careful memory layout (i.e. almost everything in a modern game.) The vtable is typically stored as a pointer so it’s completely incompatible with running on certain heterogeneous cores (Cell SPUs.) The silly requirement to have a virtual destructor in the base class means you have to make decisions about how a class might be used in the future. As you may have inferred C-UP solves all of these issues and the way it does that is by decoupling virtualisation from object instances, instead tying it to functions. This means that a function can virtual dispatch on multiple parameters including or excluding the ‘this’ pointer and that virtual functions can cross class hierarchy boundaries so no need to have a base of all types ever again. By the way rtti is also very fast and efficient so I think it’s unlikely you’ll have 8 different home grown versions of it in your project (one per middleware provider) each with their own vagaries. Speaking of which…
Reflection is built into the language. You can browse the entire symbol table programmatically; get and set variable values; create objects and arrays; invoke functions; get enum values by name and vice-versa.
And there are no includes and no linking, so it compiles really fast.
And it comes with a debugger, itself written in C-UP using all of the above features.