Skip to content

Conversation

@asklar
Copy link
Member

@asklar asklar commented Apr 9, 2023

This adds the following helpers:

  • read_only_property<T>
  • read_write_property<T>
  • simple_event<T> (maps to an event that notifies a EventHandler<T>)
  • typed_event<S, T> (maps to an event that notifies a TypedEventHandler<S, T>)
  • notify_property_changed<CRTP>- base class for a default INotifyPropertyChanged
  • property_with_notify<T> - a notifiable property

added tests. The INPC test requires instantiating the xaml type PropertyChangedEventArgs, which requires having XAML running, so that necessitates the test being either UWP or having a xaml island. Chose the latter, which means having a sxs manifest with maxversiontested. It also requires updating the command line to cppwinrt to include extensions, hence the -input sdk+ now instead of -input sdk

@roxk
Copy link
Contributor

roxk commented Apr 10, 2023

This PR would be very useful in my effort to make WinRT component authoring idl-free via idlgen (sorry for the shameless plug). Specifically, idlgen could recognize the property helpers in this PR and simplify a lot of boilerplate and attribute needed to tell whether uint64_t Foo(); in C++ is a UInt64 Foo{get;}; or UInt64 Foo(); in idl.

@jonwis
Copy link
Member

jonwis commented Apr 16, 2023

Neat - I've written that simple_property thing probably 5 times now. Here's what I settled on, cribbed from a lost local repo (I can't recreate the event version, yours looks great.):

namespace wil
{
    /// @cond
    namespace details
    {
        template<typename T> struct prop_storage
        {
            T m_value{};
            operator T& () { return m_value; }
            operator T const& () const { return m_value; }
            template<typename Q> auto operator=(Q&& q) {
                m_value = wistd::forward<Q>(q);
                return *this;
            }
        };
    }
    /// @endcond

    /** Produces the property operator() adapters around a type
    C++/WinRT property syntax requires operator()(value) to set and operator()() to get. This type
    is both the storage of the underlying property and the operator() overloads for access. It can
    be used in the same way as the property type as simple_property<Q> is-a Q.
    ~~~
    struct MyType : MyTypeT<MyType> {
        wil::simple_property<winrt::hstring> DisplayName{ L"puppies" };
        wil::simple_property<bool> IsEnabled { false };
        void Flip() {
            if (IsEnabled) {
                DisplayName = L"kittens";
                IsEnabled = false;
            } else {
                DisplayName = L"puppies";
                IsEnabled = true;
            }
        }
    };
    ~~~
    */
    template<typename T> struct simple_property : 
        wistd::conditional_t<wistd::is_scalar_v<T>, details::prop_storage<T>, T>
    {
        template<typename Q> auto& operator()(Q&& q)
        {
            *this = std::forward<Q>(q);
            return *this;
        }

        auto& operator()()
        {
            return *this;
        }

        template<typename Q> auto operator=(Q&& q)
        { 
            return static_cast<T&>(*this) = std::forward<Q>(q); 
        }
    };
}

This "is-a" means PropertyName can be used just like the underlying type - so you can say things like DisplayName.empty() or similar.

I couldn't get a "glom on" version like this to work nicely because property_adapter<> still requires storage for the T&. It's probably possible with some offsetof hilarity in a macro.

template<typename T> struct property_adapter
{
    T& m_source;

    template<typename Q> auto& operator()(Q&& q)
    {
        return m_source = std::forward<Q>(q);
    }

    auto& operator()()
    {
        return m_source;
    }
};

struct thing
{
    int m_value{3};
    property_adapter<int> TheValue{ m_value };
};

@asklar asklar requested a review from jonwis April 18, 2023 20:20
@jaigak
Copy link

jaigak commented Apr 19, 2023

I would also recommend adding a CTAD guide so that you can avoid writing the property type (only works if it's static).
Example:

inline static single_threaded_rw_property MyInt { 10 };

@jonwis jonwis merged commit 1c6126b into microsoft:master Apr 29, 2023
@asklar asklar deleted the propertyHelpers branch April 29, 2023 08:12
@citelao
Copy link
Contributor

citelao commented Jul 11, 2023

@asklar @jonwis, is there any documentation in the README about how to use these helpers? They look cool.

@asklar
Copy link
Member Author

asklar commented Jul 11, 2023

@citelao looks like I forgot to add docs (oops!); mind adding some? (it doesn't look like we have detailed docs for the rest of wil?)
the library that preceded this (and its docs) can be found here: https://asklar.github.io/xaml-islands/
repo: https://github.com/asklar/xaml-islands

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants