When Standard C++ Isn’t Enough

Last time I showed how Standard C++ lets you go beyond the compiler. Modern C++ offers the productivity of C# and performance that beats even C++/CX. Well if Standard C++ is so great, why isn’t anyone using it to write Windows apps with the Windows Runtime? Because Standard C++ is not enough.

Think about this. The Windows SDK for Windows 8.1 – counting only the Windows Runtime – includes about 2,450 interfaces amounting to around 10,880 methods. Windows 10 – as of build 10069 – is over 3,930 interfaces and 17,748 methods. It is naïve to think that C++ developers are going to use the ABI interfaces directly since they are tedious to use and were never intended to be used directly. It is further unrealistic to think that C++ developers are going to “wrap” those native interfaces with modern C++ classes to create useful libraries. All of the supposed productivity is lost when you have to spend countless hours first writing a library. I tried that before. Here’s another C# example:

CurrencyFormatter currency = new CurrencyFormatter("USD");
currency.ApplyRoundingForCurrency(RoundingAlgorithm.RoundDown);

String result = currency.Format(123.456);
Debug.Assert(result == "$123.45");

I’ve created a currency formatter, set the rounding algorithm, and formatted a value. What about Standard C++? A lot of developers seem to think that C# is the most popular language on the Windows platform today because it is so much better than C++. It’s not, instead it’s all about the tooling. One of the main arguments for .NET was that it avoided the reference counting hell that C++ apparently produced. Proponents of this position would like to show you the example above written in Standard C++:

HSTRING_HEADER header = {};
HSTRING string = nullptr;
WindowsCreateStringReference(L"Windows.Globalization.NumberFormatting.CurrencyFormatter", 56, &header, &string);
ICurrencyFormatterFactory * factory = nullptr;
RoGetActivationFactory(string, __uuidof(factory), reinterpret_cast<void **>(&factory));

WindowsCreateStringReference(L"USD", 3, &header, &string);
ICurrencyFormatter * currency = nullptr;
factory->CreateCurrencyFormatterCode(string, &currency);
factory->Release();

ICurrencyFormatter2 * currency2 = nullptr;
currency->QueryInterface(&currency2);
currency2->ApplyRoundingForCurrency(RoundingAlgorithm_RoundDown);
currency2->Release();

INumberFormatter * formatter = nullptr;
currency->QueryInterface(&formatter);
currency->Release();

HSTRING result = nullptr;
formatter->FormatDouble(123.456, &result);
formatter->Release();

assert(wcscmp(WindowsGetStringRawBuffer(result, nullptr), L"$123.45") == 0);
WindowsDeleteString(result);

But no C++ developer in his right mind would ever write code like this (and I haven’t even included error handling). Instead the C++ developer would naturally write something like this:

CurrencyFormatter currency(L"USD");
currency.ApplyRoundingForCurrency(RoundingAlgorithm::RoundDown);

String result = currency.Format(123.456);
assert(result == L"$123.45");

There are no pointers and no reference counting that you can see. The compiler takes care of these details and in fact does so more reliably and more efficiently than the C# compiler is able to do since this code is inherently deterministic. While Microsoft claims that C++ has always been supported this is not entirely truthful. You can read more about C++ vs C++/CX here.

The problem is that Standard C++ is not enough. Good tooling is required. C# developers don’t use the C# compiler in isolation but download the 4GB Visual Studio tools. C++ developers need better tools in order to write apps and components for the Windows platform. Modern C++ for the Windows Runtime provides that tool. Modern works in conjunction with Visual C++ to provide a first-class developer experience for C++.

currency

Why shouldn’t C++ developers get a classy type system with all the same productivity and unparalleled performance? Here’s how it works: the Modern compiler generates a complete language projection for Standard C++.

modern

I can simply run “modern.exe library” and it will produce a language projection for the latest version of the Windows SDK that is locally installed. There are options to use an alternative SDK, only include certain namespaces (to produce a smaller library), and so on.

library

The result is a header-only library that I can include with a single line of code:

#include <modern.h>

This header is backed by a small set of headers mainly because the Visual Studio editor would choke on one large file.

headers

