C++/CX和C++/WinRT可以在同一个项目中使用吗?

14
本周早些时候,Kenny Kerr 在 CppCon 2016 上 介绍了 C++/WinRT1。它是基于Modern的 Windows 运行时的标准 C++ 射影。
据我所知,C++/CX 编译器/预处理器/代码生成器不会接触标准的 C++ 代码,并且由于 C++/WinRT 是一个标准的 C++ 库,因此我的幼稚理解是,C++/CX 和 C++/WinRT 都可以在同一个项目中使用。
问题:
首先要弄清楚的是:我的幼稚解释是否正确?
如果是,C++/CX和C++/WinRT能在同一编译单元中使用吗?
如果它们不能放置在同一编译单元中,那么C++/CX和C++/WinRT可以以什么粒度混合使用?
在同一项目中,C++/WinRT能否使用使用C++/CX实现的类型?(我认为这可能很困难,因为C++/WinRT编译器需要从.winmd元数据生成头文件,所以存在对(预)编译器输出的依赖。)
如果有关于这些问题的答案,那么现在就可以决定如何将我的C++/CX项目转移到未来。
注1:Embracing Standard C++ for the Windows Runtime (on YouTube)

嗯,我们得看看他过去一年在做什么,他没有更新他的Github代码库。如果你以前从未有过涉足WRL的好理由,那么C++/WinRT不太可能会激起任何兴趣。至少现代cpp风格是这样的,它是另一个类似于WRL但针对C++1xyz进行了更新的包装器。Kerr和McNellis的代码让我头疼,你可以看到它做了什么,但很难看到它没有做什么。 - Hans Passant
@HansPassant:选择C++/WinRT(而不是C++/CX)的主要原因是性能。编译时间不够快,错误信息通常也比较难懂。尽管如此,我仍然更喜欢简洁(或过于简短,如果你愿意)的语法,而不是显式的WRL调用(虽然在第一次调试真正的问题时我可能会改变主意)。我同意,这样很难看出代码做了什么(或没做什么)。但对于C#/.NET来说也是一样的,但这从未妨碍它成为许多人首选的目标。无论如何,个人偏好就是个人偏好,我想这是吧 ;) - IInspectable
我不是C++专家,但是为什么不呢?C++/CX项目可以包含常规的C++代码,而C++/WinRT也只是常规的C++,对吧? - Filip Skakun
1
@FilipSkakun:这也是我的最初直觉。在源代码级别上,两种语言投影可以很容易地共存。但是,还涉及到工具。C++/CX编译器确实会从ref class生成代码,以及.winmd元数据,因此在同一编译单元中使用来自C++/WinRT代码的ref class可能会证明困难。在编译代码时,它需要.winmd输入,这是编译的产物。此外,我不知道C++/CX编译器是否从导入的.winmd数据中生成任何代码,这(潜在地)与C++/WinRT #include冲突。 - IInspectable
2个回答

16

简短的回答是,是的,C++/CX和C++/WinRT可以在同一个项目中使用。

C++/CX编译器将Winmd类型注入到根命名空间中。C++/WinRT将所有内容封装在自己的根winrt命名空间中,以适应与C++/CX的互操作性,并避免与其他库的C++编译器歧义错误。因此,以下是C++/CX代码:

using namespace Windows::Foundation;
using namespace Windows::Networking;

Uri ^ uri = ref new Uri(L"https://moderncpp.com/");
HostName ^ name = ref new HostName(L"moderncpp.com");

可以用C++/WinRT重写如下:

using namespace winrt;
using namespace Windows::Foundation;
using namespace Windows::Networking;

Uri uri(L"https://moderncpp.com/");
HostName name(L"moderncpp.com");

如果您正在使用 /ZW 进行编译,则可以按照以下方式重写它(以避免错误 C2872:“Windows”:模糊符号):

using namespace winrt::Windows::Foundation;
using namespace winrt::Windows::Networking;

Uri uri(L"https://moderncpp.com/");
HostName name(L"moderncpp.com");

你可以使用根命名空间将C++/CX和C++/WinRT结合在同一源文件中,如下所示:

