std::variant

1 minute read

Overview

Aleksei Ivanov have a look at the example of the interview question that he got asked during the interview loop with the high-frequency trading company.

#include <cstddef>
#include <cstdio>
#include <new>
#include <string>
#include <type_traits>
#include <utility>

template <typename... Types> struct FirstType;

template <typename Head, typename... Tail> struct FirstType<Head, Tail...> {
  using Type = Head;
};

template <typename T, typename... Types> struct TypeIndex;

template <typename T, typename... Types> struct TypeIndex<T, T, Types...> {
  static constexpr std::size_t Value = 0;
};

template <typename T, typename Head, typename... Tail>
struct TypeIndex<T, Head, Tail...> {
  static constexpr std::size_t Value = 1 + TypeIndex<T, Tail...>::Value;
};

template <typename Type, typename... Types> union VariantStorage {
  Type Value;
  VariantStorage<Types...> Rest;

  template <typename Other>
  VariantStorage(std::integral_constant<std::size_t, 0>, Other &&Input)
      : Value(std::forward<Other>(Input)) {}

  template <std::size_t Index, typename Other>
  VariantStorage(std::integral_constant<std::size_t, Index>, Other &&Input)
      : Rest(std::integral_constant<std::size_t, Index - 1>{},
             std::forward<Other>(Input)) {}

  template <std::size_t Index, typename Other> void construct(Other &&Input) {
    if constexpr (Index == 0) {
      new (&Value) Type(std::forward<Other>(Input));
    } else {
      new (&Rest) VariantStorage<Types...>(
          std::integral_constant<std::size_t, Index - 1>{},
          std::forward<Other>(Input));
    }
  }

  template <std::size_t Index> auto &get() {
    if constexpr (Index == 0) {
      return Value;
    } else {
      return Rest.template get<Index - 1>();
    }
  }

  void destroy(std::size_t ActiveIndex) {
    if (ActiveIndex == 0) {
      Value.~Type();
      return;
    }

    Rest.destroy(ActiveIndex - 1);
  }

  ~VariantStorage() {}
};

template <typename Type> union VariantStorage<Type> {
  Type Value;

  template <typename Other>
  VariantStorage(std::integral_constant<std::size_t, 0>, Other &&Input)
      : Value(std::forward<Other>(Input)) {}

  template <std::size_t Index, typename Other> void construct(Other &&Input) {
    static_assert(Index == 0, "Index is out of range");
    new (&Value) Type(std::forward<Other>(Input));
  }

  template <std::size_t Index> auto &get() {
    static_assert(Index == 0, "Index is out of range");
    return Value;
  }

  void destroy(std::size_t ActiveIndex) {
    (void)ActiveIndex;
    Value.~Type();
  }

  ~VariantStorage() {}
};

template <typename... Types> class Variant {
public:
  using FirstStoredType = typename FirstType<Types...>::Type;

  Variant()
      : ActiveIndex(0),
        Storage(std::integral_constant<std::size_t, 0>{},
                FirstStoredType{}) {}

  template <typename Type>
  Variant(Type &&Input)
      : ActiveIndex(TypeIndex<std::decay_t<Type>, Types...>::Value),
        Storage(std::integral_constant<
                    std::size_t,
                    TypeIndex<std::decay_t<Type>, Types...>::Value>{},
                std::forward<Type>(Input)) {}

  ~Variant() { Storage.destroy(ActiveIndex); }

  template <std::size_t Index> auto &get() {
    return Storage.template get<Index>();
  }

  template <typename Type> Variant &operator=(Type &&Input) {
    using CleanType = std::decay_t<Type>;
    constexpr std::size_t NewIndex = TypeIndex<CleanType, Types...>::Value;

    Storage.destroy(ActiveIndex);
    Storage.template construct<NewIndex>(std::forward<Type>(Input));
    ActiveIndex = NewIndex;

    return *this;
  }

private:
  std::size_t ActiveIndex;
  VariantStorage<Types...> Storage;
};

int main() {
  Variant<int, std::string, std::size_t> Value;

  Value = std::string("Hello");
  std::printf("str = %s\n", Value.get<1>().c_str());

  Value = std::size_t(1000);
  std::printf("size_t = %zu\n", Value.get<2>());

  Value = int(2000);
  std::printf("int = %d\n", Value.get<0>());
}

Reference

C++ Interview with High Frequency Trading Company

Leave a comment