問題を開く必要のない質問はありますか? gitter チャンネルに参加してください。
uvw
使用していて、感謝の意を表したり、プロジェクトをサポートしたい場合は、スポンサーになることを検討してください。
違いを生むのを手伝ってください。私をサポートしてくれた人たち、そして今も私をサポートしてくれる人たちに本当に感謝しています。
uvw
最新の C++ で書かれたヘッダーのみのイベントベースの小型で使いやすいlibuv
のラッパーとして始まりました。
今回、ついにコンパイル可能な静的ライブラリとしても利用できるようになりました。
基本的なアイデアは、 libuv
のC 風のインターフェイスを優雅な C++ API の背後にラップすることです。
uvw
libuv
の API に忠実であり、インターフェイスに何も追加しないことに注意してください。同じ理由で、ライブラリのユーザーはlibuv
で使用されるのと同じルールに従う必要があります。
たとえば、ハンドルは他の操作の前に初期化され、使用されなくなったら閉じる必要があります。
#include <uvw.hpp>#include <memory>void listen(uvw::loop &loop) { std::shared_ptr<uvw::tcp_handle> tcp =loop.resource<uvw::tcp_handle>(); tcp->on<uvw::listen_event>([](const uvw::listen_event &, uvw::tcp_handle &srv) { std::shared_ptr<uvw::tcp_handle> client = srv.parent().resource<uvw::tcp_handle>(); client->on<uvw::close_event>([ptr = srv.shared_from_this()](const uvw::close_event &, uvw::tcp_handle &) { ptr->close(); }); client->on<uvw::end_event>([](const uvw::end_event &, uvw::tcp_handle &client) { client.close(); }); srv.accept(*クライアント); client->read(); }); tcp->bind("127.0.0.1", 4242); tcp->listen(); }void conn(uvw::loop &loop) {auto tcp =loop.resource<uvw::tcp_handle>(); tcp->on<uvw::error_event>([](const uvw::error_event &, uvw::tcp_handle &) { /* エラーを処理します */ }); tcp->on<uvw::connect_event>([](const uvw::connect_event &, uvw::tcp_handle &tcp) {auto dataWrite = std::unique_ptr<char[]>(new char[2]{ 'b' , 'c' }); tcp.write(std::move(dataWrite), 2); tcp.close(); }); tcp->connect(std::string{"127.0.0.1"}, 4242); }int main() {自動ループ = uvw::loop::get_default();listen(*loop);conn(*loop); ループ->実行(); }
uvw
が作成された主な理由は、C++ には有効なlibuv
ラッパーが存在しないという事実です。それだけです。
uvw
使用できるようにするには、ユーザーは次のシステム全体のツールを提供する必要があります。
少なくとも C++17 をサポートするフル機能のコンパイラー。
libuv
(どのバージョンは使用中のuvw
のタグによって異なります)
meson
使用する場合、libuv がダウンロードされます
テストをコンパイルし、ドキュメントを抽出するには、以下の要件が必須です。
CMake バージョン 3.13 以降。
Doxygen バージョン 1.8 以降。
libuv
プロジェクトの依存関係の一部であり、場合によってはCMake
によって複製される可能性があることに注意してください (詳細については以下を参照)。
そのため、ユーザーはテストを実行するとき、またはuvw
ライブラリがCMake
を通じてコンパイルされるときにインストールする必要がありません。
uvw をプロジェクトのsubprojects
ディレクトリに追加するだけで、meson でuvw
使用できます。
サブプロジェクトとして使用せずにソースからuvw
コンパイルするには、 uvw
ソース ディレクトリで次のコマンドを実行します。
$ meson setup build
静的ライブラリが必要な場合は、 --default-library=static
を追加します
$ cd build
$ meson compile
uvw
デュアルモード ライブラリです。ヘッダーのみの形式で使用することも、コンパイルされた静的ライブラリとして使用することもできます。
次のセクションでは、独自のプロジェクトでuvw
起動して実行するために、両方のケースで何を行うかを説明します。
uvw
ヘッダーのみのライブラリとして使用するには、 uvw.hpp
ヘッダーまたは他のuvw/*.hpp
ファイルのいずれかをインクルードするだけです。
ファイルの先頭に次の行を追加するだけです。
#include <uvw.hpp>
次に、適切な-I
引数をコンパイラに渡して、 src
ディレクトリをインクルード パスに追加します。
この場合、ユーザーはlibuv
のインクルード ディレクトリとライブラリ検索パスを正しく設定する必要があることに注意してください。
CMake
を通じて使用すると、便宜上uvw::uvw
ターゲットがエクスポートされます。
uvw
コンパイル済みライブラリとして使用するには、プロジェクトを含める前に cmake でUVW_BUILD_LIBS
オプションを設定します。
このオプションは、 uvw::uvw-static
という名前のターゲットの生成をトリガーします。対応するバージョンのlibuv
もコンパイルされ、便宜上uv::uv-static
としてエクスポートされます。
CMake
使用しない場合、または使用したくない場合でも、すべての.cpp
ファイルをコンパイルし、すべての.h
ファイルをインクルードして作業を完了することができます。この場合、ユーザーはlibuv
のインクルード ディレクトリとライブラリ検索パスを正しく設定する必要があります。
libuv
のタグv1.12.0以降、 uvw
セマンティック バージョン管理スキームに従います。
問題は、 uvw
のどのバージョンでも、バインドされているlibuv
のバージョンを明示的に追跡する必要があることです。
そのため、後者はuvw
のバージョンに追加されます。例として:
vU.V.W_libuv-vX.Y
特に、以下が適用されます。
UVW は、 uvw
のメジャー、マイナー、パッチ バージョンです。
XY は、参照するlibuv
のバージョンです (任意のパッチ バージョンが有効です)。
つまり、今後のタグは次のようになります。
v1.0.0_libuv-v1.12
uvw
のブランチmaster
、 libuv
のブランチv1.xに続く作業中のブランチになります (少なくともマスターブランチである限り)。
このドキュメントはdoxygen
に基づいています。構築するには:
$ cd build
$ cmake ..
$ make docs
API リファレンスはbuild/docs/html
ディレクトリ内に HTML 形式で作成されます。
お気に入りのブラウザで操作するには:
$ cd build
$ your_favorite_browser docs/html/index.html
同じバージョンは、最新リリース、つまり最後の安定したタグについてもオンラインで入手できます。
このドキュメントは主に、明らかな理由から公式 libuv API ドキュメントからインスピレーションを得ています。
テストをコンパイルして実行するには、 uvw
libuv
とgoogletest
必要です。
CMake
他のライブラリをコンパイルする前に、両方のライブラリをダウンロードしてコンパイルします。
テストを構築するには:
$ cd build
$ cmake .. -DUVW_BUILD_TESTING=ON
$ make
$ ctest -j4 -R uvw
libuv
およびその他の依存関係もテストする場合は、 -R uvw
省略します。
uvw
使用する場合のルールは 1 つだけです。常にリソースを初期化して終了します。
リソースは主に、ハンドルとリクエストの2 つのファミリーに属します。
ハンドルは、アクティブなときに特定の操作を実行できる長命オブジェクトを表します。
リクエストは、(通常は) ハンドルまたはスタンドアロンで実行される短期間の操作を表します。
次のセクションでは、この種のリソースの初期化と終了が何を意味するかを簡単に説明します。
詳細については、オンラインドキュメントを参照してください。
通常、初期化は内部で実行され、 loop::resource
メンバー関数を使用してハンドルが作成されている限り、初期化を渡すこともできます。
一方、ハンドルは、明示的に閉じるまでハンドル自体を存続させます。そのため、ユーザーがハンドルを忘れただけでメモリ使用量が増加します。
したがって、ルールはすぐに常にハンドルを閉じることになります。それは、それらに対してclose
メンバー関数を呼び出すのと同じくらい簡単です。
通常、リクエスト オブジェクトの初期化は必要ありません。いずれにしても、リクエストを作成するための推奨される方法は、やはり、 loop::resource
メンバー関数を使用することです。
リクエストは、未完了の基礎となるアクティビティにバインドされている限り、存続し続けます。これは、ユーザーがリクエストを明示的に破棄する必要がないことを意味します。
したがって、ルールはすぐに気軽にリクエストを作成し、それを忘れることになります。メンバー関数を呼び出すだけで簡単です。
uvw
使用するために最初に行うことは、ループを作成することです。デフォルトのもので十分な場合は、次のようにするのが簡単です。
自動ループ = uvw::loop::get_default();
ユーザーがそうしたい場合に備えて、ループ オブジェクトがclose
メンバー関数を提供する場合でも、ループ オブジェクトを明示的に閉じる必要はないことに注意してください。
ループは、 run
メンバー関数を使用して開始できます。以下の 2 つの呼び出しは同等です。
ループ->実行(); ループ->実行(uvw::loop::run_mode::DEFAULT);
使用可能なモードは次のとおりです: DEFAULT
、 ONCE
、 NOWAIT
。詳細については、 libuv
のドキュメントを参照してください。
リソースを作成し、それを指定されたループにバインドするには、次の手順を実行します。
auto tcp = ループ->リソース<uvw::tcp_handle>();
上記の行は、TCP ハンドルを作成して初期化し、そのリソースへの共有ポインタを返します。
ユーザーはポインタが正しく初期化されているかどうかを確認する必要があります。エラーが発生した場合は初期化されません。
また、初期化されていないリソースを作成して、次のように後で初期化することもできます。
auto tcp = ループ->uninitialized_resource<uvw::tcp_handle>(); tcp->init();
すべてのリソースは、いかなる場合も触れられることのない任意のユーザー データも受け入れます。
ユーザーは、次のようにdata
メンバー関数を使用してそれらを設定および取得できます。
resource->data(std::make_shared<int>(42)); std::shared_ptr<void> データ = resource->data();
リソースはstd::shared_pointer<void>
期待し、それを返すため、あらゆる種類のデータが歓迎されます。
ユーザーは、 data
メンバー関数を呼び出すときにvoid
以外の型を明示的に指定できます。
std::shared_ptr<int> データ = resource->data<int>();
前のセクションで述べたように、ハンドルは、そのハンドルに対してclose
メンバー関数が呼び出されるまで、ハンドル自体を存続し続けます。
まだ生きていて特定のループにバインドされているハンドルが何であるかを知るために、 walk
メンバー関数が存在します。ハンドルとその型を返します。したがって、すべての種類の関心をインターセプトできるように、 overloaded
の使用をお勧めします。
handle.parent().walk(uvw::overloaded{ [](uvw::timer_handle &h){ /* ここにタイマーのアプリケーション コード */ }, [](auto &&){ /* 他のタイプはすべて無視します */ } });
この関数は、完全に汎用的なアプローチにも使用できます。たとえば、次のようにして、すべての保留中のハンドルを簡単に閉じることができます。
loop->walk([](auto &&h){ h.close(); });
それらを追跡する必要はありません。
uvw
リソースがリスナーがアタッチされる小さなイベント エミッターであるイベント ベースのアプローチを提供します。
リスナーをリソースにアタッチすることは、リソースの操作に関する通知を受信するための推奨される方法です。
リスナーはvoid(event_type &, resource_type &)
型の呼び出し可能なオブジェクトです。ここで、:
event_type
設計されたイベントのタイプです。
resource_type
イベントを発生させたリソースのタイプです。
これは、次の関数タイプがすべて有効であることを意味します。
void(event_type &, resource_type &)
void(const event_type &, resource_type &)
void(event_type &, const resource_type &)
void(const event_type &, const resource_type &)
イベントが発行されるたびにリソース自体が引数として渡されるため、リソースへの参照を保持する必要がないことに注意してください。
on
メンバー関数は、長時間実行されるリスナーを登録するための方法です。
resource.on<event_type>(リスナー)
特定の型にリスナーが存在するかどうかを確認するために、クラスはhas
関数テンプレートを提供します。同様に、 reset
関数テンプレートは、リスナーがあればリセットして切断するために使用されます。エミッタ全体をクリアする非テンプレート バージョンのreset
も存在します。
ほとんどすべてのリソースは、エラーが発生した場合にerror_event
を発行します。
他のすべてのイベントは特定のリソースに固有であり、API リファレンスに文書化されています。
以下のコードは、 uvw
を使用して単純な TCP サーバーを作成する方法を示しています。
自動ループ = uvw::loop::get_default();auto tcp = ループ->リソース<uvw::tcp_handle>(); tcp->on<uvw::error_event>([](const uvw::error_event &, uvw::tcp_handle &) { /* 何か問題が発生しました */ }); tcp->on<uvw::listen_event>([](const uvw::listen_event &, uvw::tcp_handle &srv) { std::shared_ptr<uvw::tcp_handle> client = srv.parent().resource<uvw::tcp_handle>(); client->on<uvw::end_event>([](const uvw::end_event &, uvw::tcp_handle &client) { client.close(); }); client->on<uvw::data_event>([](const uvw::data_event &, uvw::tcp_handle &) { /* データを受信しました */ }); srv.accept(*クライアント); client->read(); }); tcp->bind("127.0.0.1", 4242); tcp->listen();
uvw::tcp_handle
すでにIPv6 をそのままサポートしていることにも注意してください。
API リファレンスは、リソースとそのメソッドの詳細について推奨されるドキュメントです。
ユーザーがまだuvw
でラップされていない機能を使用する必要がある場合、または他の理由でlibuv
で定義されている基礎となるデータ構造を取得したい場合、 uvw
のほぼすべてのクラスがそれらに直接アクセスできます。
ユーザーが何をしているのか、どのようなリスクがあるのかを正確に理解していない限り、この機能を直接使用しないでください。 raw にすることは危険です。主な理由は、ループ、ハンドル、またはリクエストの有効期間管理がライブラリによって完全に制御されており、これを回避するとすぐに問題が発生する可能性があるためです。
そうは言っても、 raw にするということは、 raw
メンバー関数を使用するということです。
自動ループ = uvw::loop::get_default();auto tcp = ループ->リソース<uvw::tcp_handle>();uv_loop_t *raw = ループ->raw();uv_tcp_t *ハンドル = tcp->raw() ;
自己責任で生の方法を使用してください。ただし、バグが発生した場合のサポートは期待できません。
uvw
に基づいて構築される追加のツールやライブラリに興味がありますか?その場合、次のことが役立つかもしれません。
uvw_net
: クライアント (HTTP/Modbus/SunSpec) のコレクションを備えたネットワーク ライブラリ。dns-sd/mdns などの検出実装も含まれます。
必要に応じて、ツールをリストに自由に追加してください。
貢献したい場合は、ブランチ マスターに対するプル リクエストとしてパッチを送信してください。
貢献者リストをチェックして、これまでに誰が参加したかを確認してください。
コードとドキュメントの著作権 (c) 2016-2024 Michele Caini。
ロゴの著作権 (c) 2018-2021 Richard Caseres。
コードとドキュメントは MIT ライセンスに基づいてリリースされています。
ロゴは CC BY-SA 4.0 に基づいてリリースされました。
このプロジェクトをサポートしたい場合は、私にエスプレッソを提供してください。
それが十分ではないと思われる場合は、お好みの方法で私を助けてください。