LoginSignup
6

More than 3 years have passed since last update.

posted at

updated at

OSS design_pattern_for_cでC言語らしく、デザインパターンを活用しよう!

はじめに

自作ライブラリパッケージ OSS design_pattern_for_cの紹介です。
wikiはこちら。各ライブラリAPIのDoxygenコードはこちらです。

最新バージョンはV1.00です。

(以前v0.0.1 pre-releaseで同名タイトルの記事を書きました。前回の記事は削除して正式版公開記事として再作成しました。)

design_pattern_for_cとは?

Linux上で動作する、C言語でデザインパターンのいい所を活用するためのライブラリパッケージです。

Cのシステム開発をしながらオブジェクト指向設計やデザインパターンに関する話を聞くたび、「そういう考え方、表現ってCでもよく使われてるよな」となることが多々あります。ただ、オブジェクト指向言語の方から見ると「これはXXXパターンではないのでは?」と思うような使い方だったりするかもしれません。
なぜかというと、よくできたCシステムは、C言語にとって効率のいいやり方でデザインパターンを利用しているからです。
ただデザインを再現するんじゃなく、Cとデザインパターンのいいとこどりをしているといった言い方も出来るかもしれません。

どの言語でもそうだと思います。便利なデザインやツールは言語に合わせた形で利用して、よりよいシステムを作り上げる。それが本来のそれらの表現と完全一致するかが目的ではなく、いいシステムを作ろう!が目的。

ただ、C言語はオブジェクト指向言語ではないので、ただオブジェクト指向の真似してしまうとただ使いにくさが増すばかりでもったいない。逆に、void *関数ポインタのような自由度の高いCの得意技を利用すれば、表現方法は違うかもしれないけど、デザインパターンという素敵な道具を活用することが出来るはず。そんな思いで出来たのがこのライブラリです。

何ができるの?

ここでは、パッケージ内のサポートライブラリ紹介を簡単に行います。ほとんどデザインパターンの紹介みたいなものですが、ご了承ください。

Threadpool

複数のファイルディスクリプタ(FD)の待ち受けを作成し、FDイベントをラップするライブラリです。
ライブラリの使用者はFDと関数ポインタを登録するだけで、ライブラリ内の空いているスレッドでそのFDを待ち受け。FDイベントに合わせて登録関数を呼んでくれます。

select, epoll等のイベント待ち受け処理をマルチスレッド化したものと思ってください。

日本語ページ
OSSページ

memorypool

同じ型(サイズ)分のメモリを毎回mallocするのではなくまとめてメモリ確保しておくことで、領域確保や初期化にかかる速度を上げることが出来ます。
上限が決まっていて初期化処理に時間のかかるようなデータに対して効果を発揮します。

ライブラリの使用者はデータサイズ、個数の他にコンストラクタを指定することが出来るので、領域確保時点でコンストラクタを利用した初期化をライブラリが行ってくれるようになっています。

日本語解説ページはないですが、こちらのページ内の更新版ラッパーを改良したものが本ライブラリになります。
OSSページ

State, StateMachine

状態とその状態の時に実行する関数を登録することで、状態遷移をStateライブラリのAPIで実現することが出来ます。複雑な状態を持つプログラム、状態ごとの振る舞いが仕様変更されやすいようなプログラムで役に立つと思います。

SateMachineはStateライブラリに状態遷移イベントを加えたライブラリとなります。状態遷移表の実現に役に立つでしょう。

日本語解説ページ
OSSページ

Observer(Publish-Subscribe)

以下のような購読者(Subscriber)-出版社(Publisher)の関係を実現したライブラリです。

  1. とある本の購読者(Subscriber)がいます。
  2. 購読者は、その出版社(Publisher)の出版物を購読(subscribe)登録します。
  3. その後、出版社が本を出版(Publish)するタイミングで、購読者は本が出版されたことを知ることが出来ます。

⇒Subscriberは登録だけしておく。そうすると、Publisherがpublishしたことタイミングで通知を受け取ることが出来る!

複数のイベント関数を同じIDのpublisherに登録することが出来、publisherのpublish APIを実行するだけでまとめて沢山のイベント関数を呼ぶことが出来ます。
イベントに付随した処理が大量にある場合に有効ではないでしょうか。

日本語解説ページ
OSSページ

Chain of Responsibility

この呼んだら次はこれ、と数珠つなぎに関数を呼びたいようなプログラムに使用することが出来るのがこのライブラリです。
ライブラリに関数を登録しておけば、API一つで登録順に関数が実行されます。途中での中断も関数のreturn値を変えることでコントロールが出来ます。

日本語解説ページ
OSSページ

Flyweighy, Singleton

使用者にインスタンス作成を意識させずに同じインスタンスを使いまわすことが出来るのがFlyweighyライブラリです。インスタンスはライブラリ内で作成・保持されます。
イコール条件を関数として登録することで、作成済みインスタンスが同じインスタンスかどうかを判断し、同じものであれば既に作成済みのインスタンスを使いまわします。

