C ++の多型オブジェクトの生涯管理とメンテナンスを簡素化しようとしていますか?
パフォーマンスを犠牲にすることなく、JavaやC#などのGC言語と同じくらい簡単にC ++で多型コードを書きたいですか?
C ++で他の多型プログラミングライブラリを試したことがありますか?
もしそうなら、このライブラリはあなたのためです。
「Proxy」は、継承を必要とせずに多型(異なるタイプのオブジェクトを交換可能に使用する方法)を使用するのに役立つ最新のC ++ライブラリです。
「プロキシ」はMicrosoft Engineersによって作成され、2022年からWindowsオペレーティングシステムで使用されています。長年にわたり、C ++の多型を達成するための主な方法が継承を使用しました。ただし、Rustのような新しいプログラミング言語は、これを行うためのより良い方法を提供します。オブジェクト指向プログラミングの理解を改善し、C ++のポインターを「プロキシ」の基礎として使用することにしました。具体的には、「プロキシ」ライブラリは次のように設計されています。
より多くの背景については、プロキシのよくある質問を参照してください。詳細については、仕様を参照してください。
「プロキシ」は、ヘッダーのみのC ++ 20ライブラリです。ライブラリを使用するには、コンパイラが最小要件を満たしていることを確認し、ソースコードにヘッダーファイルProxy.hを含めてください。または、「プロキシ」を検索して、VCPKGまたはCONANを介してライブラリをインストールできます(vcpkg.ioとconan.ioを参照)。
次の「Hello World」の例を始めましょう。
# include < iostream >
# include < string >
# include " proxy.h "
struct Streamable : pro::facade_builder
::add_convention<pro::operator_dispatch< " << " , true >, std::ostream&(std::ostream& out) const >
::build {};
int main () {
std::string str = " Hello World " ;
pro::proxy<Streamable> p1 = &str;
std::cout << " p1 = " << *p1 << " n " ; // Prints: "p1 = Hello World"
pro::proxy<Streamable> p2 = std::make_unique< int >( 123 );
std::cout << " p2 = " << *p2 << " n " ; // Prints: "p2 = 123"
pro::proxy<Streamable> p3 = pro::make_proxy<Streamable>( 3.14 );
std::cout << " p3 = " << *p3 << " n " ; // Prints: "p3 = 3.14"
}これが段階的な説明です:
#include <iostream> : std::cout場合。
#include <string> : std::string場合。
#include "proxy.h" :「プロキシ」ライブラリ用。図書館の施設のほとんどは、Namespace proで定義されています。ライブラリがVCPKGまたはCONANを介して消費される場合、この行は#include <proxy/proxy.h>に変更する必要があります。
struct Streamable : pro::facade_builder ... ::build {} :Facade Type Streamableを定義します。 「ファサード」という用語は、正式にProfacade要件として定義されていますが、「プロキシ」ライブラリモデルのランタイム抽象化がどのようにモデルであるかです。具体的には、
pro::facade_builder :コンパイル時にファサードタイプを構築する機能を提供します。add_convention :「ディスパッチ」といくつかの「オーバーロード」によって定義された一般化された「呼び出し慣習」をビルドコンテキストに追加します。pro::operator_dispatch <"<<", true> :プライマリオペランド( proxy )が右側にあるoperator <<式のディスパッチを指定します(2番目のテンプレートパラメーターtrueで指定)。 「プロキシ」ライブラリの多型は、C ++仮想関数や他のOOP言語とは異なるメンバー関数ではなく、式で定義されていることに注意してください。std::ostream&(std::ostream& out) const : std::move_only_functionと同様の呼び出し条約の署名。 const一次オペランドがconstであることを指定します。build :コンテキストをファサードタイプにビルドします。 pro::proxy <Streamable> p1 = &str : std::stringの生のポインターからproxyオブジェクトを作成します。 p1生のポインターのように動作し、基礎となるstd::stringの所有権はありません。 p1前にstrの寿命が終了する場合、 p1はぶら下がっています。
std::cout << *p1 :これが機能する方法です。 「Hello World」が印刷されます。なぜなら、通話規則はファサードStreamableで定義されているため、 std::cout << strを呼び出すように機能します。
pro::proxy <Streamable> p2 = std::make_unique <int>(123) : std::unique_ptr <int>を作成し、 proxyに変換します。 p1とは異なり、 p2 std::unique_ptrの値からインスタンス化され、 p2が破壊されたときにstd::unique_ptrのデストラクタを呼び出しますが、 p1生のintから瞬時に瞬時にあるため、基礎となるintの所有権を持っていません。 p1とp2は同じタイプのpro::proxy<Streamable>です。つまり、実装の詳細に関する情報を発信者に公開することなくpro::proxy<Streamable>を返す関数を持つことができます。
std::cout << *p2 :驚くことなく「123」を印刷します。
pro::proxy <Streamable> p3 = pro::make_proxy <Streamable>(3.14) :基礎となるポインタータイプを指定せずにdoubleからproxyを作成します。具体的には、
p2と同様に、 p3基礎となるdouble価値の所有権もありますが、ヒープの割り当てを効果的に回避できます。double )のサイズは小さいことが知られているため(メジャー32または64ビットプラットフォームで)、 pro::make_proxyコンパイル時に事実を認識し、 pro::make_proxy_inplaceに戻ります。std::functionに陥ることを避けないかを明示的に定義します。 std::cout << *p3 :驚くことなく「3.14」を印刷します。
main戻ると、 p2とp3基礎となるオブジェクトを破壊しますが、 p1 、基礎となるstd::stringの所有権を持たない生のポインターを保持するため、何もしません。
前の例で実証された演算子の式に加えて、ライブラリはC ++のほぼすべての形式の表現をサポートし、それらを多型にすることができます。具体的には、
PRO_DEF_MEM_DISPATCH :メンバー関数通話式のディスパッチタイプを定義します。PRO_DEF_FREE_DISPATCH :無料の機能通話式のディスパッチタイプを定義します。pro::operator_dispatch :オペレーター式のディスパッチタイプ。pro::conversion_dispatch :変換式のためのディスパッチタイプ。現在のC ++テンプレートは任意の名前で関数の生成をサポートしていないため、一部の施設はマクロとして提供されていることに注意してください。これは、メンバー関数を呼び出して式を多型にする別の例です。
# include < iostream >
# include < sstream >
# include " proxy.h "
PRO_DEF_MEM_DISPATCH (MemDraw, Draw);
PRO_DEF_MEM_DISPATCH (MemArea, Area);
struct Drawable : pro::facade_builder
::add_convention<MemDraw, void (std::ostream& output)>
::add_convention<MemArea, double () noexcept >
::support_copy<pro::constraint_level::nontrivial>
::build {};
class Rectangle {
public:
Rectangle ( double width, double height) : width_(width), height_(height) {}
Rectangle ( const Rectangle&) = default ;
void Draw (std::ostream& out) const {
out << " {Rectangle: width = " << width_ << " , height = " << height_ << " } " ;
}
double Area () const noexcept { return width_ * height_; }
private:
double width_;
double height_;
};
std::string PrintDrawableToString (pro::proxy<Drawable> p) {
std::stringstream result;
result << " entity = " ;
p-> Draw (result);
result << " , area = " << p-> Area ();
return std::move (result). str ();
}
int main () {
pro::proxy<Drawable> p = pro::make_proxy<Drawable, Rectangle>( 3 , 5 );
std::string str = PrintDrawableToString (p);
std::cout << str << " n " ; // Prints: "entity = {Rectangle: width = 3, height = 5}, area = 15"
}これが段階的な説明です:
#include <iostream> : std::cout場合。#include <sstream> : std::stringstreamの場合。#include "proxy.h" :「プロキシ」ライブラリ用。PRO_DEF_MEM_DISPATCH (MemDraw, Draw) :呼び出しメンバー関数描画の表現のためのディスパッチタイプMemDraw Draw定義します。PRO_DEF_MEM_DISPATCH (MemArea, Area) :呼び出しメンバー関数Areaの表現のディスパッチタイプのMemAreaを定義します。struct Drawable : pro::facade_builder ... ::build {} : Drawableファサードタイプを定義します。具体的には、add_convention :build Contementionsをビルドコンテキストに追加します。support_copy < pro::constraint_level ::nontrivial> :基礎となるポインタータイプがコピー可能であると指定し、結果のproxyタイプをコピー可能にします。class Rectangle : Drawableの実装。PrintDrawableToString : Drawableをstd::stringに変換します。これは関数テンプレートではなく関数であることに注意してください。つまり、より大きなビルドシステムでABIを生成できることを意味します。pro::proxy<Drawable> p = pro::make_proxy<Drawable, Rectangle>(3, 5) : Rectangleを含むproxy<Drawable>オブジェクトを作成します。std::string str = PrintDrawableToString(p) : pをstd::stringに変換し、暗黙的にpのコピーを作成します。std::cout << str :文字列を印刷します。「プロキシ」ライブラリは、C ++のランタイム多型のための自己完結型ソリューションです。仕様には文書化されている他の多くの機能があります。上記の機能に加えて、ユーザーのフィードバックに基づいた最も人気のある機能のキュレーションリストを次に示します。
facade_builder::add_convention 、上記よりも強力です。任意の数のオーバーロードタイプ(正式には、任意のタイプを任意のタイプに満たす任意のタイプ)を使用し、 proxyを呼び出すときに標準の過負荷解像度を実行できます。facade_builder::add_facadeさまざまな抽象化の柔軟な構成を可能にします。PRO_DEF_WEAK_DISPATCHを使用して「弱いディスパッチ」を定義することができます。allocate_proxy 、任意のカスタムアロケーターを使用して値からproxyを作成できます。 C ++ 11では、 std::function and std::packaged_taskには、パフォーマンスチューニングのためにカスタムアロケーターを受け入れるコンストラクターがありましたが、これらは「セマンティクスが不明であり、型型のコンテキストでアロケーターを保存し、その後、コピーの割り当て中に必要なアロケーターを回復するための技術的な問題があるため、これらは削除されました。これらの問題は、 allocate_proxyには適用されません。facade_builder 、メモリレイアウト( restrict_layout )、コピー可能性( support_copyによる)、再配置可能性( support_relocation )、およびDestructibility( support_destructionによる)を含む制約構成を完全にサポートします。proxy 、ランタイムクエリのタイプベースのコンパイル時間リフレクションをサポートします。詳細については、 facade_builder::add_reflection and function template proxy_reflectを参照してください。 | 家族 | 最小バージョン | 必要なフラグ |
|---|---|---|
| GCC | 13.1 | -std = c ++ 20 |
| クラン | 15.0.0 | -std = c ++ 20 |
| MSVC | 19.31 | /STD:C ++ 20 |
| Nvidia HPC | 24.1 | -std = c ++ 20 |
git clone https://github.com/microsoft/proxy.git
cd proxy
cmake -B build
cmake --build build -j
ctest --test-dir build -j
このプロジェクトは、貢献と提案を歓迎します。ほとんどの貢献では、貢献者ライセンス契約(CLA)に同意する必要があります。詳細については、https://cla.opensource.microsoft.comをご覧ください。
プルリクエストを送信すると、CLAボットはCLAを提供し、PRを適切に飾る必要があるかどうかを自動的に決定します(たとえば、ステータスチェック、コメント)。ボットが提供する指示に従うだけです。 CLAを使用して、すべてのレポでこれを1回だけ行う必要があります。
このプロジェクトは、Microsoftのオープンソース行動規範を採用しています。詳細については、FAQのコードを参照するか、追加の質問やコメントについては[email protected]にお問い合わせください。
このプロジェクトには、プロジェクト、製品、またはサービスの商標またはロゴが含まれる場合があります。 Microsoftの商標またはロゴの承認された使用は、Microsoftの商標およびブランドガイドラインに従うものであり、従わなければなりません。このプロジェクトの変更されたバージョンでのMicrosoft商標またはロゴの使用は、混乱を引き起こしたり、Microsoftのスポンサーシップを暗示したりしてはなりません。サードパーティの商標またはロゴの使用は、これらのサードパーティのポリシーの対象となります。