Windows 8 Metro: C++/CX vs. C#

June 25, 2012

Lately, I’ve been doing development of a Windows 8 Metro application using C++ only (yes, that’s right, no C#) for a client. The reasons for that are mainly an existing C++ code base and a good C++ acquaintance that the team in question has.

I’ve been using the new C++/CX extensions that make it easier to work with the Windows Runtime (WinRT); easier with respect to the Windows Runtime Library (WRL) that uses standard C++ with a bunch of helpers (such as ComPtr<T> as a smart pointer for a COM/WinRT interface).

Even with C++/CX, the amount (verbosity) and complexity of of code that’s needed is higher than in C#. Here are some highlighted differences:

Defining regular property

A simple thing, isn’t it? First, let’s check out C#:

  1. public string Value { get; set; }

We can do this by wrapping a private field, but essentially it’s the same as a regular .NET property.

Here’s the equivalent code in C++/CX. First, the header file:

  1. property Platform::String^ Value {
  2.     Platform::String^ get();
  3.     void set(Platform::String^);
  4. }

The set and/or get must be specified fully. Now the implementation:

  1. String^ MyUserControl::Value::get() {
  2.     return _value;
  3. }
  4.  
  5. void MyUserControl::Value::set(String^ value) {
  6.     _value = value;
  7. }

The code assumes that a private field named _value is defined in the class header (no automatic properties in C++).

Registering for an event

C#: use regular event syntax, with or without a lambda expression. Examples:

  1. this.Suspending += OnSuspending;
  2. Suspending += (sender, e) => {
  3.     // suspending…
  4. };

In C++, both approaches are possible, but here’s the big difference:

  1. Suspending += ref new SuspendingEventHandler(this, &App::OnSuspending);
  2. Suspending += ref new SuspendingEventHandler([](Object^ sender, SuspendingEventArgs^ args) {
  3.     // suspending
  4. });

The real issue here is that the compiler insists that even in the case of a lambda function (which is of a known type to the compiler), the entire signature be specified exactly and the delegate object created. Redundancies everywhere.

Defining dependency properties

When creating templated or user controls, there is a need to define dependency properties. The definition in C# is exactly the same as it’s defined in WPF or Silverlight: a public static field and a get/set wrapper. Here’s a Text property added to some user control:

  1. public string Text {
  2.     get { return (string)GetValue(TextProperty); }
  3.     set { SetValue(TextProperty, value); }
  4. }
  5.  
  6. public static readonly DependencyProperty TextProperty =
  7.      DependencyProperty.Register("Text", typeof(string), typeof(MyUserControl1), new PropertyMetadata(null));        

What about C++? Not much fun. First, the header file:

  1. public:
  2.     property Platform::String^ Text { void set(Platform::String^); Platform::String^ get(); }
  3.  
  4.     static property Windows::UI::Xaml::DependencyProperty^ TextProperty {
  5.         Windows::UI::Xaml::DependencyProperty^ get() { return s_textProperty; }
  6.     }
  7.  
  8. private:
  9.     static Windows::UI::Xaml::DependencyProperty^ s_textProperty;

That sets the property wrappers, the private static field and the public static property wrapping that field. Why don’t we make the field public and be done with it? That’s not allowed for a WinRT type – it can only expose methods (or properties which eventually are methods as well), but not fields (everything is part of a vtable based interface).

Now for the implementation file (statics must be initialized):

  1. TypeName stringType = { "String", TypeKind::Primitive };
  2. TypeName ownerType = { ChatElement::typeid->FullName, TypeKind::Metadata };
  3.  
  4. PropertyMetadata^ textMetaData = ref new PropertyMetadata(nullptr);
  5.  
  6. DependencyProperty^ ChatElement::s_textProperty = DependencyProperty::Register("Text", stringType, ownerType, textMetaData);

ChatElement is the example user control type. The TypeName structure is projected to C# as a regular System.Type, but not in C++.

All this for just one dependency property!

Asynchronous operations

C# excels here, with the await keyword used for every IAsyncOperation<T> type interface (and similar interfaces):

  1.   var picker = new FileOpenPicker();
  2.   var file = await picker.PickSingleFileAsync();
  3.   var stm = await file.OpenReadAsync();
  4.   // use the stream, luke!

In C++, the regular way is handling the Completed event with a lambda function and then moving on from there. The problem is that we need to specify captured variables, and with nested asynchronous operations this gets tedious (see event registration above):

  1. auto picker = ref new FileOpenPicker();
  2. auto dispatcher = Dispatcher;
  3. auto filePickOperation = picker->PickSingleFileAsync();
  4. filePickOperation->Completed = ref new AsyncOperationCompletedHandler<StorageFile^>(
  5.     [dispatcher](IAsyncOperation<StorageFile^>^ operation, AsyncStatus status) {
  6.         auto file = operation->GetResults();
  7.         auto openOperation = file->OpenReadAsync();
  8.         openOperation->Completed = ref new AsyncOperationCompletedHandler<IRandomAccessStreamWithContentType^>(
  9.             [dispatcher](IAsyncOperation<IRandomAccessStreamWithContentType^>^ stmOperation, AsyncStatus status) {
  10.                 auto stm = stmOperation->GetResults();
  11.                 dispatcher->RunAsync(CoreDispatcherPriority::Normal, ref new DispatchedHandler([stm]() {
  12.                     // use the stream…
  13.                 }));
  14.         });
  15. });

Amazingly enough, this is equivalent to the previous C#  code snippet…

Some operations don’t return on the same thread, so the UI dispatcher has to be invoked; this is pretty bad. Fortunately, we can improve things by using the new Concurrency::task<> class like so:

  1. auto picker = ref new FileOpenPicker();
  2. task<StorageFile^> pickerTask(picker->PickSingleFileAsync());
  3. pickerTask.then([](StorageFile^ file) {
  4.     task<IRandomAccessStreamWithContentType^> stmTask(file->OpenReadAsync());
  5.     stmTask.then([](IRandomAccessStreamWithContentType^ stm) {
  6.         // use the stream, luke…
  7.     });
  8. });

This is certainly better, but it’s not an integral part of C++/CX, but rather a C++ class that “happens” to help. Even with this construct, it’s still pretty far from the C# version.

Conclusion

C++/CX is a step in the right direction, but it’s not yet (in my humble opinion) a viable alternative to C# for a medium or large metro application. C++/CX is certainly required for writing WinRT components, because it’s the only language that guarantees no CLR involvement. Writing a C# component is problematic in the general case, as a C++ client would have to incur the overhead of the CLR which is a primary reason to use C++ in the first place.

With the right improvements – basically less verbosity and redundancies – C++ can become an easy to use language for metro apps. Coupled with its immediate access to almost any C++ library (and DirectX!) makes it the most powerful language in the WinRT world.

Add comment
facebook linkedin twitter email

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

7 comments

  1. Jeremiah MorrillJune 25, 2012 ב 23:21

    Thanks for this article. I just wanted to suggest a few things.

    Cx does have an “auto” property, as you can define a property simply like this:

    property Platform::String^ Name;

    Also, it is possible to write your property implementation in only the header file, but if you have any large amount of logic, it is suggested to still use the cpp file.

    For dependency properties, there is a lot of code, but a strength of C++ (when used properly) is to use macros for code gen. It would be totally possible to define a DP as to make this task simple as:

    DP_PROPERTY(MyProperty, Platform::String^, ChangeCallback, …);

    It certainly isn’t as easy as C#, and you certainly won’t get fast compling/interation times, but C++ Cx is still pretty awesome 🙂

    Reply
  2. James McNellisJune 25, 2012 ב 23:39

    One correction: C++/CX does support trivial properties. You could have declared your Value property as:

    property Platform::String^ Value;

    This has the same effect as the C# code that you have: it creates private, unnamed backing storage for the property and declares and defines the trivial accessor and mutator.

    I think that your statement concerning the PPL task class misses the point: you refer to it as “not an integral part of C++/CX, but rather a C++ class that ‘happens’ to help.” That is true, but that is A Good Thing, and one of the key features of C++: C++ is highly extensible through libraries. The fact that a library-only solution like ‘task’ can make the code so much simpler is great.

    There are cleaner ways to express some of these concepts, and in some cases (e.g., dependency properties) the C++/CX code is far more concise and less repetitive than the C# code. I’ll work up a few examples to demonstrate.

    (I agree, however, that many things are still uglier and more verbose than in C#. C++ lambdas are exceedingly ugly and the await feature in C# is pretty slick.)

    Reply
  3. pavelyJune 26, 2012 ב 02:48

    I stand corrected on the auto-implemented properties.

    As for macros – sure, you can create macros to shorten the code, but that’s not the point. The point is, since we have extensions, we should have some more that cover common scenarios like dependency properties and asynchronous operations. I would have expected something like c#’s await – I don’t care if that would have been implemented with PPL tasks or some other mechanism.
    Macros belong to the C world, and should generally be avoided for the reasons you mentioned.

    Reply
  4. HunterJune 30, 2012 ב 19:26

    You are spot on. C++/CX is a great tool for a subset of scenarios requiring greater control of the machine (games, a need for DirectX, etc). Those who can wield this tool effectively are to be well respected. But, the lower productivity, complexity, safety, security, etc do not make this tool applicable to the majority of scenarios.

    Reply
  5. Ben Voigt [Visual C++ MVP]July 4, 2012 ב 20:12

    Seems like most of these short functions should be written inline inside the class declaration anyway, and that dramatically reduces the amount of code duplication required for C++.

    It’s also unfair to have using statements in C# and not use C++’s using namespace feature. There’s no reason for any difference in the type names except for “.” vs “::”.

    Reply
  6. pavelyJuly 4, 2012 ב 20:36

    using namespace in header files is frowned upon, as it makes unsuspecting CPP files get usings they may never intended to use.
    In any case, it’s not just the length – it’s the ease of use.

    Reply
  7. Bret KuhnsJuly 16, 2012 ב 16:20

    It seems to me that your examples are victims of a lack of lead-by-example documentation from Microsoft (it’s still early, so I won’t fault them… yet). I’m of the understanding that the PPL is specifically designed to work well with WinRT to address these headaches you’re pointing out. I don’t think it just “happens” that the PPL tasks work in your example. I thought I would clean up your final example a bit more, seeing that you’re leaving out concurrency::create_task (not to be confused with concurrency::make_task *grumbles*) which makes the code even more concise.

    http://pastebin.com/D7KpEf5K

    If we had polymorphic lambdas (which Herb seems keen to push for the next standard, cross your fingers!), we could be even more brief.

    Reply