Singletonはざっくりいうとただ1つの同じインスタンスを使いまわすデザインパターンなのですが、これはFlyweighyの=条件を常にtrueにすることと同義です。なのでライブラリはFlyweighy一つです。

日本語解説ページ
OSSページ

Prototype

ある状態のデータとそのデータのコピー方法を登録することで、いつでもその状態のデータがコピーできるようになるライブラリです。
初期化が重い処理の初期化後を登録して使いまわすなど、面倒事を一度だけで済ませたい場合に効果的です。

日本語解説ページ
OSSページ

Builder

こちら、デザインパターンのBuilderと少しだけ違います。例えば複数のモジュールをコントロールしたい、でもその初期化シーケンスはモジュールごとに少し違うといったケースで役立つライブラリです。
初期処理をライブラリが受け持って簡単にしつつ、微妙に初期処理の違うモジュールを利用できるようにします。

初期化APIをconfファイルで定義し、各モジュールにAPIを実装してもらう(不要なら未実装でOK)。後はライブラリにconfファイルとモジュールを咬ませてAPIを実行すれば、ライブラリがモジュールの初期化を請け負ってくれます。

日本語解説ページ
OSSページ

正直これは改良の余地ありです(シーケンスはいいけど、どうパラメーターを受け渡すのか検討不足が否めないです)。
⇒APIの仕様を見直しました。

例えばどんなプログラムが作れるの?

HTTPサーバーマルチスレッド化

本パッケージを使用して、lighttpdをマルチスレッド化しました。
https://github.com/developer-kikikaikai/lighttpd_multithread

ライブラリ使用実績はざっとこんな感じです。

  • TCPコネクション毎のマルチスレッド動作をthreadpoolとstate machineで実現しました。重いHTTPクライアントのリクエストに影響されず他のクライアントを処理できるようになりました。
  • コネクション情報をmemorypoolを利用し、コネクション情報作成の時間短縮を行いました。
  • レスポンスヘッダー情報をflyweightにより再利用し、ヘッダー作成の時間短縮を行いました。
  • 主に設定読み込みで使用されるデータをprototype登録・クローン形式に変更し、無駄コピー削減を行いました。
  • メイン処理と各コネクションのやり取り(主に終了シーケンス)にObserverを利用し、コネクション側でイベント登録⇒メイン側でイベント実行という処理を実装。mainのコードを少し整理しました。

私の作ったマルチスレッド化プログラム自体にはまだバグがあったりfastcgi側未修正だったりしますが、各ライブラリのおかげでマルチスレッド化成功 && 速度面で本家といい勝負になっています。

下3つ対応前の性能をまとめた記事はこちらになります。
(cgiが遅かったりメモリ使用量が凄かったりしますが(-_-;)。こちらはのんびりアップデートしていきます)

マルチデバイス対応のLANルーター

builderを利用してWifi, ethernetの両方がLANネットワーク側に利用可能なルーターを作成しました。
https://github.com/developer-kikikaikai/linux_router

解説ページ
https://qiita.com/developer-kikikaikai/items/0d8ea1c057302ad26ea9

こちらはbuilderを利用しており、ロードするライブラリもjsonファイルで管理しているため、LANネットワークに新たなモジュールを追加することも可能な構成となっています。

使い方

ダウンロード

こちらからダウンロードできます。

使い方

以下手順でビルド、インストールが出来ます。

  1. ./configure
  2. make
  3. sudo make install

デフォルトは/usr/local/にインスール。./configure --prefix=XXXでインストールパスの指定も可能です。

  • autoconf, libtool, libev, libevent-devのインストールが必要です。libev, libevent-devは無効化も可能です。
  • configureが環境依存でうまく動作しない可能性があります。"[aclocal.m4]"のエラーが出る場合は./autogen.shを実行して1~3を実行してください。

詳細はwikiかREADMEを参照ください。

後は必要なライブラリをリンクすれば利用可能です。API詳細はDoxygen Codeを参照ください。

ライセンス

1つのプラグインライブラリを除いて基本はMIT Licenseです。
 MIT wikipedia

Threadpoolで利用するlibevのプラグインのみGPL version 2 or any later versionになります。
※Threadpoolをlibevで利用する際は芋づる式にThreadpoolを利用したアプリケーションもGPLライセンスとなるのでご注意ください。

変更履歴

masterブランチに対するAPI仕様変更や機能追加があった場合に追記していきます。

変更日 内容
2018/07/22 V1.00初版リリース
2018/07/29 ・builder, lower_layer_director_constructの仕様を同期/非同期指定可能なように変更 && コールバックの引数にinput_parameterを追加(メモリ解放が出来るように)。
・各種ヘッダーのC++対応

最後に

ご質問、要望あれば本記事かgithubのissueにコメントいただけると助かります。
ちなみにREADME更新を忘れてて、記載が1.00になっていないですがそこはご愛敬ということで(笑)

また、改めてコメントで色々教えていただいた皆様、翻訳のアドバイスいただいたすれいぶさん、参考にさせていただいた沢山の良記事を書かれた皆様にお礼申し上げます。
ありがとうございます。

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
What you can do with signing up
6