您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

370 行
19KB

  1. //*********************************************************
  2. //
  3. // Copyright (c) Microsoft. All rights reserved.
  4. // This code is licensed under the MIT License.
  5. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
  6. // ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
  7. // TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
  8. // PARTICULAR PURPOSE AND NONINFRINGEMENT.
  9. //
  10. //*********************************************************
  11. #ifndef __WIL_SAFECAST_INCLUDED
  12. #define __WIL_SAFECAST_INCLUDED
  13. #include "result_macros.h"
  14. #include <intsafe.h>
  15. #include "wistd_config.h"
  16. #include "wistd_type_traits.h"
  17. namespace wil
  18. {
  19. namespace details
  20. {
  21. // Default error case for undefined conversions in intsafe.h
  22. template<typename OldT, typename NewT> constexpr wistd::nullptr_t intsafe_conversion = nullptr;
  23. // is_known_safe_static_cast_v determines if a conversion is known to be safe or not. Known
  24. // safe conversions can be handled by static_cast, this includes conversions between the same
  25. // type, when the new type is larger than the old type but is not a signed to unsigned
  26. // conversion, and when the two types are the same size and signed/unsigned. All other
  27. // conversions will be assumed to be potentially unsafe, and the conversion must be handled
  28. // by intsafe and checked.
  29. template <typename NewT, typename OldT>
  30. constexpr bool is_known_safe_static_cast_v =
  31. (sizeof(NewT) > sizeof(OldT) && !(wistd::is_signed_v<OldT> && wistd::is_unsigned_v<NewT>)) ||
  32. (sizeof(NewT) == sizeof(OldT) && ((wistd::is_signed_v<NewT> && wistd::is_signed_v<OldT>) || (wistd::is_unsigned_v<NewT> && wistd::is_unsigned_v<OldT>)));
  33. // Helper template to determine that NewT and OldT are both integral types. The safe_cast
  34. // operation only supports conversions between integral types.
  35. template <typename NewT, typename OldT>
  36. constexpr bool both_integral_v = wistd::is_integral<NewT>::value && wistd::is_integral<OldT>::value;
  37. // Note on native wchar_t (__wchar_t):
  38. // Intsafe.h does not currently handle native wchar_t. When compiling with /Zc:wchar_t-, this is fine as wchar_t is
  39. // typedef'd to unsigned short. However, when compiling with /Zc:wchar_t or wchar_t as a native type, the lack of
  40. // support for native wchar_t in intsafe.h becomes an issue. To work around this, we treat native wchar_t as an
  41. // unsigned short when passing it to intsafe.h, because the two on the Windows platform are the same size and
  42. // share the same range according to MSDN. If the cast is to a native wchar_t, the result from intsafe.h is cast
  43. // to a native wchar_t.
  44. // Intsafe does not have a defined conversion for native wchar_t
  45. template <typename NewT, typename OldT>
  46. constexpr bool neither_native_wchar_v = !wistd::is_same<NewT, __wchar_t>::value && !wistd::is_same<OldT, __wchar_t>::value;
  47. // Check to see if the cast is a conversion to native wchar_t
  48. template <typename NewT, typename OldT>
  49. constexpr bool is_cast_to_wchar_v = wistd::is_same<NewT, __wchar_t>::value && !wistd::is_same<OldT, __wchar_t>::value;
  50. // Check to see if the cast is a conversion from native wchar_t
  51. template <typename NewT, typename OldT>
  52. constexpr bool is_cast_from_wchar_v = !wistd::is_same<NewT, __wchar_t>::value && wistd::is_same<OldT, __wchar_t>::value;
  53. // Validate the conversion to be performed has a defined mapping to an intsafe conversion
  54. template <typename NewT, typename OldT>
  55. constexpr bool is_supported_intsafe_cast_v = intsafe_conversion<OldT, NewT> != nullptr;
  56. // True when the conversion is between integral types and can be handled by static_cast
  57. template <typename NewT, typename OldT>
  58. constexpr bool is_supported_safe_static_cast_v = both_integral_v<NewT, OldT> && is_known_safe_static_cast_v<NewT, OldT>;
  59. // True when the conversion is between integral types, does not involve native wchar, has
  60. // a mapped intsafe conversion, and is unsafe.
  61. template <typename NewT, typename OldT>
  62. constexpr bool is_supported_unsafe_cast_no_wchar_v =
  63. both_integral_v<NewT, OldT> &&
  64. !is_known_safe_static_cast_v<NewT, OldT> &&
  65. neither_native_wchar_v<NewT, OldT> &&
  66. is_supported_intsafe_cast_v<NewT, OldT>;
  67. // True when the conversion is between integral types, is a cast to native wchar_t, has
  68. // a mapped intsafe conversion, and is unsafe.
  69. template <typename NewT, typename OldT>
  70. constexpr bool is_supported_unsafe_cast_to_wchar_v =
  71. both_integral_v<NewT, OldT> &&
  72. !is_known_safe_static_cast_v<NewT, OldT> &&
  73. is_cast_to_wchar_v<NewT, OldT> &&
  74. is_supported_intsafe_cast_v<unsigned short, OldT>;
  75. // True when the conversion is between integral types, is a cast from native wchar_t, has
  76. // a mapped intsafe conversion, and is unsafe.
  77. template <typename NewT, typename OldT>
  78. constexpr bool is_supported_unsafe_cast_from_wchar_v =
  79. both_integral_v<NewT, OldT> &&
  80. !is_known_safe_static_cast_v<NewT, OldT> &&
  81. is_cast_from_wchar_v<NewT, OldT> &&
  82. is_supported_intsafe_cast_v<NewT, unsigned short>;
  83. // True when the conversion is supported and unsafe, and may or may not involve
  84. // native wchar_t.
  85. template <typename NewT, typename OldT>
  86. constexpr bool is_supported_unsafe_cast_v =
  87. is_supported_unsafe_cast_no_wchar_v<NewT, OldT> ||
  88. is_supported_unsafe_cast_to_wchar_v<NewT, OldT> ||
  89. is_supported_unsafe_cast_from_wchar_v<NewT, OldT>;
  90. // True when T is any one of the primitive types that the variably sized types are defined as.
  91. template <typename T>
  92. constexpr bool is_potentially_variably_sized_type_v =
  93. wistd::is_same<T, int>::value ||
  94. wistd::is_same<T, unsigned int>::value ||
  95. wistd::is_same<T, long>::value ||
  96. wistd::is_same<T, unsigned long>::value ||
  97. wistd::is_same<T, __int64>::value ||
  98. wistd::is_same<T, unsigned __int64>::value;
  99. // True when either type is potentialy variably sized (e.g. size_t, ptrdiff_t)
  100. template <typename OldT, typename NewT>
  101. constexpr bool is_potentially_variably_sized_cast_v =
  102. is_potentially_variably_sized_type_v<OldT> ||
  103. is_potentially_variably_sized_type_v<NewT>;
  104. // Mappings of all conversions defined in intsafe.h to intsafe_conversion
  105. // Note: Uppercase types (UINT, DWORD, SIZE_T, etc) and architecture dependent types resolve
  106. // to the base types. The base types are used since they do not vary based on architecture.
  107. template<> constexpr auto intsafe_conversion<__int64, char> = LongLongToChar;
  108. template<> constexpr auto intsafe_conversion<__int64, int> = LongLongToInt;
  109. template<> constexpr auto intsafe_conversion<__int64, long> = LongLongToLong;
  110. template<> constexpr auto intsafe_conversion<__int64, short> = LongLongToShort;
  111. template<> constexpr auto intsafe_conversion<__int64, signed char> = LongLongToInt8;
  112. template<> constexpr auto intsafe_conversion<__int64, unsigned __int64> = LongLongToULongLong;
  113. template<> constexpr auto intsafe_conversion<__int64, unsigned char> = LongLongToUChar;
  114. template<> constexpr auto intsafe_conversion<__int64, unsigned int> = LongLongToUInt;
  115. template<> constexpr auto intsafe_conversion<__int64, unsigned long> = LongLongToULong;
  116. template<> constexpr auto intsafe_conversion<__int64, unsigned short> = LongLongToUShort;
  117. template<> constexpr auto intsafe_conversion<int, char> = IntToChar;
  118. template<> constexpr auto intsafe_conversion<int, short> = IntToShort;
  119. template<> constexpr auto intsafe_conversion<int, signed char> = IntToInt8;
  120. template<> constexpr auto intsafe_conversion<int, unsigned __int64> = IntToULongLong;
  121. template<> constexpr auto intsafe_conversion<int, unsigned char> = IntToUChar;
  122. template<> constexpr auto intsafe_conversion<int, unsigned int> = IntToUInt;
  123. template<> constexpr auto intsafe_conversion<int, unsigned long> = IntToULong;
  124. template<> constexpr auto intsafe_conversion<int, unsigned short> = IntToUShort;
  125. template<> constexpr auto intsafe_conversion<long, char> = LongToChar;
  126. template<> constexpr auto intsafe_conversion<long, int> = LongToInt;
  127. template<> constexpr auto intsafe_conversion<long, short> = LongToShort;
  128. template<> constexpr auto intsafe_conversion<long, signed char> = LongToInt8;
  129. template<> constexpr auto intsafe_conversion<long, unsigned __int64> = LongToULongLong;
  130. template<> constexpr auto intsafe_conversion<long, unsigned char> = LongToUChar;
  131. template<> constexpr auto intsafe_conversion<long, unsigned int> = LongToUInt;
  132. template<> constexpr auto intsafe_conversion<long, unsigned long> = LongToULong;
  133. template<> constexpr auto intsafe_conversion<long, unsigned short> = LongToUShort;
  134. template<> constexpr auto intsafe_conversion<short, char> = ShortToChar;
  135. template<> constexpr auto intsafe_conversion<short, signed char> = ShortToInt8;
  136. template<> constexpr auto intsafe_conversion<short, unsigned __int64> = ShortToULongLong;
  137. template<> constexpr auto intsafe_conversion<short, unsigned char> = ShortToUChar;
  138. template<> constexpr auto intsafe_conversion<short, unsigned int> = ShortToUInt;
  139. template<> constexpr auto intsafe_conversion<short, unsigned long> = ShortToULong;
  140. template<> constexpr auto intsafe_conversion<short, unsigned short> = ShortToUShort;
  141. template<> constexpr auto intsafe_conversion<signed char, unsigned __int64> = Int8ToULongLong;
  142. template<> constexpr auto intsafe_conversion<signed char, unsigned char> = Int8ToUChar;
  143. template<> constexpr auto intsafe_conversion<signed char, unsigned int> = Int8ToUInt;
  144. template<> constexpr auto intsafe_conversion<signed char, unsigned long> = Int8ToULong;
  145. template<> constexpr auto intsafe_conversion<signed char, unsigned short> = Int8ToUShort;
  146. template<> constexpr auto intsafe_conversion<unsigned __int64, __int64> = ULongLongToLongLong;
  147. template<> constexpr auto intsafe_conversion<unsigned __int64, char> = ULongLongToChar;
  148. template<> constexpr auto intsafe_conversion<unsigned __int64, int> = ULongLongToInt;
  149. template<> constexpr auto intsafe_conversion<unsigned __int64, long> = ULongLongToLong;
  150. template<> constexpr auto intsafe_conversion<unsigned __int64, short> = ULongLongToShort;
  151. template<> constexpr auto intsafe_conversion<unsigned __int64, signed char> = ULongLongToInt8;
  152. template<> constexpr auto intsafe_conversion<unsigned __int64, unsigned char> = ULongLongToUChar;
  153. template<> constexpr auto intsafe_conversion<unsigned __int64, unsigned int> = ULongLongToUInt;
  154. template<> constexpr auto intsafe_conversion<unsigned __int64, unsigned long> = ULongLongToULong;
  155. template<> constexpr auto intsafe_conversion<unsigned __int64, unsigned short> = ULongLongToUShort;
  156. template<> constexpr auto intsafe_conversion<unsigned char, char> = UInt8ToChar;
  157. template<> constexpr auto intsafe_conversion<unsigned char, signed char> = UIntToInt8;
  158. template<> constexpr auto intsafe_conversion<unsigned int, char> = UIntToChar;
  159. template<> constexpr auto intsafe_conversion<unsigned int, int> = UIntToInt;
  160. template<> constexpr auto intsafe_conversion<unsigned int, long> = UIntToLong;
  161. template<> constexpr auto intsafe_conversion<unsigned int, short> = UIntToShort;
  162. template<> constexpr auto intsafe_conversion<unsigned int, signed char> = UIntToInt8;
  163. template<> constexpr auto intsafe_conversion<unsigned int, unsigned char> = UIntToUChar;
  164. template<> constexpr auto intsafe_conversion<unsigned int, unsigned short> = UIntToUShort;
  165. template<> constexpr auto intsafe_conversion<unsigned long, char> = ULongToChar;
  166. template<> constexpr auto intsafe_conversion<unsigned long, int> = ULongToInt;
  167. template<> constexpr auto intsafe_conversion<unsigned long, long> = ULongToLong;
  168. template<> constexpr auto intsafe_conversion<unsigned long, short> = ULongToShort;
  169. template<> constexpr auto intsafe_conversion<unsigned long, signed char> = ULongToInt8;
  170. template<> constexpr auto intsafe_conversion<unsigned long, unsigned char> = ULongToUChar;
  171. template<> constexpr auto intsafe_conversion<unsigned long, unsigned int> = ULongToUInt;
  172. template<> constexpr auto intsafe_conversion<unsigned long, unsigned short> = ULongToUShort;
  173. template<> constexpr auto intsafe_conversion<unsigned short, char> = UShortToChar;
  174. template<> constexpr auto intsafe_conversion<unsigned short, short> = UShortToShort;
  175. template<> constexpr auto intsafe_conversion<unsigned short, signed char> = UShortToInt8;
  176. template<> constexpr auto intsafe_conversion<unsigned short, unsigned char> = UShortToUChar;
  177. }
  178. // Unsafe conversion where failure results in fail fast.
  179. template <
  180. typename NewT,
  181. typename OldT,
  182. wistd::enable_if_t<details::is_supported_unsafe_cast_no_wchar_v<NewT, OldT>, int> = 0
  183. >
  184. NewT safe_cast_failfast(const OldT var)
  185. {
  186. NewT newVar;
  187. FAIL_FAST_IF_FAILED((details::intsafe_conversion<OldT, NewT>(var, &newVar)));
  188. return newVar;
  189. }
  190. // Unsafe conversion where failure results in fail fast.
  191. template <
  192. typename NewT,
  193. typename OldT,
  194. wistd::enable_if_t<details::is_supported_unsafe_cast_from_wchar_v<NewT, OldT>, int> = 0
  195. >
  196. NewT safe_cast_failfast(const OldT var)
  197. {
  198. NewT newVar;
  199. FAIL_FAST_IF_FAILED((details::intsafe_conversion<unsigned short, NewT>(static_cast<unsigned short>(var), &newVar)));
  200. return newVar;
  201. }
  202. // Unsafe conversion where failure results in fail fast.
  203. template <
  204. typename NewT,
  205. typename OldT,
  206. wistd::enable_if_t<details::is_supported_unsafe_cast_to_wchar_v<NewT, OldT>, int> = 0
  207. >
  208. NewT safe_cast_failfast(const OldT var)
  209. {
  210. unsigned short newVar;
  211. FAIL_FAST_IF_FAILED((details::intsafe_conversion<OldT, unsigned short>(var, &newVar)));
  212. return static_cast<__wchar_t>(newVar);
  213. }
  214. // This conversion is always safe, therefore a static_cast is fine.
  215. template <
  216. typename NewT,
  217. typename OldT,
  218. wistd::enable_if_t<details::is_supported_safe_static_cast_v<NewT, OldT>, int> = 0
  219. >
  220. NewT safe_cast_failfast(const OldT var)
  221. {
  222. return static_cast<NewT>(var);
  223. }
  224. #ifdef WIL_ENABLE_EXCEPTIONS
  225. // Unsafe conversion where failure results in a thrown exception.
  226. template <
  227. typename NewT,
  228. typename OldT,
  229. wistd::enable_if_t<details::is_supported_unsafe_cast_no_wchar_v<NewT, OldT>, int> = 0
  230. >
  231. NewT safe_cast(const OldT var)
  232. {
  233. NewT newVar;
  234. THROW_IF_FAILED((details::intsafe_conversion<OldT, NewT>(var, &newVar)));
  235. return newVar;
  236. }
  237. // Unsafe conversion where failure results in a thrown exception.
  238. template <
  239. typename NewT,
  240. typename OldT,
  241. wistd::enable_if_t<details::is_supported_unsafe_cast_from_wchar_v<NewT, OldT>, int> = 0
  242. >
  243. NewT safe_cast(const OldT var)
  244. {
  245. NewT newVar;
  246. THROW_IF_FAILED((details::intsafe_conversion<unsigned short, NewT>(static_cast<unsigned short>(var), &newVar)));
  247. return newVar;
  248. }
  249. // Unsafe conversion where failure results in a thrown exception.
  250. template <
  251. typename NewT,
  252. typename OldT,
  253. wistd::enable_if_t<details::is_supported_unsafe_cast_to_wchar_v<NewT, OldT>, int> = 0
  254. >
  255. NewT safe_cast(const OldT var)
  256. {
  257. unsigned short newVar;
  258. THROW_IF_FAILED((details::intsafe_conversion<OldT, unsigned short>(var, &newVar)));
  259. return static_cast<__wchar_t>(newVar);
  260. }
  261. // This conversion is always safe, therefore a static_cast is fine.
  262. template <
  263. typename NewT,
  264. typename OldT,
  265. wistd::enable_if_t<details::is_supported_safe_static_cast_v<NewT, OldT>, int> = 0
  266. >
  267. NewT safe_cast(const OldT var)
  268. {
  269. return static_cast<NewT>(var);
  270. }
  271. #endif
  272. // This conversion is unsafe, therefore the two parameter version of safe_cast_nothrow must be used
  273. template <
  274. typename NewT,
  275. typename OldT,
  276. wistd::enable_if_t<details::is_supported_unsafe_cast_v<NewT, OldT>, int> = 0
  277. >
  278. NewT safe_cast_nothrow(const OldT /*var*/)
  279. {
  280. static_assert(!wistd::is_same_v<NewT, NewT>, "This cast has the potential to fail, use the two parameter safe_cast_nothrow instead");
  281. }
  282. // This conversion is always safe, therefore a static_cast is fine.
  283. template <
  284. typename NewT,
  285. typename OldT,
  286. wistd::enable_if_t<details::is_supported_safe_static_cast_v<NewT, OldT>, int> = 0
  287. >
  288. NewT safe_cast_nothrow(const OldT var)
  289. {
  290. return static_cast<NewT>(var);
  291. }
  292. // Unsafe conversion where an HRESULT is returned. It is up to the callee to check and handle the HRESULT
  293. template <
  294. typename NewT,
  295. typename OldT,
  296. wistd::enable_if_t<details::is_supported_unsafe_cast_no_wchar_v<NewT, OldT>, int> = 0
  297. >
  298. HRESULT safe_cast_nothrow(const OldT var, NewT* newTResult)
  299. {
  300. return details::intsafe_conversion<OldT, NewT>(var, newTResult);
  301. }
  302. // Unsafe conversion where an HRESULT is returned. It is up to the callee to check and handle the HRESULT
  303. template <
  304. typename NewT,
  305. typename OldT,
  306. wistd::enable_if_t<details::is_supported_unsafe_cast_from_wchar_v<NewT, OldT>, int> = 0
  307. >
  308. HRESULT safe_cast_nothrow(const OldT var, NewT* newTResult)
  309. {
  310. return details::intsafe_conversion<unsigned short, NewT>(static_cast<unsigned short>(var), newTResult);
  311. }
  312. // Unsafe conversion where an HRESULT is returned. It is up to the callee to check and handle the HRESULT
  313. template <
  314. typename NewT,
  315. typename OldT,
  316. wistd::enable_if_t<details::is_supported_unsafe_cast_to_wchar_v<NewT, OldT>, int> = 0
  317. >
  318. HRESULT safe_cast_nothrow(const OldT var, NewT* newTResult)
  319. {
  320. return details::intsafe_conversion<OldT, unsigned short>(var, reinterpret_cast<unsigned short *>(newTResult));
  321. }
  322. // This conversion is always safe, therefore a static_cast is fine. If it can be determined the conversion
  323. // does not involve a variably sized type, then the compilation will fail and say the single parameter version
  324. // of safe_cast_nothrow should be used instead.
  325. template <
  326. typename NewT,
  327. typename OldT,
  328. wistd::enable_if_t<details::is_supported_safe_static_cast_v<NewT, OldT>, int> = 0
  329. >
  330. HRESULT safe_cast_nothrow(const OldT var, NewT* newTResult)
  331. {
  332. static_assert(details::is_potentially_variably_sized_cast_v<OldT, NewT>, "This cast is always safe; use safe_cast_nothrow<T>(value) to avoid unnecessary error handling.");
  333. *newTResult = static_cast<NewT>(var);
  334. return S_OK;
  335. }
  336. }
  337. #endif // __WIL_SAFECAST_INCLUDED