The base.h header is the language projection itself. This contains all of the generic or metaprogramming that describes the bindings between modern C++ and the underlying ABI in such a way that the C++ compiler can chew on it at compile time. The sdk.* headers are essentially the metadata describing the various Windows API classes and other types that form the public surface area for the Windows API. While base.h is handcrafted, the sdk.* headers are entirely generated based on the Windows SDK.

The Modern library is also not simply a class library. It provides all of the building blocks you might need to drill in or tweak the performance of your app. You might normally be satisfied with the productivity of creating a CurrencyFormatter object as follows:

CurrencyFormatter currency(L"USD");

At other times you might want to create the activation factory yourself. The Modern library has got you covered:

auto factory = GetActivationFactory<CurrencyFormatter, ICurrencyFormatterFactory>();

You can then create a CurrencyFormatter object at your convenience:

CurrencyFormatter currency = factory.CreateCurrencyFormatterCode(L"USD");

This is obviously still far more convenient and reliable than using the ABI provided by the Windows SDK for C++ developers. There’s no reference counting, string handles, or pointers.

And then there’s formatting a currency value:

String result = currency.Format(123.456);

You might want to avoid an exception here perhaps to deal with any failures directly. No problem, Format is actually a method called FormatDouble on the INumberFormatter interface:

INumberFormatter formatter = currency;

String result;
HRESULT hr = formatter->abi_FormatDouble(123.456, set(result));

assert(hr == S_OK);
assert(result == L"$123.45");

As you can see you have all the control you might need, all of the productivity and safety you would expect, and all from your favorite C++ compiler thanks to Modern C++ for the Windows Runtime. Now what about C++? (19m3s) 🙂

12 thoughts on “When Standard C++ Isn’t Enough

  1. John L.

    Sorry for the stupid question but why can’t we avoid reference counting altogether? If I use C++ to call a Windows API written in C++, why the need for AddRef/Release? If it cannot be avoided, why not use something like Apple does with ARC (Automatic Reference Counting) to do all of this at compile time without having to butcher my code.

    Reply
    1. Kenny Kerr Post author

      It certainly can be avoided. The C++ compiler with the help of the Modern library takes care of all the reference counting so you never need to think about it.

      Reply
  2. jponsager

    I generally am very stubborn about writing my own code (or wrappers) against any native APIs (i.e. Direct2D), however you make a convincing argument. You must have a tremendous number of hours into this. I look forward to using it. How do we get our hands on the beta?

    Reply
  3. Jim

    Is the String type refferred to in your c++ example part of the Modern library? Can you easily convert from STL strings to this String type

    Reply
  4. JJD

    It’s a bit unfair to call your reference counting example as standard C++. I think the one responsible for it is that API’s designer, don’t you think? Why not mention RAII and smart pointers?

    Reply
    1. Kenny Kerr Post author

      The Modern library provides a projection of these reference-counted types in Standard C++ such that the C++ developer does not need to wrap everything inside a ComPtr, or similar smart pointer and then have to deal with pointer semantics and HRESULTs. A key point of the new Windows API is to provide a classy type system rather than the traditional COM-style API.

      Reply
  5. steve heller

    Would something like this also allow the automatic creation of classes for the letter/envelope idiom? Having to maintain those classes manually is about the only downside to using that very powerful idiom.

    Reply
  6. Alex Zarenin

    Kenny, how is this different from the “managed C++”, which allows one seamlessly use .Net Framework classes and, if necessary, intermix it with the traditional C++ objects? I think that Managed C++ covers all the bases.

    Reply
  7. Eric Rasmussen

    I’ve been a C# developer for more than a decade, and I remember absolutely loathing C++ after having been forced to work with DCOM and ATL for years. Microsoft’s C++ was a horrid mess back then, and I spent countless hours tracking down bugs in the template libraries – they are almost indecipherable, a tangled mess of preprocessor instructions and interface aggregation. I liked standard gcc on Linux with the GTK interfaces for UI. I hated C++ on Windows, dealing with BSTR, CString, VARIANT, etc.

    I’ve ignored C++ for many years. I’ve always been envious of the performance, but not enough to warrant going back to that mess. Today I came across this blog and saw what modern C++ can be like. You’ve renewed my interest in the language, so thank you!

    Reply

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s