#pragma once
#include "pluginterfaces/base/fplatform.h"
#include "pluginterfaces/base/funknown.h"
#include <atomic>
#include <type_traits>
#if !(SMTG_CPP11)
#error "C++11 is required for this header"
#endif
namespace Steinberg {
namespace FUnknownImpl {
using Unknown = FUnknown;
struct HideIIDBase : FUnknown
{
using iid = void;
};
struct Destroyer
{
template <typename UnknownT>
static void destroy (UnknownT* ptr)
{
if (!!ptr)
ptr->release ();
}
};
template <typename Base, typename D, typename I>
class ImplementsImpl;
template <uint32 t1, uint32 t2, uint32 t3, uint32 t4>
struct UID
{
enum : int8
{
l1_1 = static_cast<int8> ((t1 & 0xFF000000) >> 24),
l1_2 = static_cast<int8> ((t1 & 0x00FF0000) >> 16),
l1_3 = static_cast<int8> ((t1 & 0x0000FF00) >> 8),
l1_4 = static_cast<int8> ((t1 & 0x000000FF)),
l2_1 = static_cast<int8> ((t2 & 0xFF000000) >> 24),
l2_2 = static_cast<int8> ((t2 & 0x00FF0000) >> 16),
l2_3 = static_cast<int8> ((t2 & 0x0000FF00) >> 8),
l2_4 = static_cast<int8> ((t2 & 0x000000FF)),
l3_1 = static_cast<int8> ((t3 & 0xFF000000) >> 24),
l3_2 = static_cast<int8> ((t3 & 0x00FF0000) >> 16),
l3_3 = static_cast<int8> ((t3 & 0x0000FF00) >> 8),
l3_4 = static_cast<int8> ((t3 & 0x000000FF)),
l4_1 = static_cast<int8> ((t4 & 0xFF000000) >> 24),
l4_2 = static_cast<int8> ((t4 & 0x00FF0000) >> 16),
l4_3 = static_cast<int8> ((t4 & 0x0000FF00) >> 8),
l4_4 = static_cast<int8> ((t4 & 0x000000FF))
};
UID () = delete;
static constexpr TUID data = {
#if COM_COMPATIBLE
l1_4, l1_3, l1_2, l1_1, l2_2, l2_1, l2_4, l2_3,
#else
l1_1, l1_2, l1_3, l1_4, l2_1, l2_2, l2_3, l2_4,
#endif
l3_1, l3_2, l3_3, l3_4, l4_1, l4_2, l4_3, l4_4,
};
static const TUID& toTUID () { return data; }
};
template <typename T>
const TUID& getTUID ()
{
return ::Steinberg::getTUID<T> ();
}
template <typename I>
IPtr<I> cast (Unknown* u)
{
I* out = nullptr;
return u && u->queryInterface (getTUID<I> (), reinterpret_cast<void**> (&out)) == kResultOk ?
owned (out) :
nullptr;
}
template <typename I, typename S, typename T, typename U>
IPtr<I> cast (ImplementsImpl<S, T, U>* u)
{
return cast<I> (u->unknownCast ());
}
template <typename I, typename T>
IPtr<I> cast (const IPtr<T>& u)
{
return cast<I> (u.get ());
}
namespace Detail {
struct RefCounted
{
RefCounted () = default;
RefCounted (const RefCounted&) {}
RefCounted (RefCounted&& other) SMTG_NOEXCEPT : refCount {other.refCount.load ()} {}
virtual ~RefCounted () = default;
RefCounted& operator= (const RefCounted&) { return *this; }
RefCounted& operator= (RefCounted&& other) SMTG_NOEXCEPT
{
refCount = other.refCount.load ();
return *this;
}
uint32 PLUGIN_API addRef () { return ++refCount; }
uint32 PLUGIN_API release ()
{
auto rc = --refCount;
if (rc == 0)
{
destroyInstance ();
refCount = -1000;
delete this;
return uint32 ();
}
return rc;
}
private:
virtual void destroyInstance () {}
std::atomic<int32> refCount {1};
};
struct NonDestroyable
{
NonDestroyable () = default;
virtual ~NonDestroyable () = default;
uint32 PLUGIN_API addRef () { return 1000; }
uint32 PLUGIN_API release () { return 1000; }
private:
virtual void destroyInstance () {}
};
template <typename T>
struct QueryInterfaceEnd : T
{
tresult PLUGIN_API queryInterface (const TUID , void** obj)
{
*obj = nullptr;
return kNoInterface;
}
};
}
template <typename... T>
struct Directly
{
};
template <typename... T>
struct Indirectly
{
};
template <typename Base, typename D, typename I>
class ImplementsImpl
{
static_assert (sizeof (Base) == -1, "use U::Directly and U::Indirectly to specify interfaces");
};
template <typename Base, typename... DirectInterfaces, typename... IndirectInterfaces>
class ImplementsImpl<Base, Indirectly<IndirectInterfaces...>, Directly<DirectInterfaces...>>
{
static_assert (sizeof (Base) == -1, "U::Indirectly only allowed after U::Directly");
};
template <typename BaseClass, typename I, typename... DirectIFs, typename... IndirectIFs>
class ImplementsImpl<BaseClass, Directly<I, DirectIFs...>, Indirectly<IndirectIFs...>>
: public BaseClass, public I, public DirectIFs...
{
public:
using Base = ImplementsImpl<BaseClass, Directly<I, DirectIFs...>, Indirectly<IndirectIFs...>>;
template <typename... Args>
ImplementsImpl (Args&&... args) : BaseClass {std::forward<Args> (args)...}
{
}
tresult PLUGIN_API queryInterface (const TUID tuid, void** obj) override
{
if (!obj)
return kInvalidArgument;
if (queryInterfaceImpl<I, DirectIFs...> (tuid, *obj) ||
queryInterfaceImpl<IndirectIFs...> (tuid, *obj))
{
static_cast<Unknown*> (*obj)->addRef ();
return kResultOk;
}
return BaseClass::queryInterface (tuid, obj);
}
uint32 PLUGIN_API addRef () override { return BaseClass::addRef (); }
uint32 PLUGIN_API release () override { return BaseClass::release (); }
Unknown* unknownCast () { return static_cast<Unknown*> (static_cast<I*> (this)); }
private:
template <typename Interface>
inline constexpr bool match (const TUID tuid) const noexcept
{
return reinterpret_cast<const uint64*> (tuid)[0] ==
reinterpret_cast<const uint64*> (getTUID<Interface> ())[0] &&
reinterpret_cast<const uint64*> (tuid)[1] ==
reinterpret_cast<const uint64*> (getTUID<Interface> ())[1];
}
template <int = 0>
inline constexpr bool queryInterfaceImpl (const TUID, void*&) const noexcept
{
return false;
}
template <typename Interface, typename... RemainingInterfaces>
inline bool queryInterfaceImpl (const TUID tuid, void*& obj) noexcept
{
if (match<Interface> (tuid) || match<Unknown> (tuid))
{
obj = static_cast<Interface*> (this);
return true;
}
obj = getInterface<RemainingInterfaces...> (tuid);
return obj != nullptr;
}
template <int = 0>
inline constexpr void* getInterface (const TUID) const noexcept
{
return nullptr;
}
template <typename Interface, typename... RemainingInterfaces>
inline void* getInterface (const TUID tuid) noexcept
{
return match<Interface> (tuid) ? static_cast<Interface*> (this) :
getInterface<RemainingInterfaces...> (tuid);
}
};
template <typename BaseClass, typename D, typename I = Indirectly<>>
using Extends = ImplementsImpl<BaseClass, D, I>;
template <typename D, typename I = Indirectly<>>
using Implements = ImplementsImpl<Detail::QueryInterfaceEnd<Detail::RefCounted>, D, I>;
template <typename D, typename I = Indirectly<>>
using ImplementsNonDestroyable =
ImplementsImpl<Detail::QueryInterfaceEnd<Detail::NonDestroyable>, D, I>;
}
namespace U {
using Unknown = FUnknownImpl::HideIIDBase;
using FUnknownImpl::UID;
using FUnknownImpl::Extends;
using FUnknownImpl::Implements;
using FUnknownImpl::ImplementsNonDestroyable;
using FUnknownImpl::Directly;
using FUnknownImpl::Indirectly;
using FUnknownImpl::cast;
using FUnknownImpl::getTUID;
} }