원문 캡처 · devblogs.microsoft.com
원문 캡처 · devblogs.microsoft.com

C++/WinRT에서 직접 구현한 객체를 만들 때는 make<Impl>과 make_self<Impl> 중 하나를 쓴다. 먼저 Windows Runtime 프로젝션(projection)을 사용할지, 구현 객체에 접근할지에 따라 선택하면 된다.

여기서 Impl은 Windows Runtime 객체를 구현한 클래스다. IDL 파일에 선언된 객체라면 인스턴스 객체는 winrt::Namespace::implementation::ClassName, 팩터리 객체는 winrt::Namespace::factory_implementation::ClassName이다. IDL에 선언되지 않았다면 winrt::implements를 직접 상속한 클래스가 Impl이다.

make<Impl>은 구현 객체를 만든 뒤 Windows Runtime 프로젝션을 반환한다. Impl이 winrt::implements를 직접 상속했다면 프로젝션은 첫 번째로 구현한 인터페이스다. 프로젝션에 노출되지 않은 구현 객체의 멤버에 접근하려면 get_self<Impl>(projected)를 쓸 수 있다.

반면 make_self<Impl>은 구현 객체를 만들고 프로젝션 대신 winrt::com_ptr<Impl>을 반환한다. 이 포인터로 구현 객체의 멤버에 접근한 다음 * 연산자로 프로젝션 타입으로 바꿀 수 있다.

구현 클래스의 프로젝션에 노출되지 않는 공개 멤버에 먼저 접근할 예정이라면 com_ptr<Impl>의 -> 연산자를 쓴다. 예를 들어 프로젝션을 반환하기 전에 객체를 설정할 수 있다.

cpp
winrt::IVector<winrt::Widget>
 MakeWidgets(int style, int count)
{
 std::vector<winrt::Widget> v(count, nullptr);
 std::generate(v.begin(), v.end(), [&] {
 auto widget = winrt::make_self<implementation::Widget>(style);
 widget->Enabled(true); 
 return *widget;
 });
 return winrt::multi_threaded_vector(std::move(v));
}

프로젝션만 사용할 예정이라면 구현 타입을 거치는 대신 바로 만들 수 있다.

cpp
// The long way

winrt::IVector<winrt::Widget>
 MakeWidgets(int style, int count)
{
 std::vector<winrt::Widget> v(count, nullptr);
 std::generate(v.begin(), v.end(), [&] {
 auto widget = winrt::make_self<implementation::Widget>(style);
 return *widget;
 });
 return winrt::multi_threaded_vector(std::move(v));
}
cpp
// The short way

winrt::IVector<winrt::Widget>
 MakeWidgets(int style, int count)
{
 std::vector<winrt::Widget> v(count, nullptr);
 std::generate(v.begin(), v.end(), [&] {
 return winrt::make_self<implementation::Widget>(style);
 });
 return winrt::multi_threaded_vector(std::move(v));
}

이 경우 구현 타입을 거칠 필요가 없다. 내 추측으로는 긴 방식을 쓰는 사람들은 객체 생성 방법이 두 가지라는 점을 몰라 한 가지 방법만 익힌 뒤 더 짧은 방법이 있어도 계속 사용하는 것 같다.