Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Proxy: Next Generation Polymorphism in C++

License

NotificationsYou must be signed in to change notification settings

microsoft/proxy

Repository files navigation

Proxy-CI

Are you looking to simplify the lifetime management and maintenance of polymorphic objects in C++?

Do you want to write polymorphic code in C++ as easily as inGC languages like Java or C#, without sacrificing performance?

Have you tried other polymorphic programming libraries in C++ but found them deficient?

If so, this library is for you.

Pure Virtual C++ 2025 - Proxy - YouTube

Our Mission

"Proxy" is a modern C++ library that helps you use polymorphism (a way to use different types of objects interchangeably) without needing inheritance.

"Proxy" was created by Microsoft engineers and has been used in the Windows operating system since 2022. For many years, using inheritance was the main way to achieve polymorphism in C++. However, new programming languages likeRust offer better ways to do this. We have improved our understanding of object-oriented programming and decided to usepointers in C++ as the foundation for "Proxy". Specifically, the "Proxy" library is designed to be:

  • Portable: "Proxy" was implemented as a header-only library in standard C++20. It can be used on any platform while the compiler supports C++20. The majority of the library isfreestanding, making it feasible for embedded engineering or kernel design of an operating system.
  • Non-intrusive: An implementation type is no longer required to inherit from an abstract binding.
  • Well-managed: "Proxy" provides a GC-like capability that manages the lifetimes of different objects efficiently without the need for an actual garbage collector.
  • Fast: With typical compiler optimizations, "Proxy" produces high-quality code that is as good as or better than hand-written code. In many cases, "Proxy" performs better than traditional inheritance-based approaches, especially in managing the lifetimes of objects.
  • Accessible: Learned from user feedback, accessibility has been significantly improved with intuitive syntax, good IDE compatibility, and accurate diagnostics.
  • Flexible: Not only member functions, the "abstraction" of "Proxy" allowsany expression to be polymorphic, including free functions, operators, conversions, etc. Different abstractions can be freely composed on demand. Performance tuning is supported for experts to balance between extensibility and performance.

Please refer to theProxy's Frequently Asked Questions for more background, and refer to thespecifications for more technical details.

Quick Start

"Proxy" is a header-only C++20 library. To use the library, make sure your compiler meets theminimum requirements and just put theproxy directory in your project's include directory. Alternatively, you can install the library via:

Hello World

Let's get started with the following "Hello World" example (run):

#include<format>#include<iostream>#include<string>#include<proxy/proxy.h>structFormattable : pro::facade_builder    ::add_skill<pro::skills::format>    ::build {};intmain() {static std::string str ="Hello World";  pro::proxy<Formattable> p1 = &str;  std::cout <<std::format("*p1 = {}\n", *p1);// Prints "*p1 = Hello World"  pro::proxy<Formattable> p2 = std::make_unique<int>(123);  std::cout <<std::format("*p2 = {}\n", *p2);// Prints "*p2 = 123"  pro::proxy<Formattable> p3 = pro::make_proxy<Formattable>(3.14159);  std::cout <<std::format("*p3 = {:.2f}\n", *p3);// Prints "*p3 = 3.14"}

