Initial release
This commit is contained in:
@@ -0,0 +1,464 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Flags : clang-format SMTGSequencer
|
||||
// Project : SDK Core
|
||||
//
|
||||
// Category : SDK Core Interfaces
|
||||
// Filename : pluginterfaces/base/funknownimpl.h
|
||||
// Created by : Steinberg, 10/2021
|
||||
// Description : Steinberg Module Architecture Interface Implementation Helper
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
// This file is part of a Steinberg SDK. It is subject to the license terms
|
||||
// in the LICENSE file found in the top-level directory of this distribution
|
||||
// and at www.steinberg.net/sdklicenses.
|
||||
// No part of the SDK, including this file, may be copied, modified, propagated,
|
||||
// or distributed except according to the terms contained in the LICENSE file.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#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
|
||||
|
||||
// clang-format off
|
||||
/**
|
||||
This header provides classes for working with FUnknown.
|
||||
|
||||
An interface which shall support Steinbergs Module Architecture should inherit from U::Unknown and provide a typedef
|
||||
@c IID of type U::UID.
|
||||
|
||||
On OS X you can generate an U::UID and copy it to the clipboard with the following shell command:
|
||||
@code
|
||||
uuidgen | { read id; echo -n "using IID = U::UID<0x${id:0:8}, 0x${id:9:4}${id:14:4}, 0x${id:19:4}${id:24:4}, 0x${id:28:8}>;" ; } | pbcopy
|
||||
@endcode
|
||||
|
||||
Example:
|
||||
@code{.cpp}
|
||||
struct IFoo : public U::Unknown
|
||||
{
|
||||
// Use a generated random uid here.
|
||||
using IID = U::UID<0x01234567, 0x89012345, 0x67890123, 0x45678901>;
|
||||
|
||||
virtual void bar () = 0;
|
||||
};
|
||||
@endcode
|
||||
|
||||
A class implementing the interface @c IFoo uses the U::Implements template to specify the
|
||||
interfaces it implements. All interfaces which the class should derive from need to be listed in the
|
||||
U::Directly template.
|
||||
|
||||
Example:
|
||||
@code{.cpp}
|
||||
struct FooImpl : public U::Implements<U::Directly<IFoo>>
|
||||
{
|
||||
void bar () override {}
|
||||
};
|
||||
@endcode
|
||||
|
||||
To check if a class can provide a specific interface use the U::cast function.
|
||||
|
||||
Example:
|
||||
@code{.cpp}
|
||||
void test (U::Unknown* obj)
|
||||
{
|
||||
if (auto foo = U::cast<IFoo> (obj))
|
||||
{
|
||||
// obj provided IFoo
|
||||
}
|
||||
}
|
||||
@endcode
|
||||
|
||||
The U::Implements class supports a second template parameter U::Indirectly for specifying
|
||||
a list of interfaces which should be available via @c queryInterface but not inherited from.
|
||||
This is useful if an interface extends another interface.
|
||||
|
||||
Example:
|
||||
@code{.cpp}
|
||||
struct IBar : public IFoo
|
||||
{
|
||||
using IID = U::UID<0x11223344, 0x55667788, 0x99001122, 0x33445566>;
|
||||
|
||||
virtual void baz () = 0;
|
||||
};
|
||||
|
||||
struct BarImpl : public U::Implements<U::Directly<IBar>, U::Indirectly<IFoo>>
|
||||
{
|
||||
void bar () override {}
|
||||
void baz () override {}
|
||||
};
|
||||
@endcode
|
||||
|
||||
In some cases a class shall be extended and an additional interface implemented.
|
||||
This is possible with the U::Extends template which is a generalization of the U::Implements
|
||||
template and allows specifying a base class from which should be inherited.
|
||||
|
||||
Example:
|
||||
@code{.cpp}
|
||||
struct ITest : public U::Unknown
|
||||
{
|
||||
using IID = U::UID<0x99887766, 0x55443322, 0x11009988, 0x77665544>;
|
||||
|
||||
virtual bool equal (int a, int b) const = 0;
|
||||
};
|
||||
|
||||
struct TestImpl : public U::Extends<FooImpl, U::Directly<ITest>>
|
||||
{
|
||||
bool equal (int a, int b) const override { return a == b; }
|
||||
};
|
||||
@endcode
|
||||
|
||||
To pass arbitrary arguments to the specified base class one can use the inherited @c Base
|
||||
typedef. All arguments passed to @c Base are automatically perfectly forwarded to the base class.
|
||||
|
||||
In the following example the value 42 is passed to the @c AlternativeFooImpl base class:
|
||||
@code{.cpp}
|
||||
struct AlternativeFooImpl : public U::Implements<U::Directly<IFoo>>
|
||||
{
|
||||
AlternativeFooImpl (int dummy = 0) : dummy {dummy} {}
|
||||
void bar () override {}
|
||||
|
||||
int dummy;
|
||||
};
|
||||
|
||||
struct AlternativeTestImpl : public U::Extends<AlternativeFooImpl, U::Directly<ITest>>
|
||||
{
|
||||
AlternativeTestImpl () : Base {42} {}
|
||||
|
||||
bool equal (int a, int b) const override { return a == b; }
|
||||
};
|
||||
@endcode
|
||||
*/
|
||||
// clang-format on
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace Steinberg {
|
||||
namespace FUnknownImpl {
|
||||
|
||||
/** Typedef to keep everything in this namespace. */
|
||||
using Unknown = FUnknown;
|
||||
|
||||
/** A base class which hides the FUnknown::iid static var */
|
||||
struct HideIIDBase : FUnknown
|
||||
{
|
||||
using iid = void;
|
||||
};
|
||||
|
||||
/** Common destroyer policy for ski object instances.*/
|
||||
struct Destroyer
|
||||
{
|
||||
template <typename UnknownT>
|
||||
static void destroy (UnknownT* ptr)
|
||||
{
|
||||
if (!!ptr)
|
||||
ptr->release ();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Base, typename D, typename I>
|
||||
class ImplementsImpl;
|
||||
|
||||
/**
|
||||
* This class provides a compile-time uid and enables interfaces to specify a UID as a simple
|
||||
* typedef. This way the FUID, DECLARE_CLASS_IID and DEF_CLASS_IID code can be omitted.
|
||||
*/
|
||||
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; }
|
||||
};
|
||||
|
||||
/** @return the TUID for an interface. */
|
||||
template <typename T>
|
||||
const TUID& getTUID ()
|
||||
{
|
||||
return ::Steinberg::getTUID<T> ();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given Unknown can provide the specified interface and returns it in an IPtr.
|
||||
*
|
||||
* @return an IPtr pointing to an instance of the requested interface or nullptr in case the
|
||||
* object does not provide the interface.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/** Casts to Unknown* and then to the specified interface. */
|
||||
template <typename I, typename S, typename T, typename U>
|
||||
IPtr<I> cast (ImplementsImpl<S, T, U>* u)
|
||||
{
|
||||
return cast<I> (u->unknownCast ());
|
||||
}
|
||||
|
||||
/** Casts to Unknown* and then to the specified interface. */
|
||||
template <typename I, typename T>
|
||||
IPtr<I> cast (const IPtr<T>& u)
|
||||
{
|
||||
return cast<I> (u.get ());
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
namespace Detail {
|
||||
|
||||
/**
|
||||
* This struct implements reference counting for the @c U::Implements template.
|
||||
* It also provides a @c queryInterface method stub to support @c queryInterface
|
||||
* call made in the @c U::Implements template.
|
||||
*/
|
||||
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 /*iid*/, void** obj)
|
||||
{
|
||||
*obj = nullptr;
|
||||
return kNoInterface;
|
||||
}
|
||||
//------------------------------------------------------------------------
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // Detail
|
||||
|
||||
/**
|
||||
* This struct is used to group a list of interfaces from which should be inherited and which
|
||||
* should be available via the @c queryInterface method.
|
||||
*/
|
||||
template <typename... T>
|
||||
struct Directly
|
||||
{
|
||||
};
|
||||
|
||||
/**
|
||||
* This struct is used to group a list of interfaces from which should not be inherited but which
|
||||
* should be available via the @c queryInterface method.
|
||||
*/
|
||||
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");
|
||||
};
|
||||
|
||||
/** This class implements the required virtual methods for the U::Unknown class. */
|
||||
template <typename BaseClass, typename I, typename... DirectIFs, typename... IndirectIFs>
|
||||
class ImplementsImpl<BaseClass, Directly<I, DirectIFs...>, Indirectly<IndirectIFs...>>
|
||||
: public BaseClass, public I, public DirectIFs...
|
||||
{
|
||||
public:
|
||||
//------------------------------------------------------------------------
|
||||
/**
|
||||
* This is a convenience typedef for the deriving class to pass arguments to the
|
||||
* constructor, which are in turn passed to the base class of this class.
|
||||
*/
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
/** This typedef enables using a custom base class with the interface implementation. */
|
||||
template <typename BaseClass, typename D, typename I = Indirectly<>>
|
||||
using Extends = ImplementsImpl<BaseClass, D, I>;
|
||||
|
||||
/** This typedef provides the interface implementation. */
|
||||
template <typename D, typename I = Indirectly<>>
|
||||
using Implements = ImplementsImpl<Detail::QueryInterfaceEnd<Detail::RefCounted>, D, I>;
|
||||
|
||||
/** This typedef provides the interface implementation for objects which should not be destroyed via
|
||||
* FUnknown::release (like singletons). */
|
||||
template <typename D, typename I = Indirectly<>>
|
||||
using ImplementsNonDestroyable =
|
||||
ImplementsImpl<Detail::QueryInterfaceEnd<Detail::NonDestroyable>, D, I>;
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // FUnknownImpl
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
/** Shortcut namespace for implementing FUnknown based objects. */
|
||||
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;
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
} // namespace U
|
||||
} // namespace Steinberg
|
||||
Reference in New Issue
Block a user