Audio plugin host https://kx.studio/carla
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

551 lines
21KB

  1. //-----------------------------------------------------------------------------
  2. // Project : SDK Core
  3. //
  4. // Category : SDK Core Interfaces
  5. // Filename : pluginterfaces/base/funknown.h
  6. // Created by : Steinberg, 01/2004
  7. // Description : Basic Interface
  8. //
  9. //-----------------------------------------------------------------------------
  10. // This file is part of a Steinberg SDK. It is subject to the license terms
  11. // in the LICENSE file found in the top-level directory of this distribution
  12. // and at www.steinberg.net/sdklicenses.
  13. // No part of the SDK, including this file, may be copied, modified, propagated,
  14. // or distributed except according to the terms contained in the LICENSE file.
  15. //-----------------------------------------------------------------------------
  16. #pragma once
  17. #include "pluginterfaces/base/fplatform.h"
  18. #include "pluginterfaces/base/ftypes.h"
  19. #include "pluginterfaces/base/smartpointer.h"
  20. #include <cstring>
  21. #if SMTG_CPP11_STDLIBSUPPORT
  22. #include <type_traits>
  23. #endif
  24. //------------------------------------------------------------------------
  25. /*! \defgroup pluginBase Basic Interfaces
  26. */
  27. //------------------------------------------------------------------------
  28. // Unique Identifier macros
  29. //------------------------------------------------------------------------
  30. #if COM_COMPATIBLE
  31. #define INLINE_UID(l1, l2, l3, l4) \
  32. { \
  33. (::Steinberg::int8)(((::Steinberg::uint32)(l1) & 0x000000FF) ), (::Steinberg::int8)(((::Steinberg::uint32)(l1) & 0x0000FF00) >> 8), \
  34. (::Steinberg::int8)(((::Steinberg::uint32)(l1) & 0x00FF0000) >> 16), (::Steinberg::int8)(((::Steinberg::uint32)(l1) & 0xFF000000) >> 24), \
  35. (::Steinberg::int8)(((::Steinberg::uint32)(l2) & 0x00FF0000) >> 16), (::Steinberg::int8)(((::Steinberg::uint32)(l2) & 0xFF000000) >> 24), \
  36. (::Steinberg::int8)(((::Steinberg::uint32)(l2) & 0x000000FF) ), (::Steinberg::int8)(((::Steinberg::uint32)(l2) & 0x0000FF00) >> 8), \
  37. (::Steinberg::int8)(((::Steinberg::uint32)(l3) & 0xFF000000) >> 24), (::Steinberg::int8)(((::Steinberg::uint32)(l3) & 0x00FF0000) >> 16), \
  38. (::Steinberg::int8)(((::Steinberg::uint32)(l3) & 0x0000FF00) >> 8), (::Steinberg::int8)(((::Steinberg::uint32)(l3) & 0x000000FF) ), \
  39. (::Steinberg::int8)(((::Steinberg::uint32)(l4) & 0xFF000000) >> 24), (::Steinberg::int8)(((::Steinberg::uint32)(l4) & 0x00FF0000) >> 16), \
  40. (::Steinberg::int8)(((::Steinberg::uint32)(l4) & 0x0000FF00) >> 8), (::Steinberg::int8)(((::Steinberg::uint32)(l4) & 0x000000FF) ) \
  41. }
  42. #else
  43. #define INLINE_UID(l1, l2, l3, l4) \
  44. { \
  45. (::Steinberg::int8)(((::Steinberg::uint32)(l1) & 0xFF000000) >> 24), (::Steinberg::int8)(((::Steinberg::uint32)(l1) & 0x00FF0000) >> 16), \
  46. (::Steinberg::int8)(((::Steinberg::uint32)(l1) & 0x0000FF00) >> 8), (::Steinberg::int8)(((::Steinberg::uint32)(l1) & 0x000000FF) ), \
  47. (::Steinberg::int8)(((::Steinberg::uint32)(l2) & 0xFF000000) >> 24), (::Steinberg::int8)(((::Steinberg::uint32)(l2) & 0x00FF0000) >> 16), \
  48. (::Steinberg::int8)(((::Steinberg::uint32)(l2) & 0x0000FF00) >> 8), (::Steinberg::int8)(((::Steinberg::uint32)(l2) & 0x000000FF) ), \
  49. (::Steinberg::int8)(((::Steinberg::uint32)(l3) & 0xFF000000) >> 24), (::Steinberg::int8)(((::Steinberg::uint32)(l3) & 0x00FF0000) >> 16), \
  50. (::Steinberg::int8)(((::Steinberg::uint32)(l3) & 0x0000FF00) >> 8), (::Steinberg::int8)(((::Steinberg::uint32)(l3) & 0x000000FF) ), \
  51. (::Steinberg::int8)(((::Steinberg::uint32)(l4) & 0xFF000000) >> 24), (::Steinberg::int8)(((::Steinberg::uint32)(l4) & 0x00FF0000) >> 16), \
  52. (::Steinberg::int8)(((::Steinberg::uint32)(l4) & 0x0000FF00) >> 8), (::Steinberg::int8)(((::Steinberg::uint32)(l4) & 0x000000FF) ) \
  53. }
  54. #endif
  55. //------------------------------------------------------------------------
  56. #define DECLARE_UID(name, l1, l2, l3, l4) ::Steinberg::TUID name = INLINE_UID (l1, l2, l3, l4);
  57. //------------------------------------------------------------------------
  58. #define EXTERN_UID(name) extern const ::Steinberg::TUID name;
  59. #ifdef INIT_CLASS_IID
  60. #define DECLARE_CLASS_IID(ClassName, l1, l2, l3, l4) \
  61. static const ::Steinberg::TUID ClassName##_iid = INLINE_UID (l1, l2, l3, l4); \
  62. \
  63. const ::Steinberg::FUID ClassName::iid (ClassName##_iid);
  64. #else
  65. #define DECLARE_CLASS_IID(ClassName, l1, l2, l3, l4) \
  66. static const ::Steinberg::TUID ClassName##_iid = INLINE_UID (l1, l2, l3, l4);
  67. #endif
  68. #define DEF_CLASS_IID(ClassName) const ::Steinberg::FUID ClassName::iid (ClassName##_iid);
  69. #define INLINE_UID_OF(ClassName) ClassName##_iid
  70. #define INLINE_UID_FROM_FUID(x) \
  71. INLINE_UID (x.getLong1 (), x.getLong2 (), x.getLong3 (), x.getLong4 ())
  72. //------------------------------------------------------------------------
  73. // FUnknown implementation macros
  74. //------------------------------------------------------------------------
  75. #define DECLARE_FUNKNOWN_METHODS \
  76. public: \
  77. virtual ::Steinberg::tresult PLUGIN_API queryInterface (const ::Steinberg::TUID _iid, void** obj) SMTG_OVERRIDE; \
  78. virtual ::Steinberg::uint32 PLUGIN_API addRef () SMTG_OVERRIDE; \
  79. virtual ::Steinberg::uint32 PLUGIN_API release () SMTG_OVERRIDE; \
  80. protected : \
  81. ::Steinberg::int32 __funknownRefCount; \
  82. public:
  83. //------------------------------------------------------------------------
  84. #define DELEGATE_REFCOUNT(ClassName) \
  85. public: \
  86. virtual ::Steinberg::uint32 PLUGIN_API addRef () SMTG_OVERRIDE { return ClassName::addRef (); } \
  87. virtual ::Steinberg::uint32 PLUGIN_API release () SMTG_OVERRIDE { return ClassName::release (); }
  88. //------------------------------------------------------------------------
  89. #define IMPLEMENT_REFCOUNT(ClassName) \
  90. ::Steinberg::uint32 PLUGIN_API ClassName::addRef () \
  91. { \
  92. return ::Steinberg::FUnknownPrivate::atomicAdd (__funknownRefCount, 1); \
  93. } \
  94. ::Steinberg::uint32 PLUGIN_API ClassName::release () \
  95. { \
  96. if (::Steinberg::FUnknownPrivate::atomicAdd (__funknownRefCount, -1) == 0) \
  97. { \
  98. delete this; \
  99. return 0; \
  100. } \
  101. return __funknownRefCount; \
  102. }
  103. //------------------------------------------------------------------------
  104. #define FUNKNOWN_CTOR { __funknownRefCount = 1; }
  105. #if SMTG_FUNKNOWN_DTOR_ASSERT
  106. #include <cassert>
  107. #define FUNKNOWN_DTOR { assert (__funknownRefCount == 0); }
  108. #else
  109. #define FUNKNOWN_DTOR
  110. #endif
  111. //------------------------------------------------------------------------
  112. #define QUERY_INTERFACE(iid, obj, InterfaceIID, InterfaceName) \
  113. if (::Steinberg::FUnknownPrivate::iidEqual (iid, InterfaceIID)) \
  114. { \
  115. addRef (); \
  116. *obj = static_cast< InterfaceName* >(this); \
  117. return ::Steinberg::kResultOk; \
  118. }
  119. //------------------------------------------------------------------------
  120. #define IMPLEMENT_QUERYINTERFACE(ClassName, InterfaceName, ClassIID) \
  121. ::Steinberg::tresult PLUGIN_API ClassName::queryInterface (const ::Steinberg::TUID _iid, void** obj)\
  122. { \
  123. QUERY_INTERFACE (_iid, obj, ::Steinberg::FUnknown::iid, InterfaceName) \
  124. QUERY_INTERFACE (_iid, obj, ClassIID, InterfaceName) \
  125. *obj = nullptr; \
  126. return ::Steinberg::kNoInterface; \
  127. }
  128. //------------------------------------------------------------------------
  129. #define IMPLEMENT_FUNKNOWN_METHODS(ClassName,InterfaceName,ClassIID) \
  130. IMPLEMENT_REFCOUNT (ClassName) \
  131. IMPLEMENT_QUERYINTERFACE (ClassName, InterfaceName, ClassIID)
  132. //------------------------------------------------------------------------
  133. // Result Codes
  134. //------------------------------------------------------------------------
  135. namespace Steinberg {
  136. //------------------------------------------------------------------------
  137. #if COM_COMPATIBLE
  138. #if SMTG_OS_WINDOWS
  139. enum
  140. {
  141. kNoInterface = static_cast<tresult>(0x80004002L), // E_NOINTERFACE
  142. kResultOk = static_cast<tresult>(0x00000000L), // S_OK
  143. kResultTrue = kResultOk,
  144. kResultFalse = static_cast<tresult>(0x00000001L), // S_FALSE
  145. kInvalidArgument = static_cast<tresult>(0x80070057L), // E_INVALIDARG
  146. kNotImplemented = static_cast<tresult>(0x80004001L), // E_NOTIMPL
  147. kInternalError = static_cast<tresult>(0x80004005L), // E_FAIL
  148. kNotInitialized = static_cast<tresult>(0x8000FFFFL), // E_UNEXPECTED
  149. kOutOfMemory = static_cast<tresult>(0x8007000EL) // E_OUTOFMEMORY
  150. };
  151. #else
  152. enum
  153. {
  154. kNoInterface = static_cast<tresult>(0x80000004L), // E_NOINTERFACE
  155. kResultOk = static_cast<tresult>(0x00000000L), // S_OK
  156. kResultTrue = kResultOk,
  157. kResultFalse = static_cast<tresult>(0x00000001L), // S_FALSE
  158. kInvalidArgument = static_cast<tresult>(0x80000003L), // E_INVALIDARG
  159. kNotImplemented = static_cast<tresult>(0x80000001L), // E_NOTIMPL
  160. kInternalError = static_cast<tresult>(0x80000008L), // E_FAIL
  161. kNotInitialized = static_cast<tresult>(0x8000FFFFL), // E_UNEXPECTED
  162. kOutOfMemory = static_cast<tresult>(0x80000002L) // E_OUTOFMEMORY
  163. };
  164. #endif
  165. #else
  166. enum
  167. {
  168. kNoInterface = -1,
  169. kResultOk,
  170. kResultTrue = kResultOk,
  171. kResultFalse,
  172. kInvalidArgument,
  173. kNotImplemented,
  174. kInternalError,
  175. kNotInitialized,
  176. kOutOfMemory
  177. };
  178. #endif
  179. //------------------------------------------------------------------------
  180. typedef int64 LARGE_INT; // obsolete
  181. //------------------------------------------------------------------------
  182. // FUID class declaration
  183. //------------------------------------------------------------------------
  184. typedef int8 TUID[16]; ///< plain UID type
  185. //------------------------------------------------------------------------
  186. /* FUnknown private */
  187. namespace FUnknownPrivate {
  188. SMTG_ALWAYS_INLINE bool iidEqual (const void* iid1, const void* iid2)
  189. {
  190. const uint64* p1 = reinterpret_cast<const uint64*> (iid1);
  191. const uint64* p2 = reinterpret_cast<const uint64*> (iid2);
  192. return p1[0] == p2[0] && p1[1] == p2[1];
  193. }
  194. int32 PLUGIN_API atomicAdd (int32& value, int32 amount);
  195. }
  196. //------------------------------------------------------------------------
  197. /** Handling 16 Byte Globally Unique Identifiers.
  198. \ingroup pluginBase
  199. Each interface declares its identifier as static member inside the interface
  200. namespace (e.g. FUnknown::iid).
  201. */
  202. class FUID
  203. {
  204. public:
  205. //------------------------------------------------------------------------
  206. FUID ();
  207. FUID (uint32 l1, uint32 l2, uint32 l3, uint32 l4);
  208. FUID (const FUID&);
  209. virtual ~FUID () {}
  210. #if SMTG_CPP11_STDLIBSUPPORT
  211. FUID (FUID&& other);
  212. FUID& operator= (FUID&& other);
  213. #endif
  214. /** Generates a new Unique Identifier (UID).
  215. Will return true for success. If the return value is false, either no
  216. UID is generated or the UID is not guaranteed to be unique worldwide. */
  217. bool generate ();
  218. /** Checks if the UID data is valid.
  219. The default constructor initializes the memory with zeros. */
  220. bool isValid () const;
  221. FUID& operator = (const FUID& f);
  222. bool operator == (const FUID& f) const { return ::Steinberg::FUnknownPrivate::iidEqual (data, f.data); }
  223. bool operator < (const FUID& f) const { return memcmp (data, f.data, sizeof (TUID)) < 0; }
  224. bool operator != (const FUID& f) const { return !::Steinberg::FUnknownPrivate::iidEqual (data, f.data); }
  225. uint32 getLong1 () const;
  226. uint32 getLong2 () const;
  227. uint32 getLong3 () const;
  228. uint32 getLong4 () const;
  229. void from4Int (uint32 d1, uint32 d2, uint32 d3, uint32 d4);
  230. void to4Int (uint32& d1, uint32& d2, uint32& d3, uint32& d4) const;
  231. typedef char8 String[33];
  232. /** Converts UID to a string.
  233. The string will be 32 characters long, representing the hexadecimal values
  234. of each data byte (e.g. "9127BE30160E4BB69966670AA6087880").
  235. Typical use-case is:
  236. \code{.cpp}
  237. char8[33] strUID = {0};
  238. FUID uid;
  239. if (uid.generate ())
  240. uid.toString (strUID);
  241. \endcode
  242. */
  243. void toString (char8* string) const;
  244. /** Sets the UID data from a string.
  245. The string has to be 32 characters long, where each character-pair is
  246. the ASCII-encoded hexadecimal value of the corresponding data byte. */
  247. bool fromString (const char8* string);
  248. /** Converts UID to a string in Microsoft(R) OLE format.
  249. (e.g. "{c200e360-38c5-11ce-ae62-08002b2b79ef}") */
  250. void toRegistryString (char8* string) const;
  251. /** Sets the UID data from a string in Microsoft(R) OLE format. */
  252. bool fromRegistryString (const char8* string);
  253. enum UIDPrintStyle
  254. {
  255. kINLINE_UID, ///< "INLINE_UID (0x00000000, 0x00000000, 0x00000000, 0x00000000)"
  256. kDECLARE_UID, ///< "DECLARE_UID (0x00000000, 0x00000000, 0x00000000, 0x00000000)"
  257. kFUID, ///< "FUID (0x00000000, 0x00000000, 0x00000000, 0x00000000)"
  258. kCLASS_UID ///< "DECLARE_CLASS_IID (Interface, 0x00000000, 0x00000000, 0x00000000, 0x00000000)"
  259. };
  260. /** Prints the UID to a string (or debug output if string is NULL).
  261. \param string is the output string if not NULL.
  262. \param style can be chosen from the FUID::UIDPrintStyle enumeration. */
  263. void print (char8* string = nullptr, int32 style = kINLINE_UID) const;
  264. template <size_t N>
  265. inline explicit FUID (const int8 (&uid)[N])
  266. {
  267. #if SMTG_CPP11_STDLIBSUPPORT
  268. static_assert (N == sizeof (TUID), "only TUID allowed");
  269. #endif
  270. memcpy (data, uid, sizeof (TUID));
  271. }
  272. inline void toTUID (TUID result) const { memcpy (result, data, sizeof (TUID)); }
  273. inline operator const TUID& () const { return data; }
  274. inline const TUID& toTUID () const { return data; }
  275. static FUID fromTUID (const TUID uid)
  276. {
  277. FUID res;
  278. if (uid)
  279. memcpy (res.data, uid, sizeof (TUID));
  280. return res;
  281. }
  282. //------------------------------------------------------------------------
  283. protected:
  284. TUID data;
  285. };
  286. #if SMTG_CPP11_STDLIBSUPPORT
  287. template <typename T>
  288. inline bool operator== (const FUID& f1, T f2)
  289. {
  290. static_assert (
  291. std::is_same<typename std::remove_cv<T>::type, FUID>::value,
  292. "Do not compare a FUID with a TUID directly. Either convert the TUID to a FUID and compare them or use FUnknownPrivate::iidEqual");
  293. return f1.operator== (f2);
  294. }
  295. #endif
  296. //------------------------------------------------------------------------
  297. // FUnknown
  298. //------------------------------------------------------------------------
  299. /** The basic interface of all interfaces.
  300. \ingroup pluginBase
  301. - The FUnknown::queryInterface method is used to retrieve pointers to other
  302. interfaces of the object.
  303. - FUnknown::addRef and FUnknown::release manage the lifetime of the object.
  304. If no more references exist, the object is destroyed in memory.
  305. Interfaces are identified by 16 byte Globally Unique Identifiers.
  306. The SDK provides a class called FUID for this purpose.
  307. \ref howtoClass
  308. */
  309. class FUnknown
  310. {
  311. public:
  312. //------------------------------------------------------------------------
  313. /** Query for a pointer to the specified interface.
  314. Returns kResultOk on success or kNoInterface if the object does not implement the interface.
  315. The object has to call addRef when returning an interface.
  316. \param _iid : (in) 16 Byte interface identifier (-> FUID)
  317. \param obj : (out) On return, *obj points to the requested interface */
  318. virtual tresult PLUGIN_API queryInterface (const TUID _iid, void** obj) = 0;
  319. /** Adds a reference and returns the new reference count.
  320. \par Remarks:
  321. The initial reference count after creating an object is 1. */
  322. virtual uint32 PLUGIN_API addRef () = 0;
  323. /** Releases a reference and returns the new reference count.
  324. If the reference count reaches zero, the object will be destroyed in memory. */
  325. virtual uint32 PLUGIN_API release () = 0;
  326. //------------------------------------------------------------------------
  327. static const FUID iid;
  328. //------------------------------------------------------------------------
  329. };
  330. DECLARE_CLASS_IID (FUnknown, 0x00000000, 0x00000000, 0xC0000000, 0x00000046)
  331. //------------------------------------------------------------------------
  332. // FUnknownPtr
  333. //------------------------------------------------------------------------
  334. /** FUnknownPtr - automatic interface conversion and smart pointer in one.
  335. This template class can be used for interface conversion like this:
  336. \code{.cpp}
  337. IPtr<IPath> path = owned (FHostCreate (IPath, hostClasses));
  338. FUnknownPtr<IPath2> path2 (path); // does a query interface for IPath2
  339. if (path2)
  340. ...
  341. \endcode
  342. */
  343. template <class I>
  344. class FUnknownPtr : public IPtr<I>
  345. {
  346. public:
  347. //------------------------------------------------------------------------
  348. inline FUnknownPtr (FUnknown* unknown); // query interface
  349. inline FUnknownPtr (const FUnknownPtr& p) : IPtr<I> (p) {}
  350. inline FUnknownPtr () {}
  351. inline FUnknownPtr& operator= (const FUnknownPtr& p)
  352. {
  353. IPtr<I>::operator= (p);
  354. return *this;
  355. }
  356. inline I* operator= (FUnknown* unknown);
  357. inline I* getInterface () { return this->ptr; }
  358. #if SMTG_CPP11_STDLIBSUPPORT
  359. inline FUnknownPtr (FUnknownPtr&& p) SMTG_NOEXCEPT : IPtr<I> (std::move (p)) {}
  360. inline FUnknownPtr& operator= (FUnknownPtr&& p) SMTG_NOEXCEPT
  361. {
  362. IPtr<I>::operator= (std::move (p));
  363. return *this;
  364. }
  365. #endif
  366. };
  367. #if SMTG_CPP11_STDLIBSUPPORT
  368. //------------------------------------------------------------------------
  369. namespace FUnknownPrivate {
  370. template <typename T>
  371. struct Void : std::false_type
  372. {
  373. using Type = void;
  374. };
  375. template <typename T>
  376. using VoidT = typename Void<T>::Type;
  377. //------------------------------------------------------------------------
  378. /**
  379. * This type trait detects if a class has an @c iid member variable. It is used to detect if
  380. * the FUID and DECLARE_CLASS_IID method or the SKI::UID method is used.
  381. */
  382. template <typename T, typename U = void>
  383. struct HasIIDType : std::false_type
  384. {
  385. };
  386. //------------------------------------------------------------------------
  387. template <typename T>
  388. struct HasIIDType<T, FUnknownPrivate::VoidT<typename T::IID>> : std::true_type
  389. {
  390. };
  391. //------------------------------------------------------------------------
  392. } // FUnknownPrivate
  393. //------------------------------------------------------------------------
  394. /** @return the TUID for a SKI interface which uses the SKI::UID method. */
  395. template <typename T,
  396. typename std::enable_if<FUnknownPrivate::HasIIDType<T>::value>::type* = nullptr>
  397. const TUID& getTUID ()
  398. {
  399. return T::IID::toTUID ();
  400. }
  401. //------------------------------------------------------------------------
  402. /** @return the TUID for a SKI interface which uses the FUID and DECLARE_CLASS_IID method. */
  403. template <typename T,
  404. typename std::enable_if<!FUnknownPrivate::HasIIDType<T>::value>::type* = nullptr>
  405. const TUID& getTUID ()
  406. {
  407. return T::iid.toTUID ();
  408. }
  409. #else // SMTG_CPP11_STDLIBSUPPORT
  410. template<typename T>
  411. const TUID& getTUID ()
  412. {
  413. return T::iid.toTUID ();
  414. }
  415. #endif // SMTG_CPP11_STDLIBSUPPORT
  416. //------------------------------------------------------------------------
  417. template <class I>
  418. inline FUnknownPtr<I>::FUnknownPtr (FUnknown* unknown)
  419. {
  420. if (unknown && unknown->queryInterface (getTUID<I> (), (void**)&this->ptr) != kResultOk)
  421. this->ptr = 0;
  422. }
  423. //------------------------------------------------------------------------
  424. template <class I>
  425. inline I* FUnknownPtr<I>::operator= (FUnknown* unknown)
  426. {
  427. I* newPtr = 0;
  428. if (unknown && unknown->queryInterface (getTUID<I> (), (void**)&newPtr) == kResultOk)
  429. {
  430. OPtr<I> rel (newPtr);
  431. return IPtr<I>::operator= (newPtr);
  432. }
  433. return IPtr<I>::operator= (0);
  434. }
  435. //------------------------------------------------------------------------
  436. // FReleaser (obsolete)
  437. //------------------------------------------------------------------------
  438. /** Release an interface using automatic object (obsolete).
  439. This class is obsolete and is only kept for compatibility.
  440. The replacement for FReleaser is OPtr.
  441. Usage example with FReleaser:
  442. \code{.cpp}
  443. void someFunction ()
  444. {
  445. IPath* path = pathCreateMethod ();
  446. FReleaser releaser (path);
  447. .... do something with path...
  448. .... path not used anymore, releaser will destroy it when leaving function scope
  449. }
  450. \endcode
  451. Usage example with OPtr:
  452. \code{.cpp}
  453. void someFunction ()
  454. {
  455. OPtr<IPath> path = pathCreateMethod ();
  456. .... do something with path...
  457. .... path not used anymore, OPtr will destroy it when leaving function scope
  458. }
  459. \endcode
  460. */
  461. struct FReleaser
  462. {
  463. FReleaser (FUnknown* u) : u (u) {}
  464. ~FReleaser ()
  465. {
  466. if (u)
  467. u->release ();
  468. }
  469. FUnknown* u;
  470. };
  471. //------------------------------------------------------------------------
  472. } // namespace Steinberg