namespace cx
{
    using namespace Windows::Foundation;
    using namespace Windows::Networking;
}

namespace winrt
{
    using namespace Windows::Foundation;
    using namespace Windows::Networking;
}

void Sample()
{
    cx::Uri uri(L"https://moderncpp.com/");
    winrt::HostName name(L"moderncpp.com");
}

在一天结束时,C++/WinRT只是一个标准的C++库,可以包含到任何适用的C++项目中。但是,C++/CX和C++/WinRT处理元数据的方式非常不同。C++/CX直接使用和生成元数据,而C++/WinRT受标准C++的限制,因此需要一个独立的工具(cppwinrt.exe)来帮助弥合这个差距。

非常感谢回答。因此,在源级别上,C++/CX 和 C++/WinRT 可以在同一编译单元中愉快地共存。然而,异构工具可能需要分开使用(当消费者和生产者存在不同的语言映射时)。有一个细节我不太清楚:cppwinrt.exe 能否从 C++/WinRT 源代码或目标代码中提取 .winmd 元数据?或者是否有其他要求(如 MIDL 文件)来发布供其他语言映射消费的 C++/WinRT 实现? - IInspectable
它们可以共存,但应注意,在使用“/ZW”构建系统/实用程序标头时,某些特定的行为会发生变化,并且某些标头需要它。这也可能导致额外的编译器警告,如果您在没有“/ZW”的情况下使用C++/WinRT,则不会发生这种情况。 - Chuck Walbourn
似乎要回答OP的问题,/ZW不能同时存在于同一编译单元中。因此,对于标准C++构造,C++/CX和C++/WinRT在同一项目中共存的最小粒度是在相同链接单元(例如DLL;EXE)中的不同编译单元。但是对于.NET构造,唯一的共存方式是通过C++/CX(和C#的)和C++/WinRT呈现这些.NET构造 - 绝对不是通过标准C++域在幕后绕过。 - Andreas ZUERCHER

3
关于问题“C++/WinRT能否在同一个项目中使用C++/CX实现的类型?”。
答案是肯定和否定的。如果在同一项目中定义了'ref class'(此类项目必须启用C++/CX),则您的代码可以像使用任何ref类一样使用该类。
然而,如果您想将'ref class'作为C++/WinRT projection使用,则答案实际上是否定的。
为了获得C++/WinRT projected class definition,您需要在'ref class'元数据上运行cppwinrt.exe编译器。这将需要以某种方式获取元数据。您可以设计机制来编译'ref class'一次,获得winmd,通过mdmerge处理它以使其变成标准形式,对元数据运行cppwinrt.exe以获取投影类定义,然后包含生成的头文件。
或者,您可以编写IDL以描述'ref class',使用MIDLRT将其编译为元数据,然后运行cppwinrt.exe。在我看来,两种方法都不太实际。
最合理的替代方案是将'ref class'作为C++/CX类型直接使用,因为定义在同一个解决方案中。下一个最实用的解决方案是将此类放在单独的项目中,编译并获得winmd,然后从winmd创建头文件。此方法还允许消费'ref class'的单独项目(通过projection)构建而不使用C++/CX代码。
需要完全透明,我们的初始发布(现在可在https://github.com/Microsoft/cppwinrt找到)并不包括cppwinrt.exe编译器本身。相反,它包含包含Windows 10周年更新SDK中定义的所有Windows运行时类型/API的投影的C++/WinRT头文件--这包括通用平台API和所有扩展SDK API。

这是一个很好的总结,谢谢。这基本上就是我想到的方式。除了我完全忽略了(现在显而易见的)事实,即你可以使用C++/CX语言扩展在其他标准C++代码(例如C++/WinRT)中简单地消耗ref class。虽然你暗示了它:但“不久”意味着什么时候?感觉我已经因此失眠一年多了。没有编译器有点让人失望,尽管我理解你需要第一次就做对。 - IInspectable
目前,如果想让C++/WinRT使用C++/CX类型,请尝试应用https://gist.github.com/kennykerr/105f96d61f51b5773670844edec99d85。 :) - Chawathe Vipul S

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接