Tuesday, 7 April 2015

My C++ wishlist

I regularly use C++ for my own personal projects, that's normally because my personal projects are gamedev related and the power and performance of C++ comes into its own here. My day job however is writing Python, and although C++11 has completely revolutionised the language, there are occasionally some things I miss when I'm coding at home.

Here's a quick list of things I would love to be added to C++, but I'm pretty sure won't be!

1.  Symbolic imports


Imports in Python are amazingly clean and flexible. The ability to just type:

from os.path import join

...knowing full well that it's not going to dump the entire contents of another header file into my code is wonderful. C++ sucks here, including headers slows down compilation, it means you must use header guard #defines, and forces you to do strange tricks (like pre-declaring classes) to make things work. It's painful. I hear that modules are coming in some future C++ version, but IMO they can't come soon enough!

2. Decorators


In Python, you can write a function that wraps another function, allowing you to perform pre/post operations and have them called automatically whenever that function is called. The original function seems unchanged. In Python this implementation of the decorator pattern looks like this:

@some_property_function
def the_function_that_is_wrapped():
    pass

Since C++11 introduced lambdas defining such wrapper properties could be very similar to Python. For example:

std::function<void ()> some_property_function(std::function<void ()> the_func) {
    return []() {
        //Do pre-stuff
        return the_func();
    }
}

All that's missing is some way to apply such a decorator to a function or method. Although I'm sure that's not as trivial as it sounds.

3. Context Managers


In C++, there is no try/finally. If you want to ensure that something is destroyed no matter what the solution comes in the form of RAII.

However, RAII means that you end up having code like this:

if(something) {
   something();
   { //<< Seamingly pointless scope block that needs a comment
       Lock some_lock_which_will_be_freed;
       do_locked_thing();
   }
}

Brilliant. It would be amazing to have an actual syntax for this behaviour, a 'with' statement is perfect!

with Lock()as some_lock_which_will_be_freed {
    do_locked_thing();
}

Now there are no weird extra braces without explanation!

4. Overloading of the '.' operator


This would be tricky to implement, but I've lost track of the number of times I've had to rely on overloading '->' just because overloading '.' isn't possible.

5. Read-only properties


Properties in Python allow you to add apparently public attributes to a class, which actually implicitly return the result of a function. These kind of read-only properties make using your API nicer, and allow you to abstract away the implementation of how that property's value is calculated. For example:

auto class_size = my_school.pupil_count / my_school.room_count;

If you actually exposed pupil_count and room_count as public variables, and then decide actually, you can calculate pupil_count on the fly by summing the school_year.pupil_counts you are forced to make all calling code do this instead:

auto class_size = my_school.pupil_count() / my_school.room_count;

A minor change perhaps, but if your API is used by hundreds of third parties, you're a little bit in trouble. Of course, you could have just used a method in the first place - but it makes the calling code more cumbersome, and less readable.

It would just be nicer and more flexible if C++ allowed you to do this:

property int pupil_count() { return pupil_count_; }

6. Tuple/Pair support in the range-for loop


C++11 brought us range-based for loops which allow you to iterate any container with begin()/end() methods.

for(auto& something: my_vector) {}

Unfortunately, in the case of dictionary style classes like map and unordered_map, 'something' ends up as a std::pair. Meaning you have to do this:

for(auto & something: my_map) {
    auto key = something.first;
    auto value = something.second;
}

That quickly gets tiresome, it would be nice if you could do something like this:

for(auto key, auto value: my_map) {
      // yay!
}