Can C++/CX and C++/WinRT be used in the same project?
Asked Answered
B

2

14

Earlier this week, Kenny Kerr presented C++/WinRT at CppCon 20161. It is a Standard C++ projection for the Windows Runtime, based on Modern.

As far as I understand, the C++/CX compiler/preprocessor/code generator doesn't touch Standard C++ code, and with C++/WinRT being a Standard C++ library it is my naïve interpretation, that both C++/CX and C++/WinRT can be used in the same project.

Questions:

  • First things first: Is my naïve interpretation correct?
  • If so, can C++/CX and C++/WinRT be used in the same compilation unit?
  • At what granularity can C++/CX and C++/WinRT be mixed, in case they cannot reside in the same compilation unit?
  • Can C++/WinRT consume types implemented with C++/CX in the same project? (I expect this to be difficult, since the C++/WinRT compiler needs to generate headers from the .winmd metadata, so there is a dependency on the (pre-)compiler output.)

In case it matters, answers to those questions allow me to make decisions now on how to move my C++/CX projects into the future.


1 Embracing Standard C++ for the Windows Runtime (on YouTube).
Bassinet answered 25/9, 2016 at 11:32 Comment(4)
Hmya, we'll have to see what he's been working on the past year, he has not kept his github depository updated. If you never had a good reason to dip into WRL before then it is unlikely that C++/WinRT is going to spin any propellers. Which is really what it is, at least the moderncpp flavor, another wrapper like WRL but updated for C++1xyz. Kerr and McNellis' code make my head hurt, you can sort of see what it does but not easily see what it doesn't do.Ludwog
@HansPassant: The main reason for C++/WinRT (over C++/CX) would be performance. Compile times aren't great, and error messages are good riddles, usually. Still, I like the succinct (or overly terse, if you wish) syntax more than the explicit WRL invocations (I might change my mind, the first time I have to debug a real issue, though). I agree, that it's harder to see, what code does (or doesn't do). But that's just as true for C#/.NET, and that never was in the way of making it the preferred target for many folks out there. Anyway, personal preference is personal, I guess ;)Bassinet
I'm not a C++ expert, but... why not? C++/CX projects can have regular C++ code in them and C++/WinRT is just regular C++, right?Shurlock
@FilipSkakun: That was my initial instinct as well. At the source code level, both language projections can easily co-exist. However, there's also tooling involved. The C++/CX compiler does generate code from ref classes, as well as .winmd metadata, so using a ref class from C++/WinRT code in the same compilation unit should prove difficult. While compiling the code, it needs the .winmd input, which is a product of compiling. Plus, I don't know whether the C++/CX compiler produces any code from imported .winmd data, that (potentially) collides with C++/WinRT #includes.Bassinet
C
3

Regarding the question "Can C++/WinRT consume types implemented with C++/CX in the same project?"

The answer is Yes and No. With a 'ref class' defined in the same project, as such a project must be compiled with C++/CX enabled, your code can simply use the class as it can any ref class.

However, if you want to consume the 'ref class' as a C++/WinRT projection, the answer is effectively no.

In order to get the C++/WinRT projected class definition, you need to run the cppwinrt.exe compiler over the metadata for the 'ref class'. That would require somehow getting the metadata. You could rig up some mechanism to compile the 'ref class' once, get the winmd, process it through mdmerge to put it in canonical form, run cppwinrt.exe on the metadata to get the projected class definition, then include the generated headers.

Alternatively, you can write the IDL to describe the 'ref class', compile it to metadata using MIDLRT, then run cppwinrt.exe. Neither is practical IMO.

The most reasonable alternative is simply use the ref class as is as a C++/CX type, as the definition is in the same solution. Next most practical solution is put the class in a separate project, compile it getting the winmd, then creating headers from the winmd. This approach also allow the separate project that consumes the 'ref class' (via the projection) to build without use of C++/CX code.

To be completely transparent, note that our initial release (now available at https://github.com/Microsoft/cppwinrt) does not include the cppwinrt.exe compiler itself. Rather, it contains the C++/WinRT header files containing projections for all Windows Runtime types/APIs defined in the Windows 10 Anniversary Update SDK -- this includes the Universal Platform APIs and all Extension SDK APIs.

Countershaft answered 29/9, 2016 at 16:48 Comment(2)
That's a great summary, thanks for that. It's pretty much the way I had it figured out. Except, I completely missed the (now obvious) fact, that you can simply consume ref classes using the C++/CX language extensions in otherwise standard C++ code (e.g. C++/WinRT). And while you hinted towards it: How soon is "soon"? It feels like I've been losing sleep over this for more than a year now. Not having the compiler up front is a bit of a let-down, even though I do understand that you need to get it right the first time around.Bassinet
For C++/WinRT to consume C++/CX types try applying gist.github.com/kennykerr/105f96d61f51b5773670844edec99d85 for now. :)Stramonium
B
17

The short answer is that yes C++/CX and C++/WinRT can be used within the same project.

The C++/CX compiler injects the Winmd types into the root namespace. C++/WinRT wraps everything inside its own root winrt namespace to accommodate interop with C++/CX and avoid C++ compiler ambiguity errors with other libraries. So the following C++/CX code:

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");

May be rewritten with C++/WinRT as follows:

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

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

Alternatively, if you’re compiling with /ZW then you might rewrite it as follows (to avoid error C2872: 'Windows': ambiguous symbol):

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

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

Another way you might combine both C++/CX and C++/WinRT in the same source file is to use a root namespace for both as follows:

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");
}