Here is a step-by-step explanation:

  • #include <format>: Forstd::format.
  • #include <iostream>: Forstd::cout.
  • #include <string>: Forstd::string.
  • #include <proxy/proxy.h>: For the "Proxy" library. Most of the facilities of the library are defined in namespacepro.
  • struct Formattable : pro::facade_builder ... ::build {}: Defines a facade typeFormattable. The term "facade", formally defined as theProFacade requirements, is how the "Proxy" library models runtime abstraction. Specifically,
  • pro::proxy<Formattable> p1 = &str: Creates aproxy object from a raw pointer ofstd::string.p1 behaves like a raw pointer, and does not have ownership of the underlyingstd::string. If the lifetime ofstr ends beforep1,p1 becomes dangling.
  • std::format("*p1 = {}\n", *p1): This is how it works.*p1 is formatted as "Hello World" because the capability was defined in the facadeFormattable, so it works as if by callingstd::format("*p1 = {}\n", str).
  • pro::proxy<Formattable> p2 =std::make_unique<int>(123): Creates astd::unique_ptr<int> and converts to aproxy. Different fromp1,p2 has ownership of the underlyingint because it is instantiated from a value ofstd::unique_ptr, and will call the destructor ofstd::unique_ptr whenp2 is destroyed, whilep1 does not have ownership of the underlyingint because it is instantiated from a raw pointer.p1 andp2 are of the same typepro::proxy<Formattable>, which means you can have a function that returnspro::proxy<Formattable> without exposing any information about the implementation details to its caller.
  • std::format("*p2 = {}\n", *p2): Formats*p2 as "123" with no surprises.
  • pro::proxy<Formattable> p3 =pro::make_proxy<Formattable>(3.14159): Creates aproxy from adouble without specifying the underlying pointer type. Specifically,
    • Similar withp2,p3 also has ownership of the underlyingdouble value, but can effectively avoid heap allocation.
    • Since the size of the underlying type (double) is known to be small (on major 32- or 64-bit platforms),pro::make_proxy realizes the fact at compile-time, and falls back topro::make_proxy_inplace, which guarantees no heap allocation.
    • The "Proxy" library explicitly defines when heap allocation occurs or not to avoid users falling into performance hell, which is different fromstd::function and other existing polymorphic wrappers in the standard.
  • std::format("*p3 = {:.2f}\n", *p3): Formats*p3 as "3.14" as per thestandard format specification with no surprises.
  • Whenmain returns,p2 andp3 will destroy the underlying objects, whilep1 does nothing because it holds a raw pointer that does not have ownership of the underlyingstd::string.

Note: If you prefer the library to be consumed as a (C++20) module, refer toC++20 Modules support.

Hello World (Stream Version)

In the previous "Hello World" example, we demonstrated howproxy could manage different types of objects and be formatted withstd::format. Whilestd::format is not the only option to print objects in C++, can we simply makeproxy work withstd::cout? The answer is "yes". The previous example is equivalent to the following implementation (run):

#include<iomanip>#include<iostream>#include<string>#include<proxy/proxy.h>structStreamable : pro::facade_builder    ::add_convention<pro::operator_dispatch<"<<",true>, std::ostream&(std::ostream& out)const>    ::build {};intmain() {static 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.14159);  std::cout <<"*p3 =" << std::fixed <<std::setprecision(2) << *p3 <<"\n";// Prints "p3 = 3.14"}

Here is a step-by-step explanation:

  • #include <iomanip>: Forstd::setprecision.

  • #include <iostream>: Forstd::cout.

  • #include <string>: Forstd::string.

  • #include <proxy/proxy.h>: For the "Proxy" library.

  • struct Streamable : pro::facade_builder ... ::build {}: Defines a facade typeStreamable. Specifically,

    • pro::facade_builder: Gets prepared to build another facade.
    • add_convention: Adds a generalized "calling convention", defined by a "dispatch" and several "overloads", to the build context.
    • pro::operator_dispatch<"<<", true>: Specifies a dispatch for operator<< expressions where the primary operand (proxy) is on the right-hand side (specified by the second template parametertrue). Note that polymorphism in the "Proxy" library is defined by expressions rather than member functions, which is different from C++ virtual functions or other OOP languages.
    • std::ostream&(std::ostream& out) const: The signature of the calling convention, similar withstd::move_only_function.const specifies that the primary operand isconst.
    • build: Builds the context into a facade type.
  • pro::proxy<Streamable> p1 = &str: Creates aproxy object from a raw pointer ofstd::string.

  • std::cout << *p1: It prints "Hello World" because the calling convention is defined in the facadeStreamable, so it works as if by callingstd::cout << str.

  • pro::proxy<Streamable> p2 =std::make_unique<int>(123): Creates astd::unique_ptr<int> and converts to aproxy.

  • std::cout << *p2: Prints "123" with no surprises.

  • pro::proxy<Streamable> p3 =pro::make_proxy<Streamable>(3.14): Creates aproxy from adouble.

  • std::cout << std::fixed << std::setprecision(2) << *p3;: Prints "3.14" with no surprises.

