465 lines
14 KiB
C++
465 lines
14 KiB
C++
//-----------------------------------------------------------------------------
|
|
// 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
|