Self Unemployed

Mar 19

Conditional Inheritance in Python

I was messing around earlier today, trying to come up with a decent default value for a class to be imported in a build system I’ve been writing for a bit now. I was using a silly if-elif-else chain, and a function to return the proper class. I then wondered if it would be possible to have a default class using conditional inheritance. In python the ternary is expressed like so:

x = value if condition else not_value

This expression can be used anywhere, even when defining classes. In the case of the build system, I was trying to set the default C/C++ compiler. This resulted in the following:

import sys

windows = sys.platform == 'win32'
macsox = sys.platform == 'darwin'
linux = 'linux' in sys.platform

class GCC(object): pass
class Clang(GCC): pass
class MSVC(object): pass

class CXX(MSVC if windows else (GCC if linux else Clang)): pass

Surprisingly (or maybe not surpirsingly), it worked without issue. Whether this is something that is actually recommended is up for debate, but now I can set a ‘default’ compiler class within the toolchain (while allowing the user to change it if they wish).

Figured this was worth sharing. However, a word of warning:

Don’t actually use this in production code

Feb 16

Substitution Failure Is Not An Error (It Is Also Not Human) Part 2

Last time, I wrote about SFINAE in relation to functions. This time, I’ll be covering their use in structs, and all the neat little things this lets you do.

I had promised to show off a neat trick involving platform specific code within templates with extremely minimal use of the C preprocessor (simple defines are used).

Unfortunately, I found out that accrding to the C++11 standard that what I had done actually violates the standard, and in gcc 4.7 this actual trick will not work. The reason it currently does is due to all the compilers out there only having implemented some of the new rules, while following some from C++03.

I’m still going to show off the trick, however, don’t expect it to work. The concepts behind it still do, but for platform specific calls, everything breaks.

But enough about that.

Like functions, structs and classes are also templatable. However, we can go a step further and partially specialize a struct, like so

template <typename T, bool condition>
struct special { };

template <typename T>
struct special<T, true> { };

In the above example, we are telling the compiler that whenever the second template paramter (condition) results in a true statement, we want to use the second definition. Better yet, template parameters work more or less like function parameters, so we can even give the condition a default value

template <typename T, bool condition=true>
struct special { };

Even better, since we would most likely not use the condition variable name, we don’t even need to give it a name (just a type, like C++ functions allow)

template <typename T, bool=true>
struct special { };

With this in mind, let’s say we wanted to use this knowledge to get some compiler specific intrinsics used. And maybe we want to do it without #if statements all over the place. Luckily, we can simply use a #define to know what compiler we are using.

#define COMPILER_IS_MSVC 0
#if defined(_MSC_VER)
  #define COMPILER_IS_MSVC 1
#endif

For this example, we’re going to implement a basic integer only byteswap functions to handle 16, 32, and 64 bit integers. The three compilers we see all have builtin intrinsics for this functionality, but only gcc and clang use the same naming conventions (and with good reason, considering this is a compiler specific feature). With the previous post of SFINAE under our belts, we can take our knowledge of functions and wrap them. But first, we need to create a simple class (the default one, where COMPILER_IS_MSVC is going to be true).

template <typename T, bool=COMPILER_IS_MSVC> class swap {
  public:
    static inline uint64_t call(uint64_t v) { return _byteswap_uint64(v); }
};

template <typename T> class swap<T, false> {
  public:
    static inline uint64_t call(uint64_t v) { return __builtin_bswap64(v);}
};

Now if we tried to compile the above on any platform it would fail. This is because the types of the function call are known when the function is instantiated. The types aren’t dependent, so no substitution is actually going to take place. So we need to get a bit clever, and create a wrapper object, where the returned input type is the same as the output type.

template <typename T, typename U> class combo {
  typedef typename std::conditional<
    std::is_same<T, U>::value,
    T,
    void
  >::type return_type;

  typedef typename std::conditional<
    std::is_same<T, return_type>::value,
    T,
    U*
  >::type param_type;
};

The combo type can be used like so

typedef typename combo<T, uint64_t>::return_type qword;
typedef typename combo<T, uint64_t>::return_type qparam;

static inline qword call(qparam val) { return __builtin_bswap64(val); }

Now, in the event that type T is not a uint64_t, the qword type becomes a void, and qparam becomes T*. This means that according to the rules of SFINAE, this function cannot satisfy the type substitution of T. Assuming that we are using a compiler that performs two-phase identifier lookup, the contents of the function are never evaluated, and the compiler continues its work. We can do this with ALL of the basically sized types.

template <typename T, typename U> struct combo {
  typedef typename std::conditional<
    std::is_same<T, U>::value,
    T,
    void
  >::type return_type;

  typedef typename std::conditional<
    std::is_same<T, return_type>::value,
    T,
    U*
  >::type param_type;
};


/* msvc */
template <typename T, bool=COMPILER_IS_MSVC> class swap {
  typedef typename combo<T, uint64_t>::return_type qword;
  typedef typename combo<T, uint32_t>::return_type dword;
  typedef typename combo<T, uint16_t>::return_type word;

  typedef typename combo<T, uint64_t>::param_type qparam;
  typedef typename combo<T, uint32_t>::param_type dparam;
  typedef typename combo<T, uint16_t>::param_type wparam;

public:
  static inline qword call(qparam val) { return _byteswap_uint64(val); }
  static inline dword call(dparam val) { return _byteswap_ulong(val); }
  static inline word call(wparam val) { return _byteswap_ushort(val); }

};

