C++ Templates and SFINAE
Tags: cpp, templates, sfinae, metaprogramming
Published: 2020-02-05 17:42:28

Recently, I came across a problem while looking for `good first issues` on GitHub. I would like to share the problem and its solution with you in this post. Just for clarity, we would work with a simpler version of the problem.

Suppose there is a template class `Container` that takes a `class` as Backend conforming to a particular interface. This `Container` acts as a wrapper on the Backend providing it with extra functionalities.

```cpp
template<typename Backend>
class Container {
    private:
    Backend b;
    int data;
    int data1, data2;

    public:
    Container(int data, int data1, int data2) : data(data), data1(data1), data2(data2) {}

    int get() {
        return b.getValue(data);
    }
};
```

Now all the Backends need to have the following particular function,

```cpp
class Backend1 {
    public:
    int getValue(int data);
};
```

This function is called by a `get()` function inside our container. The problem is that we need to change this interface to add up new functionalities. So, we now need to call `getValue()` with 2 parameters, instead of 1. So, our interface should now be:

```cpp
int getValue(int data1, int data2);
```

We cannot make this change suddenly, because many users and backends depend upon the old functionality. Therefore, for the backends that support new interface we will call `getValue` with two parameters else we would call it with one.

We need to somehow statically(that is at compile-time) detect if any backend supports the new interface we use it, else we should stick with older interface.

Now, this is where the magic of C++ templates come to picture.

```cpp
class SupportedBackend {
    public:
    int getValue(int data1, int data2) {
        return data1 + data2;
    }
};

class UnsupportedBackend {
    public:
    int getValue(int data) {
        return data + 1;
    }
};
```

So, among above two backends, `SupportedBackend` supports new interface whereas `UnsupportedBackend` does not. (I could not come up with any other descriptive name)

Here, is the code that can do the work for us,

```cpp
#include <type_traits>

template <class T>
class check_new_interface {
  template <class R>
  static auto check(int) -> decltype(std::declval<R>().getValue(0, 0));

  template <class>
  static double check(double);

 public:
  static constexpr bool value = sizeof(decltype(check<T>(0))) == sizeof(int);
};
```

`check_new_interface<T>::value` would be _true_ if `T` supports new interface else it will be _false_.

Let us now understand the above code.

So, `check_new_interface` class has two template member functions with same name (but different signatures). Now the value of `value` depends upon the following expression: `sizeof(decltype(check<T>(0))) == sizeof(int)`. This is where we need to understand a lot of things.

First, `sizeof` and `decltype` do not evaluate the expression they are called with. So, the following program would only print `4` (that is sizeof(int)) instead of `hello4`. (Did you notice that we did not define the `check` functions although we called it the above expression)

```cpp
#include <cstdio>

int main() { printf("%lu", sizeof(std::printf("hello"))); }
```

Second, when C++ sees `check<T>(0)` expression, it trys to find a proper function declaration that matches this call. There are two options, so template function overload resolution comes into picture. As, `0` is an integer literal, so C++ would first try to associate this call with function that takes integer as its parameter. If it cannot do so it will then check other options. For example,

```cpp
#include <iostream>

long echo_with_type(double a) {
  std::cout << "double: " << a << std::endl;
  return a;
}

void first_echo(int a) { echo_with_type(a); }

int echo_with_type(int a) {
  std::cout << "int: " << a << std::endl;
  return a;
}

void second_echo(int a) { echo_with_type(a); }

int main() {
  first_echo(2);
  second_echo(2);
  return 0;
}
```

If, you run the above code, you would find that first output would be `double: 2` and second would be `int: 2`. This is because when `first_echo` function is called, it does not find any `echo_with_type` function with integer parameter so it calls the one which takes double as parameter, but second time it finds one with integer and therefore call its.

Similarly, in our situation for `check<T>(0)` compiler would try to substitute `T` into the first template before the second one (for similar reasons as above program). Now, this is where the real magic happens. When any type is substituted in place of R, then only if that type has a member function with signature `getValue(int, int)` the substitution would succeed. Else it will fail. So, in our case when we will substitute `SupportedBackend` then this substitution would be succeed but in case of `UnsupportedBackend` it will fail. (Sorry, I am skipping few details here, like what is `declval` and why we use it but we can get into that sometimes later.)

Now, if C++ did not support `SFINAE`, then the compiler would have thrown an error here. But luckily it does so we can continue.

> SFINAE - Substitution Failure is not an Error

Basically `SFINAE` means that if a template substitution fails then compiler does not throw error but continues to find a valid template substitution until it succeeds. (If it does not find any then ofcourse it will throw error.)

So, in our case as well compiler continues to find a valid substitution for `check<T>(0) [when T = UnsupportedBackend]`. Because first template substitution failed, it will move to the second template and succeed. But when it succeeds for the second template then return type of `check<T>` would be `double` instead of `int`. (Where as in case of `T = SupportedBackend` it would be `int`)

So, it is clear that the `check_new_interface::value` would be _true_ in case of `SupportedBackend` and _false_ in case of `UnsupportedBackend`.

Now, we may be tempted to implement `get()` as follows:

```cpp
int get() {
    if(check_new_interface<Backend>::value) {
        return b.getValue(data1, data2);
    } else {
        return b.getValue(data);
    }
}
```

But, compiler would give errors if we try to do this. The reason is although compiler knows for any given `Backend` whether if part will exceute or else part but it needs to generate code for both of them. So, obviously it will fail.

The solution again would be to use function overloading here. `std::true_type` and `std::false_type` are specialized types of class template `std::integral_constant` defined as following:

```cpp
typedef std::integral_constant<bool, true> true_type;

typedef std::integral_constant<bool, false> false_type;
```

```cpp
private:

inline int get(std::false_type) { return b.getValue(data); }

inline int get(std::true_type) { return b.getValue(data1, data2); }

public:

int get() {
return get(
    std::integral_constant<bool, check_new_interface<Backend>::value>());
}
```

Now, this would work flawlessly because, when compiler generates code for `get()` function then, it only needs to compile one of `get(std::false_type)` or get(std::true_type) depending upon the value of `check_new_interface<Backend>::value`.

So, finally we have our solution ready. You can find the complete code [here](https://gist.github.com/ram-nad/1b9eca2a078b35998833c8d7b07e04b3).

I hope that you got to learn something new in this post. Thanks for reading.