C++で使えるDIコンテナがないかと探してみたところ、Hypodermicというライブラリがあった。
タグを見ると2016年から開発が始まっており、最新リリースは2019年。
ちゃんとメンテされ続けているみたい。
特徴の1つとして、ヘッダーオンリーライブラリなので取り回しが非常に良い。
ちなみに、GitHubの1行目で、HypodermicをIoC Containerとして紹介している。広義の意味でIoC Container = DI Container と思っておけば良さそう。
参考
IoCとService LocatorとDIの関係 - yotiky Tech Blog
使用環境
- Window10
- Visual studio 2019
- Boost 1.7.3 (Boostないとエラーが出る)
使い方
どんなものかサクッと試したかったので、もともとのHypodermicプロジェクトに、Mainとなるプロジェクトを追加した。
- GitHubからHypodermic-masterリポジトリをクローン
- クローンしたディレクトリ直下のHypodermic.slnを起動
- Hypodermicプロジェクトに、Mainとなるプロジェクト(SampleClientとする)を追加
- SampleClientの追加のインクルードディレクトリにHypodermic-masterのパスを追加する
- 例)C:\Hypodermic-master
- SampleClientの追加のインクルードディレクトリにBoostのパスを追加する
- 例)C:\Boost\include\boost-1_73
- SampleClientにサンプルコードを追加する
- Getting Started のコードを今回は使った。https://github.com/ybainier/Hypodermic/wiki/Getting-started
あるメッセージ → IMessageWriter → ConsoleMessagesWriter → IMessageSerializer → LengthPrefixedMessageSerializer → コンソールに出力 という流れの処理を行わせたい。
// メッセージを出力するためのクラスのインタフェース //標準出力とメッセージを受け取る class IMessageSerializer { public: virtual ~IMessageSerializer() = default; virtual void serialize(const std::string& message, std::ostream& stream) = 0; }; // 引数から受けった標準出力の方法で、受け取ったメッセージを出力する // ここではメッセージのサイズとメッセージ内容を標準出力に出す class LengthPrefixedMessageSerializer : public IMessageSerializer { public: void serialize(const std::string& message, std::ostream& stream) override { stream << message.size(); stream << message; } }; // メッセージを何らかの形で出力することを意味するインタフェース class IMessageWriter { public: virtual ~IMessageWriter() = default; virtual void write(const std::string& message) = 0; }; // 標準出力として出力する具象クラス // IMessageSerializerにメッセージと標準出力の方法を渡して処理を委任している class ConsoleMessageWriter : public IMessageWriter { public: explicit ConsoleMessageWriter(std::shared_ptr< IMessageSerializer > serializer) : m_serializer(serializer) { } void write(const std::string& message) override { m_serializer->serialize(message, std::cout); } private: std::shared_ptr< IMessageSerializer > m_serializer; };
Applicationクラスは先程の以下処理を行わせたいので、コンストラクタで依存関係を定義しコンテナに格納する。
IMessageWriterに対してConsoleMessagesWriterを具象として指定。 IMessageSerializerに対してLengthPrefixedMessageSerializerを具象として指定。
#include "Hypodermic/Hypodermic.h" class Application { public: Application() { // 各クラスが格納するコンテナを作成 Hypodermic::ContainerBuilder builder; // IMessageSerializer に対してどの具象クラスを作成するかを指定 // ここではLengthPrefixedMessageSerializerを指定する builder.registerType< LengthPrefixedMessageSerializer >() .as< IMessageSerializer >(); // IMessageWriter に対して ConsoleMessageWriter を指定する builder.registerType< ConsoleMessageWriter >().as< IMessageWriter >(); // 上記で指定した組み合わせを生成する m_container = builder.build(); } // 実行関数 void run(){ // コンテナがIMessageWriterを返す auto messageWriter = m_container->resolve< IMessageWriter >(); // 出力するメッセージを指定 messageWriter->write("The app is running"); }; private: std::shared_ptr< Hypodermic::Container > m_container; }
Main文。
#include "Hypodermic/Hypodermic.h" #include "Application.h" int main() { Application obj_app; obj_app.run(); // 「18The app is running」 と出力される return 0; }
おわりに
C++でDIコンテナをしたいときは、Hypodermicを使えば良さそう。