At the end of the day, C++/WinRT is just a standard C++ library that you may include into any applicable C++ project. C++/CX and C++/WinRT do however deal with metadata very differently. C++/CX both consumes and produces metadata directly whereas C++/WinRT is constrained by standard C++ and thus requires a standalone tool (cppwinrt.exe) to assist in bridging that gap.

Brownell answered 27/9, 2016 at 21:31 Comment(3)
Thanks so much for answering. So at the source level, both C++/CX and C++/WinRT can happily co-exist in the same compilation unit. The heterogeneous tooling, however, may require separation (when consumers and producers live in different language projections). One detail isn't clear to me, though: Can cppwinrt.exe extract .winmd metadata from C++/WinRT source or object code? Or are there additional requirements (like MIDL files) to publish C++/WinRT implementations for consumption by other language projections?Bassinet
They can coexist, but it should be noted that some specific system/utility headers change behavior when built with /ZW than without, and some headers require it. This can also result in additional compiler warnings that do not happen if you are using C++/WinRT without /ZW.Magree
It would seem that to answer the OP's questions, /ZW cannot be both present and absent in the same compilation unit. Hence, for standard C++ constructs, the smallest granularity for C++/CX and C++/WinRT to coexist in the same project is in different compilation units in the same linkage unit (e.g., DLL; EXE). But for .NET constructs, the only way to co-exist is via C++/CX's (and C#'s) and C++/WinRT presentation of those .NET constructs—••absolutely not•• bypassing behind the scenes via the standard C++ domain.Relevance
C
3

Regarding the question "Can C++/WinRT consume types implemented with C++/CX in the same project?"

The answer is Yes and No. With a 'ref class' defined in the same project, as such a project must be compiled with C++/CX enabled, your code can simply use the class as it can any ref class.

However, if you want to consume the 'ref class' as a C++/WinRT projection, the answer is effectively no.

In order to get the C++/WinRT projected class definition, you need to run the cppwinrt.exe compiler over the metadata for the 'ref class'. That would require somehow getting the metadata. You could rig up some mechanism to compile the 'ref class' once, get the winmd, process it through mdmerge to put it in canonical form, run cppwinrt.exe on the metadata to get the projected class definition, then include the generated headers.

Alternatively, you can write the IDL to describe the 'ref class', compile it to metadata using MIDLRT, then run cppwinrt.exe. Neither is practical IMO.

The most reasonable alternative is simply use the ref class as is as a C++/CX type, as the definition is in the same solution. Next most practical solution is put the class in a separate project, compile it getting the winmd, then creating headers from the winmd. This approach also allow the separate project that consumes the 'ref class' (via the projection) to build without use of C++/CX code.

To be completely transparent, note that our initial release (now available at https://github.com/Microsoft/cppwinrt) does not include the cppwinrt.exe compiler itself. Rather, it contains the C++/WinRT header files containing projections for all Windows Runtime types/APIs defined in the Windows 10 Anniversary Update SDK -- this includes the Universal Platform APIs and all Extension SDK APIs.

Countershaft answered 29/9, 2016 at 16:48 Comment(2)
That's a great summary, thanks for that. It's pretty much the way I had it figured out. Except, I completely missed the (now obvious) fact, that you can simply consume ref classes using the C++/CX language extensions in otherwise standard C++ code (e.g. C++/WinRT). And while you hinted towards it: How soon is "soon"? It feels like I've been losing sleep over this for more than a year now. Not having the compiler up front is a bit of a let-down, even though I do understand that you need to get it right the first time around.Bassinet
For C++/WinRT to consume C++/CX types try applying gist.github.com/kennykerr/105f96d61f51b5773670844edec99d85 for now. :)Stramonium

© 2022 - 2024 — McMap. All rights reserved.