It has been quite a while since I last posted on JVL blog! As my daily activities at work involve coding, I want my free time to have as little to do with programming as possible! (which I hope is understandable).
However, this is inevitable: I do occasionally come across cool and interesting findings that I might find worthy of blogging and sharing (but sometimes just don't find the energy nor the time...). Most of these findings should now be a bit more in-depth and geared more towards C++ and Python, as those are my main and secondary "weapons". =)
Recently at work whilst refactoring our code base, I have introduced a cool technique (which I don't know if it has been publicly shared) to apply a class member selection. My initial purpose was to reduce the amount of repeated code, and make the compiler generate them for us. Basically, I wanted to template a few things, but to achieve this fully, it was necessary to have a kind of member selection technique. I accomplished this in 2 variants: a template specialization (pre-C++17) and another one using a C++17 feature, namely constexpr if() else statements. But let's first talk about the straightforward implementation.
The "Naive" Straightforward Implementation
I will present the problem differently, in light of keeping information internal, obviously. Say we have some produce classes as shown in helper_classes.cpp and a Cart class which, has a addToCart() function that adds different types of produces into their corresponding baskets, as shown in cart_naive.cpp. Please ignore printSizes() for now.
Lines 12 -15 are basically doing the same thing, just for different types (kind of like copy-paste no?) How could this be improved? Read on...
Template Specializations (Pre-C++17 Solution)
It seems that addToCart() could be templated! So, let's first try that. But after templating it, we would need a function that could return the correct basket from the Cart based only on the typename T. The resulting templated function would look like in Line 12-13, cart_template_specialization.cpp.
The getBasket<>() function will do exactly that: return the corresponding basket based on input typename T. To achieve this, we have to template the function and specialize it for each type! Lines 21-22 show the function signature, while Lines 26-29 show the different specializations.
Seems a bit better now no? Hope you agreed =), but that's not all. Now let's see a yet nicer solution.
C++17 Features Solution
The next improvement lies in the templated function getBasket<>(). With a combination of the auto return type (C++14 feature) and constexpr if() else statements (C++17 feature), the getBasket<>() could be further refactored to look nicer, in a self-contained function body.Seems a bit better now no? Hope you agreed =), but that's not all. Now let's see a yet nicer solution.
C++17 Features Solution
Let's look at it in Lines 21-28, cart_cpp17_feature.cpp. The function takes in the typename T, goes through each if statement comparing against each type until it matches, and generates the code within that if block. The std::is_same_v<> is a type trait function that checks and returns true if the input types are the same. Because the return type varies depending on typename T, we have to return an auto type.
Notice that all the described steps are done at compile time, which results in compiler generated code. Do all the presented variants actually work? Yes of course, let's see how we could test them.
Testing
In the Cart class, the printSizes() does exactly what it promises, print sizes. Then the main function would look like this:
As you may have already guessed, all the variants should produce the same result:
Size of AppleBasket << 2
Size of PearBasket << 0
Size of TomatoBasket << 2
Size of PotatoBasket << 1
Size of AppleBasket << 2
Size of PearBasket << 0
Size of TomatoBasket << 2
Size of PotatoBasket << 1
Conclusion
Here is the link to my Github repository if you want to try out the code: https://github.com/jamseams/jvl_memberselection