您是否希望簡化C ++中多態對象的終身管理和維護?
您是否想像Java或C#這樣的GC語言一樣輕鬆地以C ++編寫多態代碼,而不會犧牲性能?
您是否嘗試過C ++中的其他多態編程庫,但發現它們不足?
如果是這樣,這個庫適合您。
“代理”是一個現代的C ++庫,可幫助您無需繼承而使用多態性(一種互換使用不同類型的對象的方式)。
“代理”是由Microsoft工程師創建的,自2022年以來一直在Windows操作系統中使用。多年來,使用繼承一直是C ++中多態性的主要方法。但是,像Rust這樣的新編程語言提供了更好的方法。我們已經提高了對面向對象的編程的理解,並決定使用C ++中的指針作為“代理”的基礎。具體而言,“代理”庫的設計為:
請參閱代理的常見問題以獲取更多背景,並參考規格以獲取更多技術細節。
“代理”是僅標題的C ++ 20庫。要使用庫,請確保您的編譯器滿足最低要求,並在源代碼中包括標頭文件代理。另外,您可以通過搜索“代理”來通過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> :for std::cout 。
#include <string> :for std::string 。
#include "proxy.h" :用於“代理”庫。庫的大多數設施都在命名空間pro中定義。如果庫是通過VCPKG或Conan消費的,則應將此行更改為#include <proxy/proxy.h> 。
struct Streamable : pro::facade_builder ... ::build {} :定義一個外麵類型Streamable 。正式定義為褻瀆要求的“立面”一詞是“代理”庫模型運行時抽象的方式。具體來說,
pro::facade_builder :提供在編譯時構建外牆類型的能力。add_convention :在構建上下文中添加了一個由“調度”和幾個“過載”定義的廣義“調用約定”。pro::operator_dispatch <"<<", true> :指定操作員的調度<< extrendies <<主操作數( proxy )在右側(由第二個模板參數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的所有權。如果str的壽命在p1之前結束,則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具有基礎int的所有權,因為它是從std::unique_ptr的值實例化的,並且當p2被銷毀時,它將稱為std::unique_ptr的distructor,而p1沒有基礎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> :for std::cout 。#include <sstream> :對於std::stringstream 。#include "proxy.h" :用於“代理”庫。PRO_DEF_MEM_DISPATCH (MemDraw, Draw) :定義調用類型MemDraw用於呼叫成員函數Draw表達式。PRO_DEF_MEM_DISPATCH (MemArea, Area) :定義調用類型的MemArea ,用於呼叫成員功能Area的表達式。struct Drawable : pro::facade_builder ... ::build {} :定義Drawable外觀類型。具體來說,add_convention :在構建上下文中添加呼叫約定。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比上面所示的功能更強大。它可以採用任何數量的過載類型(正式地,符合ProVerload要求的任何類型),並在調用proxy時執行標準的超負荷分辨率。facade_builder::add_facade可以靈活地組成不同的抽象。PRO_DEF_WEAK_DISPATCH定義“弱派遣”。allocate_proxy能夠使用任何自定義分配器的值創建proxy 。在C ++ 11中, std::function和std::packaged_task具有構造函數,可以接受自定義分配器進行性能調整,但是在C ++ 17中刪除了這些構造器,因為“語義是不清楚的,並且在類型的上下文中存儲分配器存在技術問題,然後將其分配給該分配的任何分配,以便在復制分配過程中恢復任何分配”。這些問題不適用於allocate_proxy 。facade_builder為約束配置提供了全面支持,包括內存佈局(通過restrict_layout ),可複制性(通過support_copy ),可重新定位性(通過support_relocation )和可破壞性(通過support_destruction )。proxy支持基於類型的運行時查詢的基於類型的編譯時間反射。有關更多詳細信息,請參閱facade_builder::add_reflection和功能模板proxy_reflect 。 | 家庭 | 最低版本 | 必需的標誌 |
|---|---|---|
| 海灣合作委員會 | 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在所有存儲庫中進行一次。
該項目採用了Microsoft開源的行為代碼。有關更多信息,請參見《行為守則常見問題守則》或與其他問題或評論聯繫[email protected]。
該項目可能包含用於項目,產品或服務的商標或徽標。 Microsoft商標或徽標的授權使用受到了Microsoft的商標和品牌準則的約束。在此項目的修改版本中使用Microsoft商標或徽標不得引起混亂或暗示Microsoft贊助。任何使用第三方商標或徽標都遵守這些第三方政策。