476 lines
19 KiB
C
476 lines
19 KiB
C
|
//-----------------------------------------------------------------------------
|
||
|
// Project : SDK Core
|
||
|
//
|
||
|
// Category : SDK Core Interfaces
|
||
|
// Filename : pluginterfaces/base/funknown.h
|
||
|
// Created by : Steinberg, 01/2004
|
||
|
// Description : Basic Interface
|
||
|
//
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// 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/ftypes.h"
|
||
|
#include "pluginterfaces/base/smartpointer.h"
|
||
|
#include <string.h>
|
||
|
|
||
|
//------------------------------------------------------------------------
|
||
|
/*! \defgroup pluginBase Basic Interfaces
|
||
|
*/
|
||
|
//------------------------------------------------------------------------
|
||
|
|
||
|
//------------------------------------------------------------------------
|
||
|
// Unique Identifier macros
|
||
|
//------------------------------------------------------------------------
|
||
|
|
||
|
#if COM_COMPATIBLE
|
||
|
#define INLINE_UID(l1, l2, l3, l4) \
|
||
|
{ \
|
||
|
(::Steinberg::int8)((l1 & 0x000000FF) ), (::Steinberg::int8)((l1 & 0x0000FF00) >> 8), \
|
||
|
(::Steinberg::int8)((l1 & 0x00FF0000) >> 16), (::Steinberg::int8)((l1 & 0xFF000000) >> 24), \
|
||
|
(::Steinberg::int8)((l2 & 0x00FF0000) >> 16), (::Steinberg::int8)((l2 & 0xFF000000) >> 24), \
|
||
|
(::Steinberg::int8)((l2 & 0x000000FF) ), (::Steinberg::int8)((l2 & 0x0000FF00) >> 8), \
|
||
|
(::Steinberg::int8)((l3 & 0xFF000000) >> 24), (::Steinberg::int8)((l3 & 0x00FF0000) >> 16), \
|
||
|
(::Steinberg::int8)((l3 & 0x0000FF00) >> 8), (::Steinberg::int8)((l3 & 0x000000FF) ), \
|
||
|
(::Steinberg::int8)((l4 & 0xFF000000) >> 24), (::Steinberg::int8)((l4 & 0x00FF0000) >> 16), \
|
||
|
(::Steinberg::int8)((l4 & 0x0000FF00) >> 8), (::Steinberg::int8)((l4 & 0x000000FF) ) \
|
||
|
}
|
||
|
#else
|
||
|
#define INLINE_UID(l1, l2, l3, l4) \
|
||
|
{ \
|
||
|
(::Steinberg::int8)((l1 & 0xFF000000) >> 24), (::Steinberg::int8)((l1 & 0x00FF0000) >> 16), \
|
||
|
(::Steinberg::int8)((l1 & 0x0000FF00) >> 8), (::Steinberg::int8)((l1 & 0x000000FF) ), \
|
||
|
(::Steinberg::int8)((l2 & 0xFF000000) >> 24), (::Steinberg::int8)((l2 & 0x00FF0000) >> 16), \
|
||
|
(::Steinberg::int8)((l2 & 0x0000FF00) >> 8), (::Steinberg::int8)((l2 & 0x000000FF) ), \
|
||
|
(::Steinberg::int8)((l3 & 0xFF000000) >> 24), (::Steinberg::int8)((l3 & 0x00FF0000) >> 16), \
|
||
|
(::Steinberg::int8)((l3 & 0x0000FF00) >> 8), (::Steinberg::int8)((l3 & 0x000000FF) ), \
|
||
|
(::Steinberg::int8)((l4 & 0xFF000000) >> 24), (::Steinberg::int8)((l4 & 0x00FF0000) >> 16), \
|
||
|
(::Steinberg::int8)((l4 & 0x0000FF00) >> 8), (::Steinberg::int8)((l4 & 0x000000FF) ) \
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
//------------------------------------------------------------------------
|
||
|
#define DECLARE_UID(name, l1, l2, l3, l4) ::Steinberg::TUID name = INLINE_UID (l1, l2, l3, l4);
|
||
|
|
||
|
//------------------------------------------------------------------------
|
||
|
#define EXTERN_UID(name) extern const ::Steinberg::TUID name;
|
||
|
|
||
|
#ifdef INIT_CLASS_IID
|
||
|
#define DECLARE_CLASS_IID(ClassName, l1, l2, l3, l4) \
|
||
|
static const ::Steinberg::TUID ClassName##_iid = INLINE_UID (l1, l2, l3, l4); \
|
||
|
\
|
||
|
const ::Steinberg::FUID ClassName::iid (ClassName##_iid);
|
||
|
#else
|
||
|
#define DECLARE_CLASS_IID(ClassName, l1, l2, l3, l4) \
|
||
|
static const ::Steinberg::TUID ClassName##_iid = INLINE_UID (l1, l2, l3, l4);
|
||
|
#endif
|
||
|
|
||
|
#define DEF_CLASS_IID(ClassName) const ::Steinberg::FUID ClassName::iid (ClassName##_iid);
|
||
|
|
||
|
#define INLINE_UID_OF(ClassName) ClassName##_iid
|
||
|
|
||
|
#define INLINE_UID_FROM_FUID(x) \
|
||
|
INLINE_UID (x.getLong1 (), x.getLong2 (), x.getLong3 (), x.getLong4 ())
|
||
|
|
||
|
//------------------------------------------------------------------------
|
||
|
// FUnknown implementation macros
|
||
|
//------------------------------------------------------------------------
|
||
|
|
||
|
#define DECLARE_FUNKNOWN_METHODS \
|
||
|
public: \
|
||
|
virtual ::Steinberg::tresult PLUGIN_API queryInterface (const ::Steinberg::TUID _iid, void** obj) SMTG_OVERRIDE; \
|
||
|
virtual ::Steinberg::uint32 PLUGIN_API addRef () SMTG_OVERRIDE; \
|
||
|
virtual ::Steinberg::uint32 PLUGIN_API release () SMTG_OVERRIDE; \
|
||
|
protected : \
|
||
|
::Steinberg::int32 __funknownRefCount; \
|
||
|
public:
|
||
|
|
||
|
//------------------------------------------------------------------------
|
||
|
|
||
|
#define DELEGATE_REFCOUNT(ClassName) \
|
||
|
public: \
|
||
|
virtual ::Steinberg::uint32 PLUGIN_API addRef () SMTG_OVERRIDE { return ClassName::addRef (); } \
|
||
|
virtual ::Steinberg::uint32 PLUGIN_API release () SMTG_OVERRIDE { return ClassName::release (); }
|
||
|
|
||
|
//------------------------------------------------------------------------
|
||
|
#define IMPLEMENT_REFCOUNT(ClassName) \
|
||
|
::Steinberg::uint32 PLUGIN_API ClassName::addRef () \
|
||
|
{ \
|
||
|
return ::Steinberg::FUnknownPrivate::atomicAdd (__funknownRefCount, 1); \
|
||
|
} \
|
||
|
::Steinberg::uint32 PLUGIN_API ClassName::release () \
|
||
|
{ \
|
||
|
if (::Steinberg::FUnknownPrivate::atomicAdd (__funknownRefCount, -1) == 0) \
|
||
|
{ \
|
||
|
delete this; \
|
||
|
return 0; \
|
||
|
} \
|
||
|
return __funknownRefCount; \
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------
|
||
|
#define FUNKNOWN_CTOR { __funknownRefCount = 1; }
|
||
|
#define FUNKNOWN_DTOR
|
||
|
|
||
|
//------------------------------------------------------------------------
|
||
|
#define QUERY_INTERFACE(iid, obj, InterfaceIID, InterfaceName) \
|
||
|
if (::Steinberg::FUnknownPrivate::iidEqual (iid, InterfaceIID)) \
|
||
|
{ \
|
||
|
addRef (); \
|
||
|
*obj = static_cast< InterfaceName* >(this); \
|
||
|
return ::Steinberg::kResultOk; \
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------
|
||
|
#define IMPLEMENT_QUERYINTERFACE(ClassName, InterfaceName, ClassIID) \
|
||
|
::Steinberg::tresult PLUGIN_API ClassName::queryInterface (const ::Steinberg::TUID _iid, void** obj)\
|
||
|
{ \
|
||
|
QUERY_INTERFACE (_iid, obj, ::Steinberg::FUnknown::iid, InterfaceName) \
|
||
|
QUERY_INTERFACE (_iid, obj, ClassIID, InterfaceName) \
|
||
|
*obj = nullptr; \
|
||
|
return ::Steinberg::kNoInterface; \
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------
|
||
|
#define IMPLEMENT_FUNKNOWN_METHODS(ClassName,InterfaceName,ClassIID) \
|
||
|
IMPLEMENT_REFCOUNT (ClassName) \
|
||
|
IMPLEMENT_QUERYINTERFACE (ClassName, InterfaceName, ClassIID)
|
||
|
|
||
|
//------------------------------------------------------------------------
|
||
|
// Result Codes
|
||
|
//------------------------------------------------------------------------
|
||
|
|
||
|
namespace Steinberg {
|
||
|
|
||
|
//------------------------------------------------------------------------
|
||
|
#if COM_COMPATIBLE
|
||
|
#if SMTG_OS_WINDOWS
|
||
|
enum
|
||
|
{
|
||
|
kNoInterface = static_cast<tresult>(0x80004002L), // E_NOINTERFACE
|
||
|
kResultOk = static_cast<tresult>(0x00000000L), // S_OK
|
||
|
kResultTrue = kResultOk,
|
||
|
kResultFalse = static_cast<tresult>(0x00000001L), // S_FALSE
|
||
|
kInvalidArgument = static_cast<tresult>(0x80070057L), // E_INVALIDARG
|
||
|
kNotImplemented = static_cast<tresult>(0x80004001L), // E_NOTIMPL
|
||
|
kInternalError = static_cast<tresult>(0x80004005L), // E_FAIL
|
||
|
kNotInitialized = static_cast<tresult>(0x8000FFFFL), // E_UNEXPECTED
|
||
|
kOutOfMemory = static_cast<tresult>(0x8007000EL) // E_OUTOFMEMORY
|
||
|
};
|
||
|
#else
|
||
|
enum
|
||
|
{
|
||
|
kNoInterface = static_cast<tresult>(0x80000004L), // E_NOINTERFACE
|
||
|
kResultOk = static_cast<tresult>(0x00000000L), // S_OK
|
||
|
kResultTrue = kResultOk,
|
||
|
kResultFalse = static_cast<tresult>(0x00000001L), // S_FALSE
|
||
|
kInvalidArgument = static_cast<tresult>(0x80000003L), // E_INVALIDARG
|
||
|
kNotImplemented = static_cast<tresult>(0x80000001L), // E_NOTIMPL
|
||
|
kInternalError = static_cast<tresult>(0x80000008L), // E_FAIL
|
||
|
kNotInitialized = static_cast<tresult>(0x8000FFFFL), // E_UNEXPECTED
|
||
|
kOutOfMemory = static_cast<tresult>(0x80000002L) // E_OUTOFMEMORY
|
||
|
};
|
||
|
#endif
|
||
|
#else
|
||
|
enum
|
||
|
{
|
||
|
kNoInterface = -1,
|
||
|
kResultOk,
|
||
|
kResultTrue = kResultOk,
|
||
|
kResultFalse,
|
||
|
kInvalidArgument,
|
||
|
kNotImplemented,
|
||
|
kInternalError,
|
||
|
kNotInitialized,
|
||
|
kOutOfMemory
|
||
|
};
|
||
|
#endif
|
||
|
|
||
|
//------------------------------------------------------------------------
|
||
|
typedef int64 LARGE_INT; // obsolete
|
||
|
|
||
|
//------------------------------------------------------------------------
|
||
|
// FUID class declaration
|
||
|
//------------------------------------------------------------------------
|
||
|
typedef int8 TUID[16]; ///< plain UID type
|
||
|
|
||
|
//------------------------------------------------------------------------
|
||
|
/* FUnknown private */
|
||
|
namespace FUnknownPrivate {
|
||
|
SMTG_ALWAYS_INLINE bool iidEqual (const void* iid1, const void* iid2)
|
||
|
{
|
||
|
const uint64* p1 = reinterpret_cast<const uint64*> (iid1);
|
||
|
const uint64* p2 = reinterpret_cast<const uint64*> (iid2);
|
||
|
return p1[0] == p2[0] && p1[1] == p2[1];
|
||
|
}
|
||
|
|
||
|
int32 PLUGIN_API atomicAdd (int32& value, int32 amount);
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------
|
||
|
/** Handling 16 Byte Globally Unique Identifiers.
|
||
|
\ingroup pluginBase
|
||
|
|
||
|
Each interface declares its identifier as static member inside the interface
|
||
|
namespace (e.g. FUnknown::iid).
|
||
|
*/
|
||
|
//------------------------------------------------------------------------
|
||
|
class FUID
|
||
|
{
|
||
|
public:
|
||
|
//------------------------------------------------------------------------
|
||
|
FUID ();
|
||
|
FUID (uint32 l1, uint32 l2, uint32 l3, uint32 l4);
|
||
|
FUID (const FUID&);
|
||
|
virtual ~FUID () {}
|
||
|
|
||
|
#if SMTG_CPP11_STDLIBSUPPORT
|
||
|
FUID (FUID&& other);
|
||
|
FUID& operator= (FUID&& other);
|
||
|
#endif
|
||
|
|
||
|
/** Generates a new Unique Identifier (UID).
|
||
|
Will return true for success. If the return value is false, either no
|
||
|
UID is generated or the UID is not guaranteed to be unique worldwide. */
|
||
|
bool generate ();
|
||
|
|
||
|
/** Checks if the UID data is valid.
|
||
|
The default constructor initializes the memory with zeros. */
|
||
|
bool isValid () const;
|
||
|
|
||
|
FUID& operator = (const FUID& f);
|
||
|
bool operator == (const FUID& f) const { return ::Steinberg::FUnknownPrivate::iidEqual (data, f.data); }
|
||
|
bool operator < (const FUID& f) const { return memcmp (data, f.data, sizeof (TUID)) < 0; }
|
||
|
bool operator != (const FUID& f) const { return !::Steinberg::FUnknownPrivate::iidEqual (data, f.data); }
|
||
|
|
||
|
uint32 getLong1 () const;
|
||
|
uint32 getLong2 () const;
|
||
|
uint32 getLong3 () const;
|
||
|
uint32 getLong4 () const;
|
||
|
|
||
|
void from4Int (uint32 d1, uint32 d2, uint32 d3, uint32 d4);
|
||
|
void to4Int (uint32& d1, uint32& d2, uint32& d3, uint32& d4) const;
|
||
|
|
||
|
typedef char8 String[64];
|
||
|
|
||
|
/** Converts UID to a string.
|
||
|
The string will be 32 characters long, representing the hexadecimal values
|
||
|
of each data byte (e.g. "9127BE30160E4BB69966670AA6087880").
|
||
|
|
||
|
Typical use-case is:
|
||
|
\code
|
||
|
char8[33] strUID = {0};
|
||
|
FUID uid;
|
||
|
if (uid.generate ())
|
||
|
uid.toString (strUID);
|
||
|
\endcode
|
||
|
*/
|
||
|
void toString (char8* string) const;
|
||
|
|
||
|
/** Sets the UID data from a string.
|
||
|
The string has to be 32 characters long, where each character-pair is
|
||
|
the ASCII-encoded hexadecimal value of the corresponding data byte. */
|
||
|
bool fromString (const char8* string);
|
||
|
|
||
|
/** Converts UID to a string in Microsoft® OLE format.
|
||
|
(e.g. "{c200e360-38c5-11ce-ae62-08002b2b79ef}") */
|
||
|
void toRegistryString (char8* string) const;
|
||
|
|
||
|
/** Sets the UID data from a string in Microsoft® OLE format. */
|
||
|
bool fromRegistryString (const char8* string);
|
||
|
|
||
|
enum UIDPrintStyle
|
||
|
{
|
||
|
kINLINE_UID, ///< "INLINE_UID (0x00000000, 0x00000000, 0x00000000, 0x00000000)"
|
||
|
kDECLARE_UID, ///< "DECLARE_UID (0x00000000, 0x00000000, 0x00000000, 0x00000000)"
|
||
|
kFUID, ///< "FUID (0x00000000, 0x00000000, 0x00000000, 0x00000000)"
|
||
|
kCLASS_UID ///< "DECLARE_CLASS_IID (Interface, 0x00000000, 0x00000000, 0x00000000, 0x00000000)"
|
||
|
};
|
||
|
/** Prints the UID to a string (or debug output if string is NULL).
|
||
|
\param string is the output string if not NULL.
|
||
|
\param style can be chosen from the FUID::UIDPrintStyle enumeration. */
|
||
|
void print (char8* string = nullptr, int32 style = kINLINE_UID) const;
|
||
|
|
||
|
template <size_t N>
|
||
|
inline explicit FUID (const int8 (&uid)[N])
|
||
|
{
|
||
|
#if SMTG_CPP11_STDLIBSUPPORT
|
||
|
static_assert (N == sizeof (TUID), "only TUID allowed");
|
||
|
#endif
|
||
|
memcpy (data, uid, sizeof (TUID));
|
||
|
}
|
||
|
inline void toTUID (TUID result) const { memcpy (result, data, sizeof (TUID)); }
|
||
|
inline operator const TUID& () const { return data; }
|
||
|
inline const TUID& toTUID () const { return data; }
|
||
|
|
||
|
static FUID fromTUID (const TUID uid)
|
||
|
{
|
||
|
FUID res;
|
||
|
if (uid)
|
||
|
memcpy (res.data, uid, sizeof (TUID));
|
||
|
return res;
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------
|
||
|
protected:
|
||
|
TUID data;
|
||
|
};
|
||
|
|
||
|
#if SMTG_CPP11_STDLIBSUPPORT
|
||
|
template <typename T>
|
||
|
inline bool operator== (const FUID& f1, T f2)
|
||
|
{
|
||
|
static_assert (
|
||
|
std::is_same<typename std::remove_cv<T>::type, FUID>::value,
|
||
|
"Do not compare a FUID with a TUID directly. Either convert the TUID to a FUID and compare them or use FUnknownPrivate::iidEqual");
|
||
|
return f1.operator== (f2);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
//------------------------------------------------------------------------
|
||
|
// FUnknown
|
||
|
//------------------------------------------------------------------------
|
||
|
/** The basic interface of all interfaces.
|
||
|
\ingroup pluginBase
|
||
|
|
||
|
- The FUnknown::queryInterface method is used to retrieve pointers to other
|
||
|
interfaces of the object.
|
||
|
- FUnknown::addRef and FUnknown::release manage the lifetime of the object.
|
||
|
If no more references exist, the object is destroyed in memory.
|
||
|
|
||
|
Interfaces are identified by 16 byte Globally Unique Identifiers.
|
||
|
The SDK provides a class called FUID for this purpose.
|
||
|
|
||
|
\ref howtoClass */
|
||
|
//------------------------------------------------------------------------
|
||
|
class FUnknown
|
||
|
{
|
||
|
public:
|
||
|
|
||
|
//------------------------------------------------------------------------
|
||
|
/** Query for a pointer to the specified interface.
|
||
|
Returns kResultOk on success or kNoInterface if the object does not implement the interface.
|
||
|
The object has to call addRef when returning an interface.
|
||
|
\param _iid : (in) 16 Byte interface identifier (-> FUID)
|
||
|
\param obj : (out) On return, *obj points to the requested interface */
|
||
|
virtual tresult PLUGIN_API queryInterface (const TUID _iid, void** obj) = 0;
|
||
|
|
||
|
/** Adds a reference and return the new reference count.
|
||
|
\par Remarks:
|
||
|
The initial reference count after creating an object is 1. */
|
||
|
virtual uint32 PLUGIN_API addRef () = 0;
|
||
|
|
||
|
/** Releases a reference and return the new reference count.
|
||
|
If the reference count reaches zero, the object will be destroyed in memory. */
|
||
|
virtual uint32 PLUGIN_API release () = 0;
|
||
|
|
||
|
//------------------------------------------------------------------------
|
||
|
static const FUID iid;
|
||
|
//------------------------------------------------------------------------
|
||
|
};
|
||
|
|
||
|
|
||
|
DECLARE_CLASS_IID (FUnknown, 0x00000000, 0x00000000, 0xC0000000, 0x00000046)
|
||
|
|
||
|
//------------------------------------------------------------------------
|
||
|
// FUnknownPtr
|
||
|
//------------------------------------------------------------------------
|
||
|
/** FUnknownPtr - automatic interface conversion and smart pointer in one.
|
||
|
This template class can be used for interface conversion like this:
|
||
|
\code
|
||
|
IPtr<IPath> path = owned (FHostCreate (IPath, hostClasses));
|
||
|
FUnknownPtr<IPath2> path2 (path); // does a query interface for IPath2
|
||
|
if (path2)
|
||
|
...
|
||
|
\endcode
|
||
|
*/
|
||
|
//------------------------------------------------------------------------
|
||
|
template <class I>
|
||
|
class FUnknownPtr : public IPtr<I>
|
||
|
{
|
||
|
public:
|
||
|
//------------------------------------------------------------------------
|
||
|
inline FUnknownPtr (FUnknown* unknown); // query interface
|
||
|
inline FUnknownPtr (const FUnknownPtr& p) : IPtr<I> (p) {}
|
||
|
inline FUnknownPtr () {}
|
||
|
|
||
|
inline FUnknownPtr& operator= (const FUnknownPtr& p)
|
||
|
{
|
||
|
IPtr<I>::operator= (p);
|
||
|
return *this;
|
||
|
}
|
||
|
inline I* operator= (FUnknown* unknown);
|
||
|
inline I* getInterface () { return this->ptr; }
|
||
|
};
|
||
|
|
||
|
//------------------------------------------------------------------------
|
||
|
template <class I>
|
||
|
inline FUnknownPtr<I>::FUnknownPtr (FUnknown* unknown)
|
||
|
{
|
||
|
if (unknown && unknown->queryInterface (I::iid, (void**)&this->ptr) != kResultOk)
|
||
|
this->ptr = 0;
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------
|
||
|
template <class I>
|
||
|
inline I* FUnknownPtr<I>::operator= (FUnknown* unknown)
|
||
|
{
|
||
|
I* newPtr = 0;
|
||
|
if (unknown && unknown->queryInterface (I::iid, (void**)&newPtr) == kResultOk)
|
||
|
{
|
||
|
OPtr<I> rel (newPtr);
|
||
|
return IPtr<I>::operator= (newPtr);
|
||
|
}
|
||
|
|
||
|
return IPtr<I>::operator= (0);
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------
|
||
|
// FReleaser (obsolete)
|
||
|
//------------------------------------------------------------------------
|
||
|
/** Release an interface using automatic object (obsolete).
|
||
|
This class is obsolete and is only kept for compatibility.
|
||
|
The replacement for FReleaser is OPtr.
|
||
|
|
||
|
Usage example with FReleaser:
|
||
|
\code
|
||
|
void someFunction ()
|
||
|
{
|
||
|
IPath* path = pathCreateMethod ();
|
||
|
FReleaser releaser (path);
|
||
|
.... do something with path...
|
||
|
.... path not used anymore, releaser will destroy it when leaving function scope
|
||
|
}
|
||
|
\endcode
|
||
|
Usage example with OPtr:
|
||
|
\code
|
||
|
void someFunction ()
|
||
|
{
|
||
|
OPtr<IPath> path = pathCreateMethod ();
|
||
|
.... do something with path...
|
||
|
.... path not used anymore, OPtr will destroy it when leaving function scope
|
||
|
}
|
||
|
\endcode
|
||
|
*/
|
||
|
//------------------------------------------------------------------------
|
||
|
struct FReleaser
|
||
|
{
|
||
|
FReleaser (FUnknown* u) : u (u) {}
|
||
|
~FReleaser ()
|
||
|
{
|
||
|
if (u)
|
||
|
u->release ();
|
||
|
}
|
||
|
FUnknown* u;
|
||
|
};
|
||
|
|
||
|
//------------------------------------------------------------------------
|
||
|
} // namespace Steinberg
|