Как добавить защищенные методы, возвращающие типы, не относящиеся к среде выполнения Windows, в базовый класс C ++ / WinRT

У меня есть следующий код C ++ / CX, который работает так, как задумано, но хотел бы преобразовать его в код C ++ / WinRT:

namespace My.Custom.Stuff
{
  [Windows::Foundation::Metadata::WebHostHidden]
  public ref class BaseClass : public Windows::UI::Xaml::Controls::SwapChainPanel
  {
  public:
    BaseClass();
    void PublicMethod();

  protected:
    static ::DirectX::XMFLOAT3 ProtectedMethod();
  };
}


namespace My.Custom.Stuff
{
  [Windows::Foundation::Metadata::WebHostHidden]
  public ref class SubClass sealed : public BaseClass
  {
  public:
    SubClass();
    void UseProtectedMethod()
    {
      ::DirectX::XMFLOAT3 value = ProtectedMethod()
      // ...
    }
  };
}

Однако проблема, с которой я сталкиваюсь, заключается в следующем: BaseClass содержит защищенный метод, который возвращает тип, который не может быть сопоставлен с соответствующим типом среды выполнения Windows. В C ++ / CX это не проблема, потому что ProtectedMethod вообще не отображается. Если я использую свой компонент среды выполнения Windows, ProtectedMethod просто не отображается, чего я хочу.

Однако этот метод должен быть членом BaseClass, потому что несколько других классов, таких как SubClass, используют этот метод при реализации своих общедоступных методов. Я придумал следующий код MIDL на C ++ / WinRT:

namespace My.Custom.Stuff
{
  unsealed runtimeclass BaseClass : Windows.UI.Xaml.Controls.SwapChainPanel
  {
    BaseClass();
    void PublicMethod();

    // This does not work
    protected DirectX.XMFLOAT3 RgbFromBrush(IInspectable brush);
  }
}



import "BaseClass.idl";


namespace My.Custom.Stuff
{
  runtimeclass SubClass : BaseClass
  {
    SubClass();
    void UseProtectedMethod();
  }
}

Проблема в том, что если я определю ProtectedMethod таким образом, это не сработает, потому что ::DirectX::XMFLOAT3 не относится к типу среды выполнения Windows. Если я использую любой другой тип возвращаемого значения, этот защищенный метод будет отображен. Однако он не должен быть виден при использовании этого компонента среды выполнения Windows, и, конечно же, мне не придется менять его возвращаемый тип.

Как я могу добиться того, что я делаю с C ++ / CX, используя C ++ / WinRT?

Изменить

При компиляции кода MIDL получается что-то вроде этого:

#include "BaseClass.g.h"


namespace winrt::My::Custom::Stuff::implementation
{
  struct BaseClass : BaseClassT<BaseClass>
  {
    BaseClass() = default;
    // ...
  };
}


namespace winrt::My::Custom::Stuff::factory_implementation
{
  struct BaseClass : BaseClassT<BaseClass, implementation::BaseClass>
  {
  };
}

Я подумал, что можно просто добавить защищенный метод следующим образом:

namespace winrt::My::Custom::Stuff::implementation
{
  struct BaseClass : BaseClassT<BaseClass>
  {
    BaseClass() = default;

  protected:
    static ::DirectX::XMFLOAT3 ProtectedMethod();
  };
}

Однако попытка использовать ProtectedMethod в SubClass приводит к следующей ошибке:

error C2039: 'ProtectedMethod': is not a member of 'winrt::My::Custom::Stuff::BaseClass'

Вот как я его использую:

#include "SubClass.g.h"


namespace winrt::My::Custom::Stuff::implementation
{
  struct SubClass : SubClassT<SubClass>
  {
    SubClass() = default;
    void UseProtectedMethod()
    {
      ::DirectX::XMFLOAT3 value = ProtectedMethod();
    }
  };
}


namespace winrt::My::Custom::Stuff::factory_implementation
{
  struct SubClass : SubClassT<SubClass, implementation::SubClass>
  {
  };
}

person ackh    schedule 30.11.2020    source источник
comment
В вашем типе реализации вы в конечном итоге можете делать все, что захотите (включая наследование от базового класса, внутреннего по отношению к реализации). MIDL разработан для описания поверхности общедоступного API.   -  person IInspectable    schedule 30.11.2020
comment
Я так и предполагал. Однако я не могу добавить метод туда, где SubClass может его найти. Я добавил дополнительные пояснения к своему вопросу. Из того, что я вижу в опубликованном сообщении об ошибке, это несоответствие пространств имен. Я добавляю метод в пространство имен * :: implementation, но он ищет метод вне его.   -  person ackh    schedule 30.11.2020
comment
Ваша мысль, что просто добавление ProtectedMethod() в BaseClass - это правильно. Я пробую путь на своей стороне, и ProtectedMethod() может быть успешно вызван в UseProtectedMethod() из SubClass. Не могли бы вы предоставить нам код звонка ProtectedMethod() для тестирования? Я использую ProtectedMethod() в SubClass вот так: void SubClass::UseProtectedMethod() { ProtectedMethod(); } И вызываю SubClass.UseProtectedMethod() так: SubClass sc; sc.UseProtectedMethod();   -  person YanGu    schedule 01.12.2020
comment
@ YanGu-MSFT Я добавил пример, показывающий, как я пытаюсь использовать этот метод. Ничего особенного, похоже, именно то, что вы предлагаете. У меня сложилось впечатление, что это может быть связано с тем, что я использую вложенные пространства имен (My::Custom::Stuff). Однако это интуиция, и я не знаю, что именно искать.   -  person ackh    schedule 01.12.2020
comment
Думаю, это может сработать: struct BaseImpl { protected: ::DirectX::XMFLOAT3 ProtectedMethod(); }; struct BaseClass : BaseClassT<BaseClass>, BaseImpl { ... }; struct SubClass : SubClassT<SubClass>, BaseImpl { ... }.   -  person Raymond Chen    schedule 02.12.2020
comment
@RaymondChen Я пробовал этот подход, но SubClass просто не может найти BaseImpl, хотя оба они определены в пространстве имен winrt::My::Custom::Stuff::implementation. У вас есть полный рабочий пример?   -  person ackh    schedule 02.12.2020


Ответы (1)


При преобразовании из C ++ / CX в C ++ / WinRT в ваш файл IDL должны входить только общедоступные методы, поскольку это единственные методы, составляющие поверхность WinRT API.

Методы, которые являются частными / защищенными / внутренними, не являются частью поверхности WinRT API, поэтому они не должны входить в IDL.

Опубликованный вами код использования не должен компилироваться, потому что в вашем C ++ определении implementation::SubClass отсутствует параметр шаблона. Поскольку определение IDL / WinRT SubClass происходит от BaseClass, вам необходимо указать тип реализации BaseClass для SubClassT, и если вы изучите содержимое созданного cppwinrt SubClass.h, вы увидите, что это происходит. Как только вы правильно объявите свою реализацию SubClass, вы получите доступ к защищенным методам на BaseClass.

Я только что успешно попробовал это, и это выглядит так:

BaseClass.idl

namespace RuntimeComponent1
{
    [default_interface]
    unsealed runtimeclass BaseClass
    {
        BaseClass();
        void PublicMethod();
    }
}

BaseClass.h

#pragma once
#include "BaseClass.g.h"

struct non_winrt_type
{

};

namespace winrt::RuntimeComponent1::implementation
{
    struct BaseClass : BaseClassT<BaseClass>
    {
        BaseClass() = default;
        void PublicMethod() {}

    protected:
        non_winrt_type ProtectedMethod()
        {
            return {};
        }
    };
}
namespace winrt::RuntimeComponent1::factory_implementation
{
    struct BaseClass : BaseClassT<BaseClass, implementation::BaseClass>
    {
    };
}

SubClass.idl

import "BaseClass.idl";

namespace RuntimeComponent1
{
    [default_interface]
    runtimeclass SubClass : BaseClass
    {
        SubClass();
        void UseProtectedMethod();
    }
}

SubClass.h

#pragma once
#include "SubClass.g.h"
#include "BaseClass.h"

namespace winrt::RuntimeComponent1::implementation
{
    // Notice how BaseClass is used here.
    // This was copied directly from the generated boilerplate SubClass.h
    struct SubClass : SubClassT<SubClass, RuntimeComponent1::implementation::BaseClass>
    {
        SubClass() = default;

        void UseProtectedMethod()
        {
            auto result = ProtectedMethod();
        }
    };
}
namespace winrt::RuntimeComponent1::factory_implementation
{
    struct SubClass : SubClassT<SubClass, implementation::SubClass>
    {
    };
}
person Ryan Shepherd    schedule 02.12.2020
comment
Я попытался воссоздать ваш образец, и он работал так, как вы его описали. Однако это то, что я также пытался сделать в своем проекте миграции, и это просто не сработало. Итак, я пошел искать разницу. В моем проекте миграции у меня есть 4 вложенных пространства имен, например. My.Custom.Super.WindowsRuntimeComponent. Ваш образец работает хорошо, если у меня 3 или меньше пространств имен, но с 4 пространствами имен инструментарий начинает изменять имя сгенерированного файла BaseClass.g.h на My.Custom.Super.WindowsRuntimeComponent.BaseClass.g.h. Но в настоящее время это происходит только в моем недавно созданном образце, а не в моем проекте миграции. - person ackh; 03.12.2020
comment
Происходит что-то странное. Мой проект миграции компилируется, только если ::implementation часть пространства имен не добавлена ​​перед BaseClass в struct BaseClass : BaseClassT<BaseClass, implementation::BaseClass>. Чтобы разобраться в этом, нужно больше копать ... - person ackh; 03.12.2020
comment
Хорошо, поэтому помимо изменения имен сгенерированных файлов я пропустил добавление #include BaseClass.h в SubClass.h. Вместо этого я удалил ::implementation часть пространства имен. Исправление этого и учет измененных имен файлов решает мою проблему. Большое спасибо за вашу помощь, Райан! - person ackh; 03.12.2020