Skip to content

Instantly share code, notes, and snippets.

@wholivesinapineappleunderthesea
Created June 10, 2023 00:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save wholivesinapineappleunderthesea/7b24cdec315275bf6bcd061417b6dff9 to your computer and use it in GitHub Desktop.
Save wholivesinapineappleunderthesea/7b24cdec315275bf6bcd061417b6dff9 to your computer and use it in GitHub Desktop.
COMPtr implementnationn
#pragma once
#include <Unknwn.h>
#include <exception>
template <typename T> struct COMPtrOut
{
inline COMPtrOut() = delete;
inline COMPtrOut(const COMPtrOut&) = delete;
inline COMPtrOut(IUnknown** ptr) noexcept : m_pptr(ptr)
{
}
inline operator T**() const noexcept
{
return reinterpret_cast<T**>(m_pptr);
}
inline operator void**() const noexcept
{
return reinterpret_cast<void**>(m_pptr);
}
private:
IUnknown** m_pptr{};
};
template <typename T> struct COMPtrInOut
{
inline COMPtrInOut() = delete;
inline COMPtrInOut(const COMPtrInOut&) = delete;
inline COMPtrInOut(::IUnknown** ptr) noexcept
: m_pptr(ptr), m_previous(*ptr)
{
}
inline ~COMPtrInOut()
{
if (m_previous && m_previous != *m_pptr)
{
m_previous->Release();
}
}
inline operator T**() const noexcept
{
return reinterpret_cast<T**>(m_pptr);
}
inline operator void**() const noexcept
{
return reinterpret_cast<void**>(m_pptr);
}
private:
::IUnknown** m_pptr{};
::IUnknown* m_previous{};
};
template <typename T> struct COMPtr
{
static inline auto static_uuid = __uuidof(T);
using type = T;
using pointer = T*;
using reference = T&;
// Very similar to std::inout_ptr()
inline auto InOut() noexcept -> COMPtrInOut<T>
{
return COMPtrInOut<T>{&_ptr};
}
// Very similar to std::out_ptr()
inline auto Out() noexcept -> COMPtrOut<T>
{
return COMPtrOut<T>{&_ptr};
}
// Ptr operations:
inline auto operator->() noexcept -> pointer
{
return Get();
}
inline auto operator->() const noexcept -> const pointer
{
return Get();
}
inline auto operator*() noexcept -> reference
{
return *Get();
}
inline auto operator*() const noexcept -> const reference
{
return *Get();
}
inline operator pointer() noexcept
{
return Get();
}
inline operator const pointer() const noexcept
{
return Get();
}
// Boolean operations:
inline operator bool() const noexcept
{
return Get() != nullptr;
}
inline auto operator!() const noexcept -> bool
{
return Get() == nullptr;
}
inline auto operator==(std::nullptr_t) const noexcept -> bool
{
return Get() == nullptr;
}
inline auto operator!=(std::nullptr_t) const noexcept -> bool
{
return Get() != nullptr;
}
// Comparison operations:
template <template <class> class COMT, typename T2>
inline auto operator==(const COMT<T2>& other) const noexcept -> bool
requires std::equality_comparable_with<T*, T2*>
{
return Get() == other.Get();
}
template <template <class> class COMT, typename T2>
inline auto operator!=(const COMT<T2>& other) const noexcept -> bool
requires std::equality_comparable_with<T*, T2*>
{
return Get() != other.Get();
}
// Constructors:
inline COMPtr() noexcept : _ptr(nullptr)
{
}
inline COMPtr(std::nullptr_t) noexcept : _ptr(nullptr)
{
}
template <typename PtrT>
inline COMPtr(PtrT ptr) noexcept
requires std::derived_from<PtrT, T>
{
_ptr = ptr;
}
inline COMPtr(::IUnknown* ptr)
{
if (std::is_same_v<T, ::IUnknown>)
{
_ptr = ptr;
}
else
{
::IUnknown* ptemp{};
const auto hr = ptr->QueryInterface(
static_uuid, reinterpret_cast<void**>(&ptemp));
if (!SUCCEEDED(hr))
{
throw std::exception(
"QueryInterface failed, ptr will not be released!");
}
_ptr = ptemp;
// MIGHT be different, but if not we still need to release
// (QueryInterface will AddRef)
ptr->Release();
}
}
template <template <class> class COMT, typename T2>
inline COMPtr(const COMT<T2>& other) noexcept
requires std::convertible_to<T2*, T*>
{
_ptr = other._ptr;
AddRef();
}
template <template <class> class COMT, typename T2>
inline COMPtr(COMT<T2>&& other) noexcept
requires std::convertible_to<T2*, T*>
{
_ptr = other._ptr;
other._ptr = nullptr;
}
// Destructor:
inline ~COMPtr()
{
Reset();
}
// Assignment operators:
inline auto operator=(std::nullptr_t)
{
Reset();
}
inline auto operator=(::IUnknown* ptr)
{
if (std::is_same_v<T, ::IUnknown>)
{
Reset(ptr);
}
else
{
Reset();
::IUnknown* ptemp{};
const auto hr = ptr->QueryInterface(
static_uuid, reinterpret_cast<void**>(&ptemp));
if (!SUCCEEDED(hr))
{
throw std::exception(
"QueryInterface failed, ptr will not be released!");
}
_ptr = ptemp;
// MIGHT be different, but if not we still need to release
// (QueryInterface will AddRef)
ptr->Release();
}
}
template <typename PtrT>
inline auto operator=(PtrT ptr) noexcept
requires (std::derived_from<PtrT, T> && !std::same_as<PtrT, ::IUnknown>)
{
Reset(ptr);
}
template <template <class> class COMT, typename T2>
inline auto operator=(const COMT<T2>& other) noexcept
requires std::convertible_to<T2*, T*>
{
Reset(other._ptr);
AddRef();
}
template <template <class> class COMT, typename T2>
inline auto operator=(COMT<T2>&& other) noexcept
requires std::convertible_to<T2*, T*>
{
Reset(other._ptr);
other._ptr = nullptr;
}
// Member functions:
inline auto Get() noexcept -> pointer
{
return reinterpret_cast<pointer>(_ptr);
}
inline auto Get() const noexcept -> const pointer
{
return reinterpret_cast<const pointer>(_ptr);
}
inline auto Reset(::IUnknown* ptr = nullptr) -> void
{
Release();
_ptr = ptr;
}
inline auto Release() -> void
{
if (_ptr)
{
_ptr->Release();
_ptr = nullptr;
}
}
inline auto AddRef() -> void
{
if (_ptr)
{
_ptr->AddRef();
}
}
::IUnknown* _ptr{};
};
COMPtr<::IDXGISwapChain1> swapChain{};
if (SUCCEEDED(m_dxgiFactory->CreateSwapChainForHwnd(
m_d3dCommandQueue.Get(), m_hwnd,
&swapChainDesc, nullptr, nullptr, swapChain.Out())))
{
...
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment