複数の意味を持つ単一の型を分解したい

## ことの発端

単一の型（値・数値）だが複数の意味が混在している場合が、たま〜にある。
例えば、「動物の色」というパラメータがあったとする。
そこには「動物」と「色」という複数の要素がグチャッている。
で、「白い犬」、「黒い犬」、「白い猫」、「黒い猫」みたいな値を取るなんてことだと、設計した人を小一時間問い詰めたい。


| 動物の色 | 値 |
| :---: | ---: |
| （不正値） |  ０  |
| 白い犬 |１|
| 黒い犬 |２|
| 茶色い犬|３|
| 白い猫 |１０１|
| 黒い猫 |１０２|
| 茶色い猫|１０３|

色が２００種類になったら、どうすんだろう？
まぁ、いいや。。。

## さて、どうしたものか

で、既存のシステムがそうなっていたら従わないとダメだったりするので、せめて自分のコードでは整理した状態でパラメータを扱いたい。
で、どう整理するのかと言うと、大概こんな感じになるんじゃないかな。

| 動物の種類 | 色の種類 |
| :---: | :---: |
| 犬 | 白 ・ 黒 |
| 猫 | 白 ・ 黒 |

そして、これを enum型 で定義すると

```cpp
enum class Dog { Invalid = 0, White = 1, Black = 2, Brown = 3 };
enum class Cat { Invalid = 0, White = 101, Black = 102, Brown = 103 };
```

こんな感じかな。
さて、まぁ、これで整理できたとして、プログラムをガシガシ書いていきましょう。
んで、<strike>頭おかしい</strike>システムに値を投げるんだけど、その際は「動物の色」となるわけで、脳内ではこんなことを考える。

```cpp
enum class Animal {
  enum class Dog { Invalid = 0, White = 1, Black = 2, Brown = 3 };
  enum class Cat { Invalid = 0, White = 101, Black = 102, Brown = 103 };
};
```
こうすれば、Animal という enum型 に Dog と Cat がカテゴライズされて。。。と、いう妄想は実現しません。コンパイルエラーになりまする。

そこでこんなコードにしてみる。

```cpp
struct Animal {
  enum class Dog { Invalid = 0, White = 1, Black = 2, Brown = 3 };
  enum class Cat { Invalid = 0, White = 101, Black = 102, Brown = 103 };
};
```

ふむ、エラーは出ないが、これだと Animal::Dog と Animal::Cat という２つのenum型が存在するだけで、スコープ解決演算子が増えただけですね。

## これで挫けると

* enumを数値として取り扱う
* 数値からenumに変換する
* enum の数値変換はコンパイラの警告対象だから static_cast して、コード読みにくいけど勘弁ね
* Dog::White を数値して扱って、プラス１００すれば Cat::White じゃんという、とんでもコードが出現

とまぁ、だいたいこういうオチになるんですよね。

## 無い知恵絞って考えた対応策

そこで、こんなん実装してみて、ある程度、使い物になっている。。。
。。。気がするだけかも知れん


```cpp
#include <iostream>
#include <type_traits>

/* class 内 enum class に長ったらしいパスワード的な定義をして識別用として使用する */
#define MAGICWORDS Invalid_B18210C068D542BA965D8EACE74EF7FA

class Animal {
private:
  using       value_type = int;
  value_type  m_value;
  static constexpr value_type invalid_value = 0;

  template <typename T>
    void set(T&& value) {
      m_value = static_cast<value_type>(value);
    }

  template <typename T>
    Animal& setAndReturnThis(T&& value) {
      set(std::move(value));
      return *this;
    }

public:
  enum class Dog: value_type {
    MAGICWORDS = invalid_value,
    Invalid = invalid_value,
    White = 1,
    Black = 2,
    Brown = 3
  };

  enum class Cat: value_type {
    MAGICWORDS = invalid_value,
    Invalid = invalid_value,
    White = 101,
    Black = 102,
    Brown = 103
  };

  Animal(): m_value(invalid_value) {}
  template <typename T, T = T::MAGICWORDS>
    Animal(T&& dog) { set(std::move(dog)); }

  bool valid() const { return m_value != invalid_value; }

  template <typename T, T = T::MAGICWORDS> 
    Animal& operator = (T&& value) {
      return setAndReturnThis(std::move(value));
    }

  bool operator == (const Animal& animal) const { return m_value == animal.m_value; }
  bool operator != (const Animal& animal) const { return !(*this == animal); }

  template <typename T, T = T::MAGICWORDS> 
    bool operator == (T&& value) const {
      return m_value == static_cast<value_type>(value);
    }

  template <typename T, T = T::MAGICWORDS> 
    bool operator != (T&& value) const {
        return !(*this == std::move(value));
    }

  template<typename T, T = T::MAGICWORDS>
    T valueAs() const {
      return static_cast<T>(m_value);
    }

  /* 良い子はこんな関数名付けちゃダメですよ */
  /* ただ、後でリスクを伴う代入・取得の箇所を探すのには長めの名前にするのはかなり有効です */
  value_type getRawValueForCrazyServer() const {
    return m_value;
  }

  void setRawValueForCrazyServer(value_type value) {
    m_value = value;
  }
};

int main(void) {
  auto animal = Animal(Animal::Dog::White);
  animal = Animal::Cat::Black;
  std::cout << animal.rawValue() << std::endl;  /* 102が表示 */

  animal = 1;   /* ERROR: 即値の代入は不可 */
  auto some = static_cast<Animal::Dog>(99);
  animal = some; /* ERROR: 代入できるのは右辺値のみ（得体の知れないものはsetRawValueForCrazyServer()に誘導したい） */

  auto cat = animal.valueAs<Animal::Cat>();
  cat = Animal::Dog::Brown; /* ERROR : cat は Animal::Cat型 */

  auto dog = animal.valueAs<Animal::Dog>(); /* 本来できないが、私の力量では防げない（チェック機構を実装して例外発生はできるけど静的解析は不可能） */

  auto some = animal.valueAs<int>();  /* ERROR: Cat か　Dog 以外の型変換禁止 */

  enum class Sheep {
    White = 999
  };
  auto sheep = animal.valueAs<Sheep>(); /* ERROR: Cat か　Dog 以外の型変換禁止 */
}
```

