memory.hpp 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  1. /*
  2. * Copyright 2005 - 2016 Zarafa and its licensors
  3. * Copyright 2016 Kopano and its licensors
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU Affero General Public License, version 3,
  7. * as published by the Free Software Foundation.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU Affero General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU Affero General Public License
  15. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. */
  17. #ifndef _KCHL_MEMORY_HPP
  18. #define _KCHL_MEMORY_HPP 1
  19. #include <kopano/zcdefs.h>
  20. #include <type_traits> /* std::is_base_of */
  21. #include <utility> /* std::swap */
  22. #include <cstdlib>
  23. #include <mapiutil.h> /* MAPIFreeBuffer */
  24. #include <edkmdb.h> /* ROWLIST */
  25. #include <kopano/ECGuid.h>
  26. #include <kopano/ECTags.h>
  27. #include <kopano/IECUnknown.h>
  28. namespace KCHL {
  29. using namespace KC;
  30. template<typename _T> class memory_proxy _kc_final {
  31. public:
  32. memory_proxy(_T **__p) noexcept : _m_ptr(__p) {}
  33. operator _T **(void) noexcept { return _m_ptr; }
  34. template<typename _U> _U **as(void) const noexcept
  35. {
  36. static_assert(sizeof(_U *) == sizeof(_T *), "This hack won't work");
  37. return reinterpret_cast<_U **>(_m_ptr);
  38. }
  39. operator void **(void) noexcept { return as<void>(); }
  40. private:
  41. _T **_m_ptr;
  42. };
  43. template<typename _T> class memory_proxy2 _kc_final {
  44. public:
  45. memory_proxy2(_T **__p) noexcept : _m_ptr(__p) {}
  46. memory_proxy<_T> operator&(void)
  47. {
  48. return memory_proxy<_T>(_m_ptr);
  49. }
  50. private:
  51. _T **_m_ptr;
  52. };
  53. template<typename _T> class object_proxy _kc_final {
  54. public:
  55. object_proxy(_T **__p) noexcept : _m_ptr(__p) {}
  56. operator _T **(void) noexcept { return _m_ptr; }
  57. template<typename _U> _U **as(void) const noexcept
  58. {
  59. static_assert(sizeof(_U *) == sizeof(_T *), "This hack won't work");
  60. return reinterpret_cast<_U **>(_m_ptr);
  61. }
  62. operator void **(void) noexcept { return as<void>(); }
  63. operator IUnknown **(void) noexcept { return as<IUnknown>(); }
  64. private:
  65. _T **_m_ptr;
  66. };
  67. template<> class object_proxy<IUnknown> _kc_final {
  68. public:
  69. object_proxy(IUnknown **__p) noexcept : _m_ptr(__p) {}
  70. operator IUnknown **(void) noexcept { return _m_ptr; }
  71. template<typename _U> _U **as(void) const noexcept
  72. {
  73. static_assert(sizeof(_U *) == sizeof(IUnknown *), "This hack won't work");
  74. return reinterpret_cast<_U **>(_m_ptr);
  75. }
  76. operator void **(void) noexcept { return as<void>(); }
  77. private:
  78. IUnknown **_m_ptr;
  79. };
  80. template<typename _T> class object_proxy2 _kc_final {
  81. public:
  82. object_proxy2(_T **__p) noexcept : _m_ptr(__p) {}
  83. object_proxy<_T> operator&(void)
  84. {
  85. return object_proxy<_T>(_m_ptr);
  86. }
  87. private:
  88. _T **_m_ptr;
  89. };
  90. class default_delete {
  91. public:
  92. void operator()(void *p) { MAPIFreeBuffer(p); }
  93. };
  94. /**
  95. * The KCHL memory_ptr works a lot like std::unique_ptr, with the
  96. * additional differences:
  97. * - operator& is defined (this is why we cannot use/derive from unique_ptr)
  98. * - the deleter is fixed (for now…)
  99. * - conversion to base class is not permitted (not very relevant to KC)
  100. * - operator bool not present (not really desired for KC)
  101. * Differences to the prior implementation (ZCP's mapi_memory_ptr):
  102. * - "constexpr", "noexcept" and "explicit" keywords added
  103. * - move constructor and move assignment is implemented
  104. * - methods "is_null", "free" and "as" are gone
  105. * - operator void** and operator! is gone
  106. */
  107. template<typename _T, typename _Deleter = default_delete> class memory_ptr {
  108. public:
  109. typedef _T value_type;
  110. typedef _T *pointer;
  111. constexpr memory_ptr(void) noexcept {}
  112. constexpr memory_ptr(std::nullptr_t) noexcept {}
  113. explicit memory_ptr(_T *__p) noexcept : _m_ptr(__p) {}
  114. ~memory_ptr(void)
  115. {
  116. if (_m_ptr != nullptr)
  117. _Deleter()(_m_ptr);
  118. /*
  119. * We normally don't need the following. Or maybe don't even
  120. * want. But g++'s stdlib has it, probably for robustness
  121. * reasons when placement delete is involved.
  122. */
  123. _m_ptr = pointer();
  124. }
  125. memory_ptr(const memory_ptr &) = delete;
  126. memory_ptr(memory_ptr &&__o) : _m_ptr(__o.release()) {}
  127. /* Observers */
  128. _T &operator*(void) const { return *_m_ptr; }
  129. _T *operator->(void) const noexcept { return _m_ptr; }
  130. _T *get(void) const noexcept { return _m_ptr; }
  131. operator _T *(void) const noexcept { return _m_ptr; }
  132. _T *operator+(size_t __n) const noexcept { return _m_ptr + __n; }
  133. /* Modifiers */
  134. _T *release(void) noexcept
  135. {
  136. _T *__p = get();
  137. _m_ptr = pointer();
  138. return __p;
  139. }
  140. void reset(_T *__p = pointer()) noexcept
  141. {
  142. std::swap(_m_ptr, __p);
  143. if (__p != pointer())
  144. _Deleter()(__p);
  145. }
  146. void swap(memory_ptr &__o)
  147. {
  148. std::swap(_m_ptr, __o._m_ptr);
  149. }
  150. memory_proxy2<_T> operator~(void)
  151. {
  152. reset();
  153. return memory_proxy2<_T>(&_m_ptr);
  154. }
  155. memory_proxy2<_T> operator+(void)
  156. {
  157. return memory_proxy2<_T>(&_m_ptr);
  158. }
  159. memory_ptr &operator=(const memory_ptr &) = delete;
  160. memory_ptr &operator=(memory_ptr &&__o) noexcept
  161. {
  162. reset(__o.release());
  163. return *this;
  164. }
  165. memory_ptr &operator=(std::nullptr_t) noexcept
  166. {
  167. reset();
  168. return *this;
  169. }
  170. private:
  171. void operator&(void) const noexcept {} /* flag everyone */
  172. _T *_m_ptr = nullptr;
  173. };
  174. /**
  175. * Works a bit like shared_ptr, except that the refcounting is in the
  176. * underlying object (_T) rather than this class.
  177. */
  178. template<typename _T, REFIID _R = GUID_NULL> class object_ptr {
  179. public:
  180. typedef _T value_type;
  181. typedef _T *pointer;
  182. constexpr object_ptr(void) noexcept {}
  183. constexpr object_ptr(std::nullptr_t) noexcept {}
  184. explicit object_ptr(_T *__p, bool __addref = true) : _m_ptr(__p)
  185. {
  186. if (__addref && _m_ptr != pointer())
  187. _m_ptr->AddRef();
  188. }
  189. ~object_ptr(void)
  190. {
  191. if (_m_ptr != pointer())
  192. _m_ptr->Release();
  193. _m_ptr = pointer();
  194. }
  195. object_ptr(const object_ptr &__o)
  196. {
  197. reset(__o._m_ptr, true);
  198. }
  199. object_ptr(object_ptr &&__o)
  200. {
  201. std::swap(__o._m_ptr, _m_ptr);
  202. }
  203. /* Observers */
  204. _T &operator*(void) const { return *_m_ptr; }
  205. _T *operator->(void) const noexcept { return _m_ptr; }
  206. _T *get(void) const noexcept { return _m_ptr; }
  207. operator _T *(void) const noexcept { return _m_ptr; }
  208. static constexpr const IID &iid(void) { return _R; }
  209. template<typename _U> HRESULT QueryInterface(_U &result)
  210. {
  211. if (_m_ptr == nullptr)
  212. return MAPI_E_NOT_INITIALIZED;
  213. typename _U::pointer newobj = nullptr;
  214. HRESULT hr = _m_ptr->QueryInterface(result.iid(), reinterpret_cast<void **>(&newobj));
  215. if (hr == hrSuccess)
  216. result.reset(newobj, false);
  217. /*
  218. * Here we check if it makes sense to try to get the requested
  219. * interface through the PR_EC_OBJECT object. It only makes
  220. * sense to attempt this if the current type (value_type) is
  221. * derived from IMAPIProp. If it is higher than IMAPIProp, no
  222. * OpenProperty exists. If it is derived from
  223. * ECMAPIProp/ECGenericProp, there is no need to try the
  224. * workaround, because we can be sure it is not wrapped by
  225. * MAPI.
  226. *
  227. * The Conversion<IMAPIProp,value_type>::exists is some
  228. * template magic that checks at compile time. We could check
  229. * at run time with a dynamic_cast, but we know the current
  230. * type at compile time, so why not check it at compile time?
  231. */
  232. else if (hr == MAPI_E_INTERFACE_NOT_SUPPORTED &&
  233. std::is_base_of<IMAPIProp, value_type>::value) {
  234. KCHL::memory_ptr<SPropValue> pv;
  235. if (HrGetOneProp(_m_ptr, PR_EC_OBJECT, &~pv) != hrSuccess)
  236. return hr; // hr is still MAPI_E_INTERFACE_NOT_SUPPORTED
  237. auto unk = reinterpret_cast<IECUnknown *>(pv->Value.lpszA);
  238. hr = unk->QueryInterface(result.iid(), reinterpret_cast<void **>(&newobj));
  239. if (hr == hrSuccess)
  240. result.reset(newobj, false);
  241. }
  242. return hr;
  243. }
  244. template<typename _P> _P as(void)
  245. {
  246. _P tmp = nullptr;
  247. QueryInterface(tmp);
  248. return tmp;
  249. }
  250. /* Modifiers */
  251. _T *release(void) noexcept
  252. {
  253. _T *__p = get();
  254. _m_ptr = pointer();
  255. return __p;
  256. }
  257. void reset(_T *__p = pointer(), bool __addref = true) noexcept
  258. {
  259. if (__addref && __p != pointer())
  260. __p->AddRef();
  261. std::swap(_m_ptr, __p);
  262. if (__p != pointer())
  263. __p->Release();
  264. }
  265. void swap(object_ptr &__o)
  266. {
  267. std::swap(_m_ptr, __o._m_ptr);
  268. }
  269. object_proxy2<_T> operator~(void)
  270. {
  271. reset();
  272. return object_proxy2<_T>(&_m_ptr);
  273. }
  274. object_proxy2<_T> operator+(void)
  275. {
  276. return object_proxy2<_T>(&_m_ptr);
  277. }
  278. object_ptr &operator=(const object_ptr &__o) noexcept
  279. {
  280. reset(__o._m_ptr, true);
  281. return *this;
  282. }
  283. object_ptr &operator=(object_ptr &&__o) noexcept
  284. {
  285. std::swap(__o._m_ptr, _m_ptr);
  286. return *this;
  287. }
  288. private:
  289. void operator=(std::nullptr_t) noexcept {}
  290. void operator&(void) const noexcept {} /* flag everyone */
  291. _T *_m_ptr = nullptr;
  292. };
  293. class cstdlib_deleter {
  294. public:
  295. void operator()(void *x) { free(x); }
  296. };
  297. class rowset_delete {
  298. public:
  299. void operator()(ADRLIST *x) { FreePadrlist(x); }
  300. void operator()(SRowSet *x) { FreeProws(x); }
  301. void operator()(ROWLIST *x) { FreeProws(reinterpret_cast<SRowSet *>(x)); }
  302. };
  303. typedef memory_ptr<ADRLIST, rowset_delete> adrlist_ptr;
  304. typedef memory_ptr<SRowSet, rowset_delete> rowset_ptr;
  305. typedef memory_ptr<ROWLIST, rowset_delete> rowlist_ptr;
  306. template<typename _T> inline void
  307. swap(memory_ptr<_T> &__x, memory_ptr<_T> &__y) noexcept
  308. {
  309. __x.swap(__y);
  310. }
  311. template<typename _T, REFIID _R = GUID_NULL> inline void
  312. swap(object_ptr<_T, _R> &__x, object_ptr<_T, _R> &__y) noexcept
  313. {
  314. __x.swap(__y);
  315. }
  316. } /* namespace KCHL */
  317. #endif /* _KCHL_MEMORY_HPP */