問題を開く必要のない質問はありますか? 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 に基づいてリリースされました。
このプロジェクトをサポートしたい場合は、私にエスプレッソを提供してください。
それが十分ではないと思われる場合は、お好みの方法で私を助けてください。