と、この記事を書いていて、オペレータオーバーロードを template にすれば手数が減るということに気付いたので、これから対応しましょう。

## 戒め

こんな記事書いたって、誰の役に立つわけないし、何が嬉しいんだと思っていたが、案外、状況整理や実装方法の検討に使えると知り反省しなきゃだね。

## 夢で

クラスにすれば幸せになれるのかも！
と、コードを書き書きしてみたけど。。。

```cpp
#include <iostream>
#include <optional>

class Animal {
public:
    using value_type = int;
    using optional_t = std::optional<Animal>;

private:
    value_type m_value;

protected:
    Animal(value_type v) : m_value(v) {}
    Animal() = delete;

public:
    bool operator == (const Animal& src) const { return m_value == src.m_value; }
    bool operator != (const Animal& src) const { return m_value != src.m_value; }
    value_type value() const { return m_value; }

    static optional_t fromRawValue(value_type v) { return Animal(v); }
    static optional_t Invalid() { return std::nullopt; }
};

template <typename T, Animal::value_type N> class TAnimal : public Animal {
protected:
    TAnimal() : Animal(N) {}

public:
    static optional_t create() { return TAnimal(); }
};

struct Dog {
  struct Black : public TAnimal<Black, 1> {};
  struct White : public TAnimal<White, 2> {};
  struct Brown : public TAnimal<Brown, 3> {};
};

struct Cat {
  struct Black : public TAnimal<Black, 101> {};
  struct White : public TAnimal<Black, 102> {};
  struct Brown : public TAnimal<Black, 103> {};
};

int main(void){
  auto animal = Dog::White::create();
  animal = Cat::White::create();
  if(animal){ /* invalid は nullopt で確認 */
      std::cout << animal->value() << std::endl;  /* 102が表示される */
  }
  
  std::cout << sizeof(*animal) << std::endl;    /* 4 バイト!　*/

  /* そして、下手なキャストはもうできない（パチパチ） */

  /* ん？　メモリ節約で 継承関係はあるものの vtable ないので typeid 取れない*/
  std::cout << typeid(*animal).name() << std::endl; /* 6Animal が表示される */

  /* 詰んだ。。。 */
//   switch(*animal){
//      case は整数値じゃないとあかん。。。
//   }
}
```

はぁ〜〜〜、我ながら、何やってるんだか。。。

あ、別のコードで、Animalに vtable 作ってみたんだけど、typeid の結果は同じだった。。。
std::optional って型消去しちゃうんかしら？
あんまし使わない（人生で数えるくらい）ので、気にする程度に留めておくか。。。

## 感謝です！

@colonq さんのアドバイスを受け完成しました！

```cpp
#include <iostream>

namespace Animal {
    class Value
    {
    public:
        Value() : type(0) {} // Invalid
        constexpr explicit Value(int i) : type(i) {}
        bool operator == (const Value& a) const { return type == a.type; }
        bool operator != (const Value& a) const { return type != a.type; }
        constexpr int get() const { return type; }
    private:
        int type;
    };

    namespace Dog
    {
        constexpr Value Invalid(0), White(1), Black(2), Brown(3);
    }
    namespace Cat
    {
        constexpr Value Invalid(0), White(101), Black(102), Brown(103);
    }
    Value reinterpretation(int n) { return Value(n); }
}

int main()
{
    Animal::Value a = Animal::Dog::Black;
    std::cout << a.get() << " : ";
    if (a == Animal::Cat::Black) std::cout << "黒猫" << std::endl;
    if (a == Animal::Dog::Black) std::cout << "黒犬" << std::endl;
    switch (a.get())
    {
    case Animal::Cat::Black.get():
    case Animal::Dog::Black.get():
        std::cout << "クロ" << std::endl;
        break;
    }
    
    Animal::Value b = Animal::reinterpretation(5);
    std::cout << b.get() << std::endl;
    
}
```

まぁ〜、なんちゅうか、自分のコードは捏ねくり回している割には、まだまだC++使えてないな〜と。。。（苦笑）
兎にも角にも、こんなヘボコードにコメントいただいた、@colonq さん、 @SaitoAtsushi さん、ありがとうございました!

さて。。。　再度、本業のコードをリファクタリングしなきゃだな。

## 更に

リファクタリングしていると、案外 switch 使っているケースが多いな。。。
ということで、Value から ValueConstant を作って operator int() のオーバーロードを実装して、コンパイル時定数な Value 達は ValueConstant クラスにすることで、コードがかなりスッキリしました。
いや〜 @colonq のアイデアがなければ、ここまで整理できませんでした。
重ね重ね、ありがとうございました！

```cpp
#include <iostream>

namespace Animal {
    class Value {
    public:
        Value() : type(0) {} // Invalid
        constexpr explicit Value(int i) : type(i) {}
        bool operator == (const Value& a) const { return type == a.type; }
        bool operator != (const Value& a) const { return type != a.type; }
        constexpr int get() const { return type; }
    private:
        int type;
    };

    Value reinterpretation(int n) { return Value(n); }

    class ValueConstant : public Value {
    public:
        constexpr explicit ValueConstant(int i) : Value(i) {}
        constexpr operator int () const { return get(); }
    };
    
    namespace Dog
    {
        constexpr ValueConstant Invalid(0), White(1), Black(2), Brown(3);
    }
    namespace Cat
    {
        constexpr ValueConstant Invalid(0), White(101), Black(102), Brown(103);
    }
}

int main()
{
    Animal::Value a = Animal::Dog::Black;
    std::cout << a.get() << " : ";
    if (a == Animal::Cat::Black) std::cout << "黒猫" << std::endl;
    if (a == Animal::Dog::Black) std::cout << "黒犬" << std::endl;
    switch (a.get())
    {
    case Animal::Cat::Black:
    case Animal::Dog::Black:
        std::cout << "クロ" << std::endl;
        break;
    }
    
    Animal::Value b = Animal::reinterpretation(5);
    std::cout << b.get() << std::endl;
}
```
## 「更に」を更に

「更に」の方式でリファクタリングしていたところ「ValueConstって外で生成できるな～」なんて思っていたのですが、「他の案件あるし。。。このくらいやっときゃ、下手な使い方する人も居なかろうから、もうここで良いか」と思っていたんです。
が、しかしです。。。
流石です。。。
流石すぎます @SaitoAtsushi さん。
うん、見事なツッコミです。
お陰で、目が覚めました。
忙しいなんて言い訳になりません！
ちゃんとやらなきゃですよね！

ということで、「更に」の方式を更に改善しました。

* Value の生成は reinterpretation() のみに制約
* Constant （旧 ValueConstant)は Dog, Cat 以外生成不可
* Value の生成が限定的になったので、変換コンストラクタを許容（個人的に「＝」の方が見やすいし編集しやすいかなぁ～と）
* Dog::Invalid と Cat::Invalid がwitch～caseで衝突するので、廃止して vaiid() を新設


```cpp
#include <iostream>

namespace Animal {
    class Value {
    friend class Constant;
    private:
        int type;
        constexpr Value(int i) : type(i) {}

    public:
        Value() : type(0) {} // Invalid
        bool operator == (const Value& a) const { return type == a.type; }
        bool operator != (const Value& a) const { return type != a.type; }
        constexpr int get() const { return type; }

        constexpr bool valid() const { return type != 0; }
        static Value reinterpretation(int n) { return Value(n); }
    };

    class Constant : public Value {
    friend struct Dog;
    friend struct Cat;
    private:
        constexpr Constant(int i) : Value(i) {}

    public:
        constexpr operator int () const { return get(); }
        Constant() = delete;
    };

    struct Dog
    {
        static constexpr Constant White = 1;
        static constexpr Constant Black = 2;
        static constexpr Constant Brown = 3;
    };

    struct Cat
    {
        static constexpr Constant White = 101;
        static constexpr Constant Black = 102;
        static constexpr Constant Brown = 103;
    };
}

int main()
{
    Animal::Value a = Animal::Dog::Black;
    std::cout << a.get() << " : ";
    if (a == Animal::Cat::Black) std::cout << "黒猫" << std::endl;
    if (a == Animal::Dog::Black) std::cout << "黒犬" << std::endl;
    switch (a.get())
    {
    case Animal::Cat::Black:
    case Animal::Dog::Black:
        std::cout << "クロ" << std::endl;
        break;
    }

    Animal::Value b = Animal::Value::reinterpretation(5);
    std::cout << b.get() << std::endl;
    if(b.valid()) {}
    
    a = b;
}
```