//----------------------------------------------------------------------------- // 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 #include //------------------------------------------------------------------------ #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> { 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 (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::Indirectly> { 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> { 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> { AlternativeFooImpl (int dummy = 0) : dummy {dummy} {} void bar () override {} int dummy; }; struct AlternativeTestImpl : public U::Extends> { 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 static void destroy (UnknownT* ptr) { if (!!ptr) ptr->release (); } }; template 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 struct UID { enum : int8 { l1_1 = static_cast ((t1 & 0xFF000000) >> 24), l1_2 = static_cast ((t1 & 0x00FF0000) >> 16), l1_3 = static_cast ((t1 & 0x0000FF00) >> 8), l1_4 = static_cast ((t1 & 0x000000FF)), l2_1 = static_cast ((t2 & 0xFF000000) >> 24), l2_2 = static_cast ((t2 & 0x00FF0000) >> 16), l2_3 = static_cast ((t2 & 0x0000FF00) >> 8), l2_4 = static_cast ((t2 & 0x000000FF)), l3_1 = static_cast ((t3 & 0xFF000000) >> 24), l3_2 = static_cast ((t3 & 0x00FF0000) >> 16), l3_3 = static_cast ((t3 & 0x0000FF00) >> 8), l3_4 = static_cast ((t3 & 0x000000FF)), l4_1 = static_cast ((t4 & 0xFF000000) >> 24), l4_2 = static_cast ((t4 & 0x00FF0000) >> 16), l4_3 = static_cast ((t4 & 0x0000FF00) >> 8), l4_4 = static_cast ((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 const TUID& getTUID () { return ::Steinberg::getTUID (); } /** * 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 IPtr cast (Unknown* u) { I* out = nullptr; return u && u->queryInterface (getTUID (), reinterpret_cast (&out)) == kResultOk ? owned (out) : nullptr; } /** Casts to Unknown* and then to the specified interface. */ template IPtr cast (ImplementsImpl* u) { return cast (u->unknownCast ()); } /** Casts to Unknown* and then to the specified interface. */ template IPtr cast (const IPtr& u) { return cast (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 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 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 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 struct Indirectly { }; template class ImplementsImpl { static_assert (sizeof (Base) == -1, "use U::Directly and U::Indirectly to specify interfaces"); }; template class ImplementsImpl, Directly> { 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 class ImplementsImpl, Indirectly> : 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, Indirectly>; template ImplementsImpl (Args&&... args) : BaseClass {std::forward (args)...} { } tresult PLUGIN_API queryInterface (const TUID tuid, void** obj) override { if (!obj) return kInvalidArgument; if (queryInterfaceImpl (tuid, *obj) || queryInterfaceImpl (tuid, *obj)) { static_cast (*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 (static_cast (this)); } //------------------------------------------------------------------------ private: template inline constexpr bool match (const TUID tuid) const noexcept { return reinterpret_cast (tuid)[0] == reinterpret_cast (getTUID ())[0] && reinterpret_cast (tuid)[1] == reinterpret_cast (getTUID ())[1]; } template inline constexpr bool queryInterfaceImpl (const TUID, void*&) const noexcept { return false; } template inline bool queryInterfaceImpl (const TUID tuid, void*& obj) noexcept { if (match (tuid) || match (tuid)) { obj = static_cast (this); return true; } obj = getInterface (tuid); return obj != nullptr; } template inline constexpr void* getInterface (const TUID) const noexcept { return nullptr; } template inline void* getInterface (const TUID tuid) noexcept { return match (tuid) ? static_cast (this) : getInterface (tuid); } }; /** This typedef enables using a custom base class with the interface implementation. */ template > using Extends = ImplementsImpl; /** This typedef provides the interface implementation. */ template > using Implements = ImplementsImpl, D, I>; /** This typedef provides the interface implementation for objects which should not be destroyed via * FUnknown::release (like singletons). */ template > using ImplementsNonDestroyable = ImplementsImpl, 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