C ++에서 다형성 대상의 수명 관리 및 유지 보수를 단순화하려고합니까?
성능을 희생하지 않고 Java 또는 C#과 같은 GC 언어와 같이 C ++로 다형성 코드를 작성하고 싶습니까?
C ++에서 다른 다형성 프로그래밍 라이브러리를 사용해 보았지만 부족한 것을 발견 했습니까?
그렇다면이 도서관은 당신을위한 것입니다.
"프록시"는 상속을 필요로하지 않고 다형성 (서로 다른 유형의 물체를 상호 교환 적으로 사용하는 방법)을 사용하는 데 도움이되는 최신 C ++ 라이브러리입니다.
"프록시"는 Microsoft 엔지니어들에 의해 만들어졌으며 2022 년부터 Wind 그러나 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" : "프록시"라이브러리 용. 도서관의 대부분의 시설은 네임 스페이스 pro 에 정의되어 있습니다. 라이브러리가 vcpkg 또는 conan을 통해 소비되면이 라인은 #include <proxy/proxy.h> 로 변경되어야합니다.
struct Streamable : pro::facade_builder ... ::build {} : Facade Type Streamable 정의합니다. 공식적으로 Profacade 요구 사항으로 정의 된 "Facade"라는 용어는 "프록시"라이브러리가 런타임 추상화를 모델링하는 방법입니다. 구체적으로,
pro::facade_builder : 컴파일 타임에 외관 유형을 구축 할 수있는 기능을 제공합니다.add_convention : 빌드 컨텍스트에 "발송"및 몇 가지 "과부하"로 정의 된 일반화 된 "호출 규칙"을 추가합니다.pro::operator_dispatch <"<<", true> : 1 차 피연산자 ( proxy )가 오른쪽에있는 (두 번째 템플릿 매개 true 에 의해 지정 됨) 운영자에 대한 디스패치 << 표현식을 지정합니다. "프록시"라이브러리의 다형성은 C ++ 가상 함수 또는 기타 OOP 언어와 다른 멤버 함수가 아닌 표현식으로 정의됩니다.std::ostream&(std::ostream& out) const : std::move_only_function 과 유사한 호출 컨벤션의 서명. const 1 차 피연산자가 const 임을 지정합니다.build : 컨텍스트를 외관 유형으로 빌드합니다. pro::proxy <Streamable> p1 = &str : std::string 의 원시 포인터에서 proxy 객체를 만듭니다. p1 원시 포인터처럼 작동하며 기본 std::string 의 소유권이 없습니다. str 의 수명이 p1 전에 끝나면 p1 매달려집니다.
std::cout << *p1 : 이것이 작동하는 방식입니다. 호출 컨벤션은 Facade Streamable 에 정의되기 때문에 "Hello World"를 인쇄하므로 std::cout << str 호출하는 것처럼 작동합니다.
pro::proxy <Streamable> p2 = std::make_unique <int>(123) : std::unique_ptr <int> 생성하고 proxy 로 변환합니다. p1 과는 다른 p2 std::unique_ptr 값에서 인스턴스화되기 때문에 기본 int 의 소유권을 가지고 있으며, p2 파괴 될 때 std::unique_ptr 의 파괴자를 호출 할 것이기 때문에, 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> : std::cout 의 경우.#include <sstream> : std::stringstream 의 경우.#include "proxy.h" : "프록시"라이브러리 용.PRO_DEF_MEM_DISPATCH (MemDraw, Draw) : 호출 멤버 함수 Draw 의 표현식에 대한 디스패치 유형 MemDraw 정의합니다.PRO_DEF_MEM_DISPATCH (MemArea, Area) : 호출 멤버 기능 Area 의 표현에 대한 디스패치 유형 MemArea 정의합니다.struct Drawable : pro::facade_builder ... ::build {} : Facade 유형을 정의 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 위에서 설명한 것보다 강력합니다. proxy 호출 할 때 수많은 오버로드 유형 (공식적으로 모든 유형, 모든 유형이 ProoverLoad 요구 사항을 충족)을 수행 할 수 있습니다.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 ), regocatability ( support_relocation ) 및 파괴 ( support_destruction )를 포함한 제약 조건 구성을 완전히 지원합니다.proxy 런타임 쿼리에 대한 유형 기반 컴파일 타임 반사를 지원합니다. 자세한 proxy_reflect 은 facade_builder::add_reflection 및 함수 템플릿을 참조하십시오. | 가족 | 최소 버전 | 필요한 깃발 |
|---|---|---|
| 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 후원을 암시해서는 안됩니다. 타사 상표 또는 로고를 사용하면 타사 정책이 적용됩니다.