Вы хотите упростить управление в течение всего времени и поддержание полиморфных объектов в C ++?
Вы хотите написать полиморфический код в C ++ так же легко, как на языках GC, таких как Java или C#, не жертвуя производительностью?
Вы пробовали другие библиотеки полиморфного программирования в C ++, но обнаружили, что они недостаточны?
Если это так, эта библиотека для вас.
«Прокси» - это современная библиотека C ++, которая помогает вам использовать полиморфизм (способ использовать различные типы объектов взаимозаменяемо) без необходимости наследования.
«Прокси» был создан инженерами Microsoft и использовался в операционной системе Windows с 2022 года. В течение многих лет использование наследования было основным способом достижения полиморфизма в C ++. Тем не менее, новые языки программирования, такие как Rust, предлагают лучшие способы сделать это. Мы улучшили наше понимание объектно-ориентированного программирования и решили использовать указатели в C ++ в качестве основы для «прокси». В частности, библиотека «прокси» предназначена для:
Пожалуйста, обратитесь к часто задаваемым вопросам прокси для получения дополнительной информации и обратитесь к спецификациям для получения дополнительной технической информации.
«Прокси»-это библиотека C ++ 20 только для заголовка. Чтобы использовать библиотеку, убедитесь, что ваш компилятор соответствует минимальным требованиям и просто включите в свой исходный код простой код. В качестве альтернативы, вы можете установить библиотеку через VCPKG или CONAN, поиск «Proxy» (см. 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" : для библиотеки "прокси". Большинство объектов библиотеки определены в именах pro . Если библиотека потребляется через VCPKG или CONAN, эта строка должна быть изменена на #include <proxy/proxy.h> .
struct Streamable : pro::facade_builder ... ::build {} : определяет Streamable типа фасада. Термин «фасад», официально определяемый как требования Profacade , является то, как абстракция времени выполнения библиотеки «прокси». Конкретно,
pro::facade_builder : предоставляет возможность построить тип фасада во время компиляции.add_convention : добавляет обобщенное «Конвенция о вызове», определяемое «диспетчерской» и несколькими «перегрузками», в контекст сборки.pro::operator_dispatch <"<<", true> : указывает отправку для оператора << выражения, где основной операнд ( proxy ) находится с правой стороны (указано вторым параметром шаблона true ). Обратите внимание, что полиморфизм в библиотеке «прокси» определяется выражениями, а не функциями -членами, что отличается от виртуальных функций C ++ или других языков ООП.std::ostream&(std::ostream& out) const : подпись призванной конвенции, похожая на std::move_only_function . const указывает, что основным операндом является const .build : строит контекст в тип фасада. pro::proxy <Streamable> p1 = &str : создает proxy -объект из необработанного указателя std::string . p1 ведет себя как необработанный указатель и не имеет права собственности на основную std::string . Если срок службы str заканчивается до p1 , p1 становится свисающим.
std::cout << *p1 : так это работает. Он печатает «Привет, мир», потому что вызовая конвенция определена в Streamable , так что он работает так, как будто позвонив std::cout << str .
pro::proxy <Streamable> p2 = std::make_unique <int>(123) : создает std::unique_ptr <int> и преобразует в proxy . В отличие от p1 , p2 имеет право собственности на базовый int , потому что он создается из значения std::unique_ptr , и будет называть деструктор std::unique_ptr , когда p2 уничтожен, в то время как p1 не владеет основным int потому что он создан из необработанного указателя. p1 и p2 имеют одинаковый тип pro::proxy<Streamable> , что означает, что у вас может быть функция, которая возвращает pro::proxy<Streamable> не разоблачая какую -либо информацию о деталях реализации своему абоненту.
std::cout << *p2 : отпечатки "123" Неудивительно.
pro::proxy <Streamable> p3 = pro::make_proxy <Streamable>(3.14) : создает proxy из double без указания основного типа указателя. Конкретно,
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) Draw определяет MemDraw типа диспетчеры для выражения вызова функции члена.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) : создает proxy<Drawable> , содержащий Rectangle .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 и std::packaged_task были конструкторы, которые принимали пользовательские выделители для настройки производительности, но они были удалены в C ++ 17, потому что «семантика неясна, и существуют технические проблемы с хранением ассигноваторов в контексте типов, а затем восстановление этого выделения позже для любых ассигнований, необходимых во время уступки копирования». Эти проблемы не относятся к allocate_proxy .facade_builder обеспечивает полную поддержку конфигурации ограничений, включая макет памяти (BY restrict_layout ), копируемость (By support_copy ), перемещаемость (By support_relocation ) и разрушительность (By support_destruction ).proxy поддерживает отражение времени компиляции на основе типов для запросов времени выполнения. Пожалуйста, обратитесь к facade_builder::add_reflection и FUNCTION 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.
Этот проект принял код поведения с открытым исходным кодом Microsoft. Для получения дополнительной информации см. Кодекс поведения FAQ или свяжитесь с [email protected] с любыми дополнительными вопросами или комментариями.
Этот проект может содержать товарные знаки или логотипы для проектов, продуктов или услуг. Уполномоченное использование товарных знаков или логотипов Microsoft подлежит и должно следовать указаниям Microsoft по товарной марке и брендам. Использование товарных знаков Microsoft или логотипов в модифицированных версиях этого проекта не должно вызывать путаницу или подразумевать спонсорство Microsoft. Любое использование сторонних товарных знаков или логотипов подвержена политике сторонних сторон.