1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

drogon に hypodermic を組み込み(C++でDependency Injection)

Last updated at Posted at 2025-03-05

はじめに

drogon はC++によるフルスタックのWebフレームワークです。
サンプルコードはわかりやすさ優先と思われますが,コントローラ内で色々やっています。

この記事は,Java の Spring と同じように,DIコンテナで疎結合にしたい... と思ってそのやり方を調べたメモです。
DIコンテナとしてこの記事では hypodermic を使いますが,他のライブラリでも同じようにできるはずです。

方針

drogon のプラグインとして,hypodermic を保持するものを作ります。
これだけだと,コントローラ(HttpControllerなど)にインジェクションができないので,コントローラ生成をテンプレート特殊化でフックし,hypodermic でインスタンスを作るようにします。

hypodermic を使うプラグイン

こんなコードを書きます。

class HypodermicPlugin : public drogon::Plugin<HypodermicPlugin>
{
    std::shared_ptr<Hypodermic::Container> m_container;
  
public:
    // drogon から呼び出されるプラグイン初期化関数
    void initAndStart(const Json::Value &config) override
    {
        // DIコンテナのbuilderに対象のクラスとインスタンスを登録
        Hypodermic::ContainerBuilder builder;

        // TODO: ここでbuilderにクラスを登録
        
        // DIコンテナ構築
        m_container = builder.build();
    }

    // drogon から呼び出されるプラグイン終了関数
    void shutdown() override
    {
        m_container = nullptr;
    }

    // コンテナへのアクセッサ
    std::shared_ptr<Hypodermic::Container> getContainer()
    { 
      return m_container;
    }
};

main関数など初期化時にこのプラグインを登録します。

void main()
{
    auto &instance = drogon::app();
    instance.addPlugin("HypodermicPlugin", Json::Value::Members{}, Json::Value{});

    // 以後,待ち受けポートの設定や instance.run() 呼び出し
    // ...
}

これで,HttpController継承クラスでこんな処理が書けます。
SomeClassはhypodermicに登録したタイプです。

auto plugin = drogon::app().getSharedPlugin<HypodermicPlugin>();
auto container = plugin->getContainer();
auto instance = container->resolve<SomeType>();

コントローラ生成処理のフック

上記コードで,コントローラ内で色々なクラス(例えばdrogon::orm::DbClient)をhypodermic から取得できます。

ただ,コントローラそのものはdrogonが生成しているので,コントローラに依存性を注入することができません。
コントローラ生成は,drogonのクラスマップにコントローラが登録される時,ファクトリ関数オブジェクトを同時に登録することで行われています。
具体的な処理は, drogon::DrObject::DrAllocator::registerClass() メソッドです。

以下のようなコードを書くと,テンプレート特殊化でこれを上書きし, hypodermic を使ってコントローラを生成することができます。
SomeClassはhypodermicに登録したコントローラクラスです。

namespace drogon {
    // DIコンテナを使ってUserControllerを構築するように drogon を設定(テンプレート特殊化)
    template <>
    template <>
    inline void DrObject<SomeClass>::DrAllocator::registerClass<SomeClass>()
    {
        DrClassMap::registerClass(
            className(),
            []() -> DrObjectBase * { return nullptr; },  // 生成処理(生ポインタ返却)
            []() -> std::shared_ptr<DrObjectBase> {    // 生成処理(スマートポインタ返却)
                auto plugin = drogon::app().getSharedPlugin<HypodermicPlugin>();
                auto container = plugin->getContainer();
                return container->resolve<SomeClass>();
            });
    }
}

生ポインタを取得する関数オブジェクトは(コントローラ生成では)使われてないようなので,手抜きしています。

おわりに

これで,hypodermic のコンストラクタインジェクションをコントローラにも使えるようになります。

Javaのようにアノテーションを書けばDIできるとはならないですが,C++でもDI導入によりクラス間を疎結合にできるのが分かってよかったです。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?