enable_shared_from_this から派生したクラスを確実に shared_ptr で生成する

## ことの発端

非同期処理を実装していると `std::enable_shared_from_this` をよく使う。
でも、`std::enable_shared_from_this`から派生したオブジェクトは`std::shared_ptr`で所有するコードに縛るとなると、コードを記述する人の匙加減になってしまうため、これを制限したい。


## 問題点

下記のようなコードの場合、　`BAZ` が `std::shared_ptr` で所有されないとクラッシュします。

```cpp
#include <memory>

class FOO : public std::enable_shared_from_this<FOO> {
  int foo_value_;
public: 
  FOO(int value) : foo_value_(value) {}
  void foo() {
    auto instance = shared_from_this();
    std::cout << instance->foo_value_ << std::endl;
  }
};

int main() {
  auto foo = std::make_shared<FOO>(123);
  foo->foo();

  auto foo2 = FOO(456);
  foo2.foo(); // <-- crash!
}
```

### とりあえずの解決方法

よくあるのが、コンストラクタを`protected`か`private`にして、生成関数を作る方法。

```cpp
#include <memory>

class FOO : public std::enable_shared_from_this<FOO> {
private:
  int foo_value_;

protected:
  FOO(int value) : foo_value_(value) {}

public: 
  template<typename ...ARGS> static auto create(ARGS&&...args) {
    // std::make_shared が使えないけど、本題はココじゃないから、まぁ、いいか。。。
    return std::shared_ptr<FOO>(new FOO(std::forward<ARGS>(args)...));
  }

  void foo() {
    auto instance = shared_from_this();
    std::cout << instance->foo_value_ << std::endl;
  }
};

int main() {
  // コンパイルエラー
  // auto foo = std::make_shared<FOO>(123);
  // foo->foo();

  // コンパイルエラー
  // auto foo2 = FOO(456);
  // foo2.foo();

  auto foo2 = FOO::create(456);
  foo2->foo();
}
```

一見、これでも良いように思えるが、この `FOO` を派生すると、話がややこしくなる。

```cpp
#include <memory>

class FOO : public std::enable_shared_from_this<FOO> {
protected:
  int foo_value_;
  FOO(int value) : foo_value_(value) {}

public: 
  template<typename ...ARGS> static auto create(ARGS&&...args) {
    return std::shared_ptr<FOO>(new FOO(std::forward<ARGS>(args)...));
  }

  void foo() {
    auto instance = shared_from_this();
    std::cout << instance->foo_value_ << std::endl;
  }
};

class BAR : public FOO {
  int bar_value_;
public:
  BAR(int value1, int value2) : FOO(value1), bar_value_(value2) {}
};

int main() {
 auto bar = BAR(1, 2);
 bar.foo(); // <-- crash!
}
```

まぁ、`BAR`にも生成関数を作れば良いのだけど、もう少し制約を設けて、設計意図を後世に伝えられないかとという課題になります。

## 制限と生成を一気に解決する

まず、`must_be_shared` というクラスを定義します。
このクラスは `_magic` とそれを初期化する `_magic_seed` という空の構造体がキモとなります。
そして、`must_be_shared`の生成には`_magic`（これは`magic_t`で再定義され実質`protected`です）が必要になりますが、`_magic`のコンストラクタ引数の`_magic_seed`を生成できるのは `must_be_shared`　のみです。

```cpp:must_be_shared
#include <memory>

template <typename T> class must_be_shared :
  public std::enable_shared_from_this<T>
{
private:
  struct _magic_seed {};
  struct _magic { _magic(const _magic_seed&) noexcept {} };

protected:
  using magic_t = typename must_be_shared<T>::_magic;
  must_be_shared(const magic_t&) noexcept {}

public:
  virtual ~must_be_shared() = default;

  template <typename ...ARGS>
  static auto create(ARGS&&...args) {
    return std::make_shared<T>(_magic{_magic_seed{}}, std::forward<ARGS>(args)...);
  }

  template <typename Derived, typename OutType = Derived, typename ...ARGS>
  static auto create(ARGS&&...args) {
    return std::static_pointer_cast<OutType>(
      std::make_shared<Derived>(_magic{_magic_seed{}}, std::forward<ARGS>(args)...)
    );
  }

  template <typename X> auto shared_from_this_as() noexcept {
    return std::dynamic_pointer_cast<X>(must_be_shared<T>::shared_from_this());
  }
};
```

### 使い方

1. `std::shared_ptr` を `must_be_shared` に置き換える。
1. コンストラクタを`public`に配置する。
1. コンストラクタの第１引数に `const magic_t&　_` と書く。
1. コンストラクタの初期化子に `must_be_shared<XXX>(_)` と書く。（`XXX`はクラス名）
1. 生成関数`create()`にはコンストラクタの第２引数以降を設定する。

コンストラクタを `public` に配置することを強要しますが、`magic_t`は`must_be_shared`内部でしか生成する手段が提供されていないため、ローカルでコンストラクタを呼び出すことは不可能になります。

### サンプル

```cpp
class FOO : public must_be_shared<FOO> {
private:
  int foo_value_;
public:
  FOO(const magic_t& _, int foo_value):
    must_be_shared<FOO>(_),
    foo_value_(foo_value)
    {}
  void foo() {
    auto instance = shared_from_this();
    std::cout << instance->foo_value_ << std::endl;
  }
};

int main() {
    auto foo = FOO::create(1);
}
```

で、`FOO`を派生して`BAR`を定義する場合は、下記のようになります。

```cpp
class BAR : public FOO {
private:
  int bar_value_;
public:
  BAR(const magic_t& _, int bar_value = 100, int foo_value = 200):
    FOO(_, foo_value),
    bar_value_(bar_value)
     {}
  void bar() {
    auto instance = shared_from_this_as<BAR>();
    std::cout << instance->bar_value_ << std::endl;
  }
};

int main() {
  // コンパイルエラー
  // auto foo = FOO(FOO::magic_t{FOO::_magic_seed{}}, 1);

  // 基本的な生成方法
  auto foo = FOO::create(1);
  foo->foo();

  // 派生したクラスを生成する方法
  auto bar = BAR::create<BAR>(10, 20);
  bar->foo();
  bar->bar();

  // デフォルトパラメータも使える
  auto bar_default = BAR::create<BAR>();
  bar_default->foo();
  bar_default->bar();

  // ベースクラスで管理したい場合の生成方法
  auto bar_as_foo = BAR::create<BAR, FOO>(10, 20);
  bar_as_foo->foo();
  // bar_as_foo->bar(); // <-- compile error
  bar_as_foo->shared_from_this_as<BAR>()->bar();
}

// foo -> 1
// foo -> 20
// bar -> 10
// foo -> 200
// bar -> 100
// foo -> 20
// bar -> 10
```

## 所感

コンストラクタにお約束事が出てきますが、毎回`create`みたいな生成関数を作成する必要がなくなるし、設計意図を後世に伝えるという意味でも、良いかも？

ただ、下記のコードが許される。

```cpp
  auto bar = BAR::create(1);
```

この場合、`std::shared_ptr<FOO>`が出来上がってしまう。
なので、`template <typename ...ARGS> static auto create(ARGS&&...args)` は削除して明示的に生成クラスを指定した方がバグが少なくなるかも知れない。

```cpp
  auto foo = FOO::create<FOO>(1);
  auto bar = BAR::create<BAR>(1);
```


https://github.com/CODIANZ/must_be_shared