I have wrapper classes contaning COM pointers (or smart pointers) to different interfaces.
INTEND: Some COM classes can be obtained from various other COM interfaces, and I want to make a template constructor with variadic types, which would allow passing arguments only of appropriate types.
something like:
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
};
helper function:
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;
}
Therefore, I will define my wrapper class
class WrapperClass : protected WithCOMptrbase<IThis, IInterface1, IInterface2, IInterface3>
{
//methods
};
So far I have found this thread: How to make a variadic is_same? but it is only about structs, not functions. My goal is to limit the possibility of passing inapproprtiate Interface pointer, hence not to deal with wrong interface errors at runtime.
UPDATE: Since Composition is preferable over inheritance, I've done some rethinking and decided to use a template function rather than a template class. So far I've managed to combine given answers and came up with this:
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);
}
Now I can define a constructor of my COM wrapper 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
};
So far it behaved as intended (WFilterComInterfPtr), I don't expect it to fail in the wrapper class costructor.
Try with
I mean... you're using three ellipsis instead of one (remove the ellipsis after
::value
and the one afterTypes
) and you need an additional couple of parentheses.Off topic: are you sure that works
?
SFINAE through a default type after a variadic list?