2024/8/25(日)

ローゼンメイデンを読み直し進めている。今までの主人たちと指輪はどうなってたのという話、思いっきりちゃんと説明されていた。読んだのに覚えてなかった。

ドールが指輪を失ったとき、ローザミスティカを別のドールに差し出した場合はその後永遠にゲームを降りることになる。一方でそうでなかった場合、鞄の中で眠って長い時間をかけて指輪を再構成できる。なんか雛苺が指輪を失ったシーンの「敗北!終わり!」的な印象が強すぎて忘れていたが、指輪は再び作れるのだ。第1部開始から終了までに雛苺、蒼星石、真紅、翠星石が指輪を失っており、蒼星石と真紅は例外的にローゼンの干渉によって一等早く指輪を再構成したとされている。

物語開始時点で1体も欠けていなかったということは今まで誰も敗北で指輪を失ったことが無かったということになる。そういえば「7体が同時に目覚めた」ことに驚いていたのでそもそも起きるタイミングが揃うこと自体が中々なかったらしい。

明確に書いてはいないが「指輪は原則1体につき1つ」というのは恐らく、人間の寿命と指輪を再構成するのにかかる時間の都合で基本的にはそうなるくらいのイメージなんじゃないかと想像している。こういう何かに利用できそうな微妙なルールの曖昧さを探すのが結構好き。オディールさんの指輪の件について「いや契約の条件満たすの無理じゃね?」「nのフィールド経由なら物理的距離関係なくね?」「ほな…いけるんかな…」みたいな会話とか。

ところで、「人工精霊」というものの存在の立ち位置についてよく分からないでいたのだが、人形たちの造り主である「お父様」=ローゼンを造物主=神とする見立てが終盤で強調されていることを考えると、名前からしてキリスト教における聖霊にあたるものとして描かれているのかなという気がしている。


C++でコールバック的なものをやろうと思ったらstd::functionが筆頭に上がるが、それがやたら遅いらしいことにどうにも納得がいかない。ので、代替を考えてみた。

要するにどの関数なのかとか普通の関数なのか関数オブジェクトなのかとかそういうのを実行時情報として持っているのがいかんのだ。型情報に全部静的に埋め込んでしまえばそんなオーバーヘッドは生じ得ない。問題はそれをいかにきれいに書けるかどうかだ。

頑張ってみたところ、思ったより良い感じになった。

#include <iostream>
#include <utility>

template <class Event, class Handler, class Parent, class Base>
class EventHandlerContainer : public Parent {
    Handler handler;

  public:
    EventHandlerContainer(Parent &&_parent, Handler &&_handler)
        : Parent(std::move(_parent)), handler{std::move(_handler)} {}

    template <class TargetEvent, class... Args>
    auto call(Args &&...args) {
        if constexpr (std::is_same_v<TargetEvent, Event>) {
            return handler(std::forward<decltype(args)>(args)...);
        } else if constexpr (std::is_same_v<Parent, Base>) {
            return;
        } else {
            return this->Parent::call<TargetEvent>(std::forward<decltype(args)>(args)...);
        }
    };

    template <class NewEvent, class NewF>
    auto on(NewF &&f) && {
        using SelfType = EventHandlerContainer<Event, Handler, Parent, Base>;
        using NewType = EventHandlerContainer<NewEvent, NewF, SelfType, Base>;
        return NewType(std::move(*this), std::move(f));
    }
};

template <class T>
class EventHandlerBuilder {
    T &base;

  public:
    EventHandlerBuilder(T &_base) : base(_base) {}

    template <class NewEvent, class NewF>
    auto on(NewF &&f) && {
        return EventHandlerContainer<NewEvent, NewF, T, T>(std::move(base), std::move(f));
    }
};

class Hoge : public EventHandlerBuilder<Hoge> {
  public:
    Hoge() : EventHandlerBuilder(*this) {}
};

struct Ev1 {};
struct Ev2 {};

int main() {
    auto hoge =
        Hoge{}
            .on<Ev1>([](int a) {
                std::cout << "Ev1: " << a << std::endl;
            })
            .on<Ev2>([](double p, const char *q) {
                std::cout << "Ev2: " << p << ' ' << q << std::endl;
            });

    hoge.call<Ev1>(123);
    hoge.call<Ev2>(3.14, "Hello");
}

EventHandlerContainerおよびEventHandlerBuilderがキモだ。これらがあれば、class Hogeの実装者はそれらを利用するだけで様々なイベントのハンドラの設定機能を自作のクラスに実装できる。

Categories: