Skip to content

Feature: Support [variant] and [hasvariant] attributes#1524

@oldnewthing

Description

@oldnewthing

Version

2.0.250303.1

Summary

The [variant] and [hasvariant] parameter attributes are hints that scalars should be auto-boxed. C++/WinRT does not take advantage of this right now.

Reproducible example

// Sample.idlnamespaceSample{runtimeclass Thing{staticAccept([variant] Object value)} } // C++/WinRT without [variant] supportwinrt::Thing::Accept(winrt::box_value(3)); // C++/WinRT with [variant] supportwinrt::Thing::Accept(3);

Expected behavior

The value "3" to be accepted without boxing.

Actual behavior

Compiler error saying that 3 cannot be converted to IInspectable const&.

Additional comments

Sketch is to create a new parameter type winrt::param::variant.

Option 1: Simple but expensive.

structwinrt::param::variant{template<typename T> variant(T&& arg) : value(box_value(arg)){} IInspectable value}; consume_Blah(winrt::param::variant arg);

This adds an AddRef/Release to the cost of an IInspectable parameter.

Option 2:

struct winrt::param::variant{template<typename = std::enable_if_t<std::is_base_of_v<Windows::Foundation::IInspectable, std::decay_t<T>>> variant(T&& arg) : value(get_abi(arg)){} template<typename U> variant(U&& arg) : temp(box_value(arg)){value = get_abi(temp)} void* value; IInspectable temp}; consume_Blah(winrt::param::variant arg); 

This avoids the AddRef/Release for inbound IInspectables, but still costs a null check when the variant destructs (assuming the compiler can't optimize it out).

Option 3: Overload the consumer to accept either IInspectable or param::boxed_value

structwinrt::param::boxed_value{template<typename U> variant(U&& arg) : value(box_value(arg)){} IInspectable value}; consume_Blah(IInspectable arg); consume_Blah(winrt::param::boxed_arg arg);

This avoids the overhead in the case where the caller passes an IInspectable, but it results in 2^N overload explosion.

Option 4: Use CTAD

template<bool boxed> structvariant{template<typename U> variant(U&& arg) : value(box_value(arg)){} IInspectable value}; template<> structvariant<false>{variant(Insp const& arg) : value(&arg){} constvoid* value}; template<typename T, typename = std::enable_if_t<!std::is_base_of_v<IInspectable, std::decay_t<T>>, int>> variant(T&&) -> variant<true> template<typename T, typename = std::enable_if_t<std::is_base_of_v<IInspectable, std::decay_t<T>>, void>> variant(T&&) -> variant<false> template<bool boxed1> consume_Blah(winrt::param::varaint<boxed1> arg);

This still has 2^N explosion (since each variant parameter gets a different bool parameter for its variant) but you only emit the code once.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions