Ограничьте допустимые COM-интерфейсы с помощью шаблонной функции с ограниченными переменными параметрами (std :: is_same) - PullRequest
0 голосов
/ 15 ноября 2018

У меня есть классы-обертки, соединяющие COM-указатели (или умные указатели) с различными интерфейсами.

INTEND: Некоторые COM-классы могут быть получены из различных других COM-интерфейсов, и я хочу создать шаблонный конструктор с переменными типами, который позволял бы передавать аргументы только соответствующих типов.

что-то вроде:

template <class T, class = typename 
   std::enable_if<std::is_base_of<IUnknown, T>::value>::type, class ... Types>
   class WithCOMptrbase
{
   protected:
   T* ptr_;

   public: 

   //construct smart pointer by copy
   WithCOMptrbase(T* ptr, bool AddRef = false)
     : ptr_(ptr)
   {
      if (AddRef) ptr_->AddRef();
   }

   /*construct a smart pointer by querying an interface from an argument of 
   a type which is the same as one of the variadics*/
   template <class TypeOther, class = typename
      std::enable_if<syd::is_same<Types... , TypeOther>::value... || 
      ...>::type> /*there needs to be a proper version*/
      WithCOMptrbase(TypeOther* ptr)
        : ptr_(cQueryInterface<T>(ptr))
      {}

//other methods
};

вспомогательная функция:

template <class U, class = typename 
   std::enable_if<std::is_base_of<IUnknown, U>::value>::type>
   T* cQueryInterface<T>(U *ptr)
{
   T* out;
   HRESULT hr = ptr->QueryInterface(__uuidof(T), (void**)&out);
   if (!SUCCEEED(hr)) throw _com_error(hr);
   return out;
}

Поэтому я определю свой класс-оболочку

class WrapperClass : protected WithCOMptrbase<IThis, IInterface1, IInterface2, IInterface3>
{
   //methods
};

Пока я нашел эту тему: Как сделать variadic is_same? но речь идет только о структурах, а не о функциях. Моя цель состоит в том, чтобы ограничить возможность передачи неверного указателя интерфейса, чтобы не допускать неправильных ошибок интерфейса во время выполнения.

UPDATE: Поскольку составление предпочтительнее наследования, я немного переосмыслил и решил использовать шаблонную функцию, а не шаблонный класс. До сих пор мне удалось объединить данные ответы и придумать это:

template <bool S, class Out, class Other, typename
std::enable_if<S>::type* = nullptr>
//copy-construct Smart Pointer for same Interfaces
WComPtr<Out> WFilterSame(const WComPtr<Other>& pOther)
{
    return WComPtr<Out>(pOther);
}

template <bool S, class Out, class Other, typename
    std::enable_if<!S>::type* = nullptr>
//Query Interface if differ
WComPtr<Out> WFilterSame(const WComPtr<Other>& pOther)
{
    return pOther.QueryInterface<Out>();
}

template <class Out, class ... Permitted, class Other>
WComPtr<Out> WFilterComInterfPtr(const WComPtr<Other>& pOther)
{

    static_assert(std::is_same<Out, Other>::value ||
        (std::is_same<Permitted, Other>::value || ...),
        "Interface is not supported.");

    return WFilterSame<std::is_same<Out, Other>::value, Out>(pOther);
}

Теперь я могу определить конструктор моего класса оболочки COM:

class WComClass
{
   private:
   WComPtr<Interface> pComPtr_; //My Smart COM pointer

   template <class Other>
   WComPtr<Interface> WFilter(const WComPtr<Other>& pOther)
   {
      return WFilterComInterfPtr<Interface, IAllowed1, IAllowed2>(pOther);
   }

   public:
   template <class Other>
   WComClass(const WComPtr<Other>& pOther)
      : pComPtr_(WFilter(pOther))
   {}

   //methods
};

Пока он ведет себя как задумано (WFilterComInterfPtr), я не ожидаю, что он потерпит неудачу в конструкторе класса-оболочки.

Ответы [ 2 ]

0 голосов
/ 15 ноября 2018

Как насчет CRTP, чтобы избежать какого-либо шаблона:

template <typename Base, typename T>
class Impl
{
public:
    Impl() = default;
    explicit Impl(T*) { static_cast<Base*>(this)->ptr_ = cQueryInterface<T>(ptr); }
};

template <class T, class ... Ts>
class WithCOMptrbase : private Impl<WithCOMptrbase<T, Ts...>, Ts>...
{
    static_assert(std::is_base_of<IUnknown, T>::value);
    static_assert((std::is_base_of<IUnknown, Ts>::value && ...));

    template <typename, typename> friend struct Impl; // We don't have variadic friend :/
protected:
    T* ptr_ = nullptr;


public:
    using Impl<WithCOMptrbase, Ts>::Impl...;

    //construct smart pointer by copy
    explicit WithCOMptrbase(T* ptr, bool AddRef = false) : ptr_(ptr)
    {
        if (AddRef) ptr_->AddRef();
    }

    //other methods
};
0 голосов
/ 15 ноября 2018

Попробуйте с

   template <class TypeOther, class =
      std::enable_if_t<(std::is_same_v<Types, TypeOther> || ...)>>        
      WithCOMptrbase(TypeOther* ptr)
        : ptr_(cQueryInterface<T>(ptr))
      {}

Я имею в виду ... вы используете три многоточия вместо одного (удалите многоточие после ::value и одно после Types) и вам нужна дополнительная параскобок.

Не по теме: вы уверены, что работает

template <class T, class ... Types, class = typename 
   std::enable_if<std::is_base_of<IUnknown, T>::value>::type>
   class WithCOMptrbase

?

SFINAE с типом по умолчанию после списка вариаций?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...