/* gcc/clang */
template <typename T> class swap<T, false> {
  typedef typename combo<T, uint64_t>::return_type qword;
  typedef typename combo<T, uint32_t>::return_type dword;
  typedef typename combo<T, uint16_t>::return_type word;

  typedef typename combo<T, uint64_t>::param_type qparam;
  typedef typename combo<T, uint32_t>::param_type dparam;
  typedef typename combo<T, uint16_t>::param_type wparam;

public:
  static inline qword call(qparam val) { return __builtin_bswap64(val); }
  static inline dword call(dparam val) { return __builtin_bswap32(val); }
  static inline word call(wparam val) {
    return ((val & 0xFF00) >> 8) | ((val & 0x00FF) << 8);
  }
};

We’re not done of course. We need to come up with a way to wrap this struct, so let’s create a mini wrapper function which will also do the error handling for us, so that we don’t have to worry about incorrect types getting through and an actual error resulting from no proper substitution from being available.

template <typename T> inline T swap_bytes(T val) {
  static_assert(std::is_integral<T>::value,
                "Only integral types are allowed");
  return swap<T>::call(val);
}

And that’s that. We’ve now created a non C preprocessor implementation of compiler specific code. Granted, it’s verbose as heck because we didn’t resort to preprocessor macros to cut down on the return_type and param_type typedefs, but it’s a pretty darn good example of “what could have been” if C++ compilers weren’t getting so good at what they do.

Dec 29

Substitution Failure Is Not An Error (It is also not a Waffle House) Part 1

With the finalization of the new C++ standard, I’ve begun tinkering with some features of the language that I haven’t really needed until now, or chose a hackier route. At the suggestion of a friend, I’ve decided to ‘esplain’ what I’ve done recently that, aside from basic #ifndef and #include statement, allows the circumvention for platform or compiler specific code without use of the C preprocessor.

But, before I actually show that off, I think some explanation of some concepts that are used to achieve this might be necessary, especially because some folks struggle with it. As such, I’m writing this as a series, with the explanation of SFINAE (the easier to type acronym of the title) in conjunction with function overloading and deduction to be the main focus of this post. To help explain the concept, we’ll be using a non-car analogy. Not because car analogies are bad (they are, in fact, the worst kind of analogies), but because the example for SFINAE involving food is a little easier to stomach.

So, first, we need to comprehend a few basic, simple concepts. Specifically, that you can overload functions, and you can overload template functions.

template <typename T>
T conjunction(T) { return T(); }

Above is a simple function with a signature of (T) -> T (this is an optional new function declaration syntax available, which is used elsewhere such as in Haskell, OCaml, F#, and even Python sort of). That is, whatever the type of the parameter it takes, will be the type of the item returned. This makes it easier for the compiler to deduce what is returned by the function.

template <typename T>
T conjunction(T) { return T(); }

auto val = conjunction(5);

Effectively, the compiler says “conjunction deduction, what’s the function?” To which it replies “Hooking up integrals and scalars and numbers”.

Now, here’s where we can get a little neat. Because functions support overloading, we can specialize the conjunction function to make the deduction require fewer assumptions.

template <typename T>
T conjunction(T) { return T(); }

template <> void conjunction(void) { }

auto x = conjunction(5);

Now, conjunction can be used in a situation where type T is actually void. We can do this with literally anything, as long as we are explicit about what we overload with the function. However, what happens when we have the following?

void conjunction(int16_t);
void conjunction(uint32_t);

conjunction(0xFFFF);

Which conjunction overload will the compiler select? In this case it selects the second, and always the second, because 0xFFFF is an int literal. However, the int16_t variant of conjunction was declared first, so why did the compiler skip it, and why did it not error out because 0xFFFF is anint literal and is not unsigned? (thereby making it an int32_t) Because of SFINAE. The compiler looked at the varying factors we gave it, and discarded the functions whose signatures did not match, or were not able to be converted.

Let’s get analogical all up in this. Suppose you wished to dine at a restaurant in your current residence. Before selecting what you think is the best place to eat, varying factors are taken into account. These can range from information you know beforehand (e.g., never setting foot inside an establishment contained within a city-block sized market/emporium) to information that might be dependent on information you may not know until you need to know it (Is this the place where that guy took a monkey hostage and sang the Russian National Anthem backwards? Or was that Denny’s?). Sometimes, a restaurant can be right out (Waffle House), or not appropriate for the occasion (an Anniversary Dinner at Waffle House), or maybe result in undefined behavior (Waffle House).

Effectively, SFINAE can be viewed as a reason to avoid an error (or in the case of the restaurant analogy, a Waffle House) based on constraints that are known before the actual visitation to the restaurant, while still having the option to attempt to visit another if it turns out that possible constraints are ineligible (Just because the W is out in the Waffle House sign doesn’t mean it stopped being a Waffle House).

With SFINAE, we can sometimes get this bizarre effect where the compiler will choose the best function available (and if we’re not careful, the wrong one), simply so that it may not error. Applied to the restaurant analogy, we can sometimes get this bizarre effect where a person will choose the best restaurant available (and if we’re not careful, the wrong one), simply so that they don’t have to step foot into a Waffle House.

Next time, we’ll talk about Waffle House partial template specialization and how it relates to SFINAE.