通过使用具有有限可变参数的模板函数来限制允许的 COM 接口 (std::is_same)
Limit allowed COM Interfaces by using template function with limited variadic parameters (std::is_same)
我有包装器 classes 包含指向不同接口的 COM 指针(或智能指针)。
打算:
一些 COM classes 可以从各种其他 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
class WrapperClass : protected WithCOMptrbase<IThis, IInterface1, IInterface2, IInterface3>
{
//methods
};
到目前为止我已经找到这个线程:
How to make a variadic is_same?
但它只是关于结构,而不是函数。
我的目标是限制传递不适当的接口指针的可能性,因此不在运行时处理错误的接口错误。
更新:
由于组合优于继承,我重新考虑并决定使用模板函数而不是模板 class。到目前为止,我已经设法结合给定的答案并得出以下结论:
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:
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),我不希望它在包装器 class 构造函数中失败。
试试
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 通过默认类型在 可变参数列表之后?
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
};
我有包装器 classes 包含指向不同接口的 COM 指针(或智能指针)。
打算: 一些 COM classes 可以从各种其他 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
class WrapperClass : protected WithCOMptrbase<IThis, IInterface1, IInterface2, IInterface3>
{
//methods
};
到目前为止我已经找到这个线程: How to make a variadic is_same? 但它只是关于结构,而不是函数。 我的目标是限制传递不适当的接口指针的可能性,因此不在运行时处理错误的接口错误。
更新: 由于组合优于继承,我重新考虑并决定使用模板函数而不是模板 class。到目前为止,我已经设法结合给定的答案并得出以下结论:
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:
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),我不希望它在包装器 class 构造函数中失败。
试试
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 通过默认类型在 可变参数列表之后?
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
};