作成日:2020-07-31、最終更新日:2020-08-06
この、cppにおけるHTTPヘッダ、リクエストボディ、リターン構造の定義についてお話しました:
今日のタスクは「[簡単] cpptomlを導入してファイルから設定を読み込み、spdlogを導入してデバッグ用のログを打ってみる」でした。
すべてのコードは github.com/htt あります 。
やりましょう
x1 設定ファイルをロードする理由
一度コンパイルされたプログラムのクラスは、n回実行することができ、いくつかのテストに合格すると、一年中動作することはよく知られています。しかし、いくつかの方法でプログラムの動作を変更しようとする場合があります:
- プログラムの設定項目は、リスニング・ポートだけでなく、例えばtls証明書の場所など、実際にはもっとたくさんあります。
- 通常、日常的な使用は、いくつかの問題を引き起こすログの山積みの多数を防ぐために、ログのほんの一部を印刷しますが、プログラムが異常である場合、私はより多くの手順を印刷できるようにしたい、あなたはログプログラムの印刷レベルを調整する必要があります。
このような内容の修正に遭遇した場合、再度コンパイルすることは望ましくありません。
また、これらのコマンドを標準入力で入力したり、実行時に引数として渡したりするのはあまり便利ではないので、設定ファイルを使用することになります。
設定には TOML フォーマットを使うことをお勧めします。 TOML フォーマットのおかげでインデントを気にする必要がありませんし、サポートされているフォーマットも、配列、フロート、文字列など多岐にわたります。
いくつか選択した後、 cpptoml というライブラリを見つけました。
x2 cpptoml
A header-only library for parsing TOML configuration files.
インストール
git clone https://./-/.it
cd toml; mkdir build; cd build
cmake .. && make && make install
これを見れば、cmakeが何のためにあるのかわからなくても、いかに一般的なものかわかりますよね?ここまでで、sockppとcpptomlをインストールしましたが、片方はリンクされたライブラリで、もう片方はヘッダのみです。
一挙手一投足
この例のコードはすべて、コース開始時に基づいています。
#include<iostream>
#include "cxxhttp.h"
#include "cpptoml.h"
int main() {
auto config = cpptoml::parse_file("./config.toml");
auto port = config->get_qualified_as<in_addr_t>("ENV.port");
std::cout << "server will start at port " << *port << '
';
Request req("GET", "/", "body");
...
}
こんな風に作ってみると
» make
[ 25%] Building CXX object CMakeFiles/main.dir/main.cpp.o
/Users/dashjay/CLionProjects/http_demo/main.cpp:4:10: fatal error: 'cpptoml.h' file not found
#include "cpptoml.h"
include_directories(/usr/local/include)
同じ理由で、CMakeLists.txt に cpptoml のインストール先を追加する必要があります。
実行すると次のようになります。
server will start at port 8083
....
前提条件として、config.tomlファイルがrunディレクトリの一番下にあること。
[ENV]
port = 8083
なぜ*portを使って値を取得するのか: ソースコードを見てわかったのですが、auto port = .... はターゲット型のオブジェクトではなく、以下のコードで説明されているように、optionと呼ばれるオブジェクトです。
template <class T>
class option
{
public:
......
explicit operator bool() const
{
return !empty_;
}
const T& operator*() const
{
return value_;
}
const T* operator->() const
{
return &value_;
}
...
ご覧のように、返されるオブジェクトはbool()、*、->演算子でオーバーロードされています。それぞれ、 オブジェクトが空かどうかの値、オブジェクトへの参照、オブジェクトへのポインタを取得できます。
x3 std::cout または std::cerr をロギング出力に使用しない理由
std::cout << ...
標準出力と標準エラーログへの出力は、マルチスレッドによるオーバーラップが発生します。ここでは実演しませんが、100スレッドが堂々と使用した場合に発生するオーバーラップ、ロスの量を想像してください。
ログプログラムにはロックのオーバーヘッドがありますが、今のところその価値はありそうです:
- デバッグに役立つ、さまざまな色とレベルのラベル
- 複数のスレッドを使用しても見当違いではないこと
x4 spdlog
非常に高速で、ヘッダのみ/コンパイル済みの C++ ロギングライブラリ。
log4cppのようなロギング・ライブラリをいくつも探した結果、それらは肥大化していて、中には個別の設定ファイルを必要とするものもあり、このライブラリのように使いやすいものではありませんでした。
スプドログ
インストール方法は言うまでもないでしょう。これも純粋なヘッダー・ライブラリで、リンクの必要はありません。
いくつかのコードを変更した後のメインファイルは次のようになります。
#include<iostream>
#include "spdlog/spdlog.h"
#include "cxxhttp.h"
#include "cpptoml.h"
int main() {
auto config = cpptoml::parse_file("./config.toml");
auto port = config->get_qualified_as<in_addr_t>("ENV.port");
std::cout << "server will start at port " << *port << '
';
Request req("GET", "/", "body");
spdlog::info("request: {}", req.to_string());
Response resp;
resp.proto = "HTTP/1.1";
resp.status = "200 OK";
resp.body = "body";
spdlog::info("response: {}", resp.to_string());
}
リクエストの{}はプレースホルダーです。 通常の%s %dで文字列をフォーマットする代わりに、彼は{}を使っています。
これ以上高度なことが必要なければ、グローバル・コールで十分です。
今日の内容はちょっと水増しです。これは、以下の最も難しいセクションのための水増しです。再コンパイルし直せば問題が解決すると思っている人が、狂ったようにmake ....
Footnotes
std::cout << あるいはstd::cerr <<
私はこのことを確認するためにコードを書いたわけでも、ソースコードを読んだわけでもありませんし、ここでそのように記述するのは無責任ですが、マルチスレッドの使用は確実にエラーを発生させる方法です。