text: #ifndef EITHER_H
  #define EITHER_H
  
  #include <functional>
  #include <string>
  #include <type_traits>
  
  #include "converter.h"
  
  /**
   * The Either<A,B> class holds either something of type
   * A or of type B. This is essentially what is called
   * a 'tagged union' or 'variant' in C++17.
   * This only works properly if A and B are sufficiently
   * incompatible and the C++ function overloading finds the right
   * constructor.
   */
  template<typename A, typename B>
  class Either {
  public:
      /** @brief perform a case distinction and call either a
       *  handler for the type A or for the type B. Both
       *  handlers have the same return type X, so in any of the
       *  two cases, the case distinction returns something of type X.
       *
       *  (Up to the side effects of the handlers, this is essentially
       *  what's also called the "universal property of the coproduct")
       * @param the handler for type A
       * @param the handler for type B
       * @return the return value of the appropriate handler
       */
      template<typename X> // using X = std::string;
      X cases(std::function<X(const A&)> onA, std::function<X(const B&)> onB) const {
          if (isA_) {
              return onA(data_.a_);
          } else {
              return onB(data_.b_);
          }
      }
  
      // since 'void' is not a typename, we need to handle this explicitly:
      void cases(std::function<void(const A&)> onA, std::function<void(const B&)> onB) const {
          if (isA_) {
              return onA(data_.a_);
          } else {
              return onB(data_.b_);
          }
      }
      Either(const A& a)
          : data_(a), isA_(true)
      {}
      Either(const B& b)
          : data_(b), isA_(false)
      {}
  private:
      Either() = delete;
      union UnionAB {
          UnionAB(A a) : a_(a) {}
          UnionAB(B b) : b_(b) {}
          A a_;
          B b_;
      };
      UnionAB data_;
      bool isA_;
  };
  
  class Completion;
  
  /**
   * The Converter for Either<A,B> combines the Converter of A and B.
   * In the parsing, priority is given to A, i.e. if the parser for A
   * succeeds it is not even attempted to parse the given source
   * string to something of type B. All other functions are symmetric
   * in A and B.
   */
  template <typename A, typename B>
  class Converter<Either<A,B>, typename std::enable_if< std::true_type::value >::type> {
  public:
      static std::string str(const Either<A,B>& payload) {
          return payload.template cases<std::string>(Converter<A>::str, Converter<B>::str);
      }
      static Either<A,B> parse(const std::string& source) {
          try {
              return Converter<A>::parse(source);
          }  catch (const std::exception& e1) {
              try {
                  return Converter<B>::parse(source);
              }  catch (const std::exception& e2) {
                  throw std::invalid_argument(std::string(e1.what()) + "; or: " + e2.what());
              }
          }
      }
      static Either<A,B> parse(const std::string& source, const Either<A,B>&) {
          return parse(source);
      };
      static void complete(Completion& complete, Either<A,B> const*) {
          Converter<A>::complete(complete, nullptr);
          Converter<B>::complete(complete, nullptr);
      }
      static void complete(Completion& completionObject) {
          complete(completionObject, nullptr);
      }
  };
  
  #endif // EITHER_H
  