More Expressions

In addition to the operator expressions demonstrated in the previous examples, the library supports almost all forms of expressions in C++ and can make them polymorphic. Specifically,

Note that some facilities are provided as macro, because C++ templates today do not support generating a function with an arbitrary name. Here is another example that makes member function call expressions polymorphic (run):

#include<iostream>#include<sstream>#include<proxy/proxy.h>PRO_DEF_MEM_DISPATCH(MemDraw, Draw);PRO_DEF_MEM_DISPATCH(MemArea, Area);structDrawable : pro::facade_builder    ::add_convention<MemDraw,void(std::ostream& output)>    ::add_convention<MemArea,double()noexcept>    ::support_copy<pro::constraint_level::nontrivial>    ::build {};classRectangle {public:Rectangle(double width,double height) : width_(width), height_(height) {}Rectangle(const Rectangle&) =default;voidDraw(std::ostream& out)const {    out <<"{Rectangle: width =" << width_ <<", height =" << height_ <<"}";  }doubleArea()constnoexcept {return width_ * height_; }private:double width_;double height_;};std::stringPrintDrawableToString(pro::proxy<Drawable> p) {  std::stringstream result;  result <<"entity =";  p->Draw(result);  result <<", area =" << p->Area();returnstd::move(result).str();}intmain() {  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"}

Here is a step-by-step explanation:

  • #include <iostream>: Forstd::cout.
  • #include <sstream>: Forstd::stringstream.
  • #include <proxy/proxy.h>: For the "Proxy" library.
  • PRO_DEF_MEM_DISPATCH(MemDraw, Draw): Defines a dispatch typeMemDraw for expressions of calling member functionDraw.
  • PRO_DEF_MEM_DISPATCH(MemArea, Area): Defines a dispatch typeMemArea for expressions of calling member functionArea.
  • struct Drawable : pro::facade_builder ... ::build {}: Defines a facade typeDrawable. Specifically,
  • class Rectangle: An implementation ofDrawable.
  • FunctionPrintDrawableToString: Converts aDrawable into astd::string. Note that this is a function rather than a function template, which means it can generateABI in a larger build system.
  • pro::proxy<Drawable> p = pro::make_proxy<Drawable, Rectangle>(3, 5): Creates aproxy<Drawable> object containing aRectangle.
  • std::string str = PrintDrawableToString(p): Convertsp into astd::string, implicitly creates a copy ofp.
  • std::cout << str: Prints the string.

Other Useful Features

The "Proxy" library is a self-contained solution for runtime polymorphism in C++. There are many other capabilities documented in thespecifications. In addition to the features mentioned above, here is a curated list of the most popular features based on user feedback:

FamilyMinimum versionRequired flags
GCC13.1-std=c++20
Clang16.0.0-std=c++20
MSVC19.31/std:c++20
NVIDIA HPC24.1-std=c++20
Intel oneAPI2024.0-std=c++20

Build and Run Tests with CMake

git clone https://github.com/microsoft/proxy.gitcd proxycmake -B buildcmake --build build -jctest --test-dir build -j

Related Resources

Contributing

This project welcomes contributions and suggestions. Most contributions require you to agree to aContributor License Agreement (CLA) declaring that you have the right to, and actually do, grant usthe rights to use your contribution. For details, visithttps://cla.opensource.microsoft.com.

When you submit a pull request, a CLA bot will automatically determine whether you need to providea CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructionsprovided by the bot. You will only need to do this once across all repos using our CLA.

This project has adopted theMicrosoft Open Source Code of Conduct.For more information see theCode of Conduct FAQ orcontactopencode@microsoft.com with any additional questions or comments.

Trademarks

This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsofttrademarks or logos is subject to and must followMicrosoft's Trademark & Brand Guidelines.Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.Any use of third-party trademarks or logos are subject to those third-party's policies.


[8]ページ先頭

©2009-2025 Movatter.jp