Interesting concept, but very inefficient. I really could not get over the exponential virtual inharitance tree. I was thinking whether there is a better version, and came up with one that uses implicit conversion rules instead of virtual inheritance. Here is the source code:
As it can be seen, it eliminates the need of AllCodeFeatures as well, so it can be generically applied to any feature set. It also works for larger amount of features. For example:
I was thinking further about my idea, and realized the problem with this. It works well for basic purposes: to enforce certain functions to have a set of features. However, it doesn't work for automatic overload resolution. For example, if we have a function with an overload that is thread safe, and another that is both thread safe and exception safe, and we call it with a requirement of being only thread safe, we will get an error of ambiguous overload.
The original solution uses virtual inheritance, which provides a built-in overload resolution choosing the closest possible match. With implicit conversion, we can't use this algorithm. We have to choose the overload "manually" via enable_if. I built a forwarding of enable_if that computes this without the need to write large expressions for every overload. The disadvantage is that we need to know all the overloads in advance. Here is how it works.
This version has advantages over the original implementation as well. First, as it uses templates and decides which overload to use with the metafunction on the top, we don't even need to instantiate the feature variables. Second, the template makes these functions pass its requirement transitively. For example, we have a caller function like this:
This function provides both thread safety and exception safety to its callers, and has no overloads. However, if we only need thread safety, it will call the TSafe version of callee, providing a more efficient program without needing to use overloads over the whole call hierarchy.
Of course, if we don't want to use templates, the original approach works just as fine:
typedef TESafe callerFeatures; void callerNoTemplate(callerFeatures features) { calleeNoTemplate(features); // the original way callee<callerFeatures>(); // of course we can't use the variable in the template parameter. We have to use the type name itself. }
Here is the implementation. First, I introduced some new metafunctions because the lack of contains_if in Boost MPL.
With this, even the definition of Features becomes easier. Otherwise, it does the same as before (except that in my last post, I left out the default constructor).
template <typename S> struct Features { typedef S features_type;
Now comes the implementation for SelectFeatures. In human words, it does the following: "Enable this overload if T is convertible to ThisType and not convertible to any types in AllTypes which ThisType is also not convertible to."