C++/WinRT gotcha: Setting properties incorrectly

Raymond Chen

Raymond

Getting and setting a Windows Runtime property looks like this:

LanguageGetterSetter
C#v = o.Property;o.Property = 42;
C++/CXv = o->Property;o->Property = 42;
C++/WinRTv = o.Property();o.Property(42);
JavaScriptv = o.property;o.property = 42;
Pythonv = o.propertyo.property = 42

Somebody is the odd man out.

All the projections use a simple member access to read a property and a simple assignment statement to set a property, with the exception of C++/WinRT, which uses a function call in both places.

That’s because the standard C++ language doesn’t have “properties”, and C++/WinRT is a projection of the Windows Runtime into standard C++. (C++/CX gets away with it because it’s not standard C++.)

If you’re translating existing code from one of the other languages to C++/WinRT, you may realize that properties need to change to function calls, but in your haste (or tiredness), you mistakenly convert o.Property = 42 to

    o.Property() = 42;

Fortunately, this gives you a compiler error because you cannot assign to an integer value.

Unfortunately, if the property has a non-primitive type, you don’t get an error.

    o.Name() = L"Fred";
    // oops: Should be o.Name(L"Fred");

    lv.Background() = greenBrush;
    // oops: Should be lv.Background(greenBrush);

That’s because you are assigning to the temporary object returned by the property getter method, and that temporary object has an assignment operator.

The above code breaks down like this:

    auto name = o.Name();
    name = L"Fred";
    // destruct temporary "name"

    auto background = lv.Background();
    background = greenBrush;
    // destruct temporary "background"

Congratulations, you updated a temporary that was immediately destructed. Total waste of time.

21 comments

Comments are closed.

    • Avatar
      Mpho Mokoena

      Don’t worry, the C++ standard has a solution to this problem:
      If the class declares its assignment operator with a ref-qualifier:
      `Class& operator=(const Class&) &;`
      the compiler will report an error when trying to assign to a temporary.

      This nicely illustrates the fundamental character of C++: sane behavior is possible if you’re careful; but insanity is the default.

      • Raymond Chen
        Raymond ChenMicrosoft logo

        The C++ standard giveth, and the C++ standard taketh away. If a base class has a Base& operator=(const Base&)& method (denying the rvalue assignment), the derived class's default copy assignment operator does not have the lvalue qualifier, so it can be used on rvalues again!

        • Ben Voigt
          Ben Voigt

          The C++ language has all the tools for building smart property members as a library (using proxy objects). I did it way back with C++98. It wasn’t worth the trouble, because C++ code using “smart property” members breaks every paradigm and programmer expectation.

          Notably, the C++ way using proxy objects results in properties that can be passed by reference (although they aren’t type-compatible with references to plain data of the same type) which is something that all the newer languages struggle with.

  • Avatar
    Simon Clarkstone

    Do linters pick this mistake up? If the assignment has no effect then that needs a warning, and if the assignment does have an effect then that is a bizarre design that deserves a warning too.

      • Avatar
        Simon Clarkstone

        Ah, thanks. I didn’t think of reference-counting as I haven’t touched C++ for about 20 years and used it very little before that. I agree the compiler is doing the right thing there, but from a human point of view are there any cases where assigning to a temporary like this is both a useful and clear way of accomplishing something?

        • Raymond Chen
          Raymond ChenMicrosoft logo

          Even if it were std::string, the compiler doesn’t optimize it out. In the special case of std::string, the compiler would be permitted to do so because std::string is defined by the standard library. But if it were folly::fbstring, the compiler is probably going to perform the copy.

  • Avatar
    Joshua Hudson

    Are you sure you don’t want to see properties in current C++? I did them back in college and I’m sure it still works. Just don’t use them on temporary copies of objects. I always made vectors of classes as vectors of pointers to classes so my property implementation didn’t have to care.

    • Avatar
      Ray Koopa

      What if that would have been done, and the WinRT class also defined an actual “GetProperty” or “SetProperty” method besides a “Property” property? Wouldn’t that clash? despite poor naming efforts in first place.

  • Avatar
    Neil Rashbrook

    I guess the other approach would be for o.Property() to return an intermediate object, such that if you tried to use it then you would get the property for real, but if you tried to assign to it then it would set the property, in much the same way that array-like objects allow you to write o[1] = o[0];.