ADTF  3.18.2
result_description_impl.h
Go to the documentation of this file.
1 
16 #ifndef A_UTIL_UTIL_RESULT_DETAIL_DESCRIPTION_IMPL_HEADER_INCLUDED
17 #define A_UTIL_UTIL_RESULT_DETAIL_DESCRIPTION_IMPL_HEADER_INCLUDED
18 
20 
21 // This comment keeps result_description_decl.h on top while clang-formatting
24 
25 #include <cassert>
26 #include <new>
27 
28 namespace a_util {
29 namespace result {
30 namespace detail {
31 template <typename DescriptionIntf>
33  const std::uint64_t value)
34 {
35  // detailed result was allocated...
36  return (!isErrorCodeSet(value)) && // if the error code bit is not set, ...
37  0 != (value & error_code_bitmask); //... but other bits
38 }
39 
40 template <typename DescriptionIntf>
41 inline bool ResultDescriptionTraits<DescriptionIntf>::isErrorCodeSet(const std::uint64_t value)
42 {
43  // the error code bit indicates whether only the error code is set
44  return error_code_bit == (value & error_code_bit);
45 }
46 
47 template <typename DescriptionIntf>
49  const std::int32_t error_code) noexcept
50 {
51  // highly optimized code to bypass the cast on negative error codes...
52  return (
53  // fit the error code to the first 32 bits
54  (static_cast<std::uint64_t>(error_code) & error_code_serialize_bitmask) |
55  // set the error code bit to the most significant bit
56  error_code_bit);
57 }
58 
59 template <typename DescriptionIntf>
61  const std::uint64_t error_code) noexcept
62 {
63  return static_cast<std::int32_t>(
64  // get rid of the error code bit
65  error_code_bitmask &
66  // convert error code to its original value
67  (error_code | error_code_deserialize_bitmask));
68 }
69 
70 template <typename DescriptionIntf>
72  DescriptionIntf>::toInternalErrorPointer(const std::uint64_t error_code) noexcept
73 {
74  return reinterpret_cast<const ReferenceCountedDescriptionType*>(error_code);
75 }
76 
77 template <typename DescriptionIntf>
79 {
80  // check whether the internal representation remains binary compatible
81  // the calculation might be changed
82  using namespace std;
83  constexpr auto minus_one_representation = toInternalErrorCode(-1);
84  constexpr auto plus_one_representation = toInternalErrorCode(1);
85 
86 #if defined(__GNUC__) && ((__GNUC__ == 5) && (__GNUC_MINOR__ == 2))
87 #pragma GCC diagnostic push
88 #pragma GCC diagnostic ignored "-Wattributes"
89 #endif // defined(__GNUC__) && ((__GNUC__ == 5) && (__GNUC_MINOR__ == 2))
90  constexpr auto min_val_representation =
91  toInternalErrorCode((numeric_limits<std::int32_t>::min)());
92  constexpr auto max_val_representation =
93  toInternalErrorCode((numeric_limits<std::int32_t>::max)());
94 #if defined(__GNUC__) && ((__GNUC__ == 5) && (__GNUC_MINOR__ == 2))
95 #pragma GCC diagnostic pop
96 #endif // defined(__GNUC__) && ((__GNUC__ == 5) && (__GNUC_MINOR__ == 2))
97 
98  // but these checks must not be changed!
99  // this is the original error code specification
100  // these checks exist to make sure refactoring the code does not change the original behavior
101  static_assert(0x8000000000000000 == error_code_bit, "error_code_bit must not be changed!");
102  static_assert(~0x8000000000000000 == error_code_bitmask,
103  "error_code_bitmask must not be changed!");
104  static_assert(0xFFFFFFFF00000000 == error_code_deserialize_bitmask,
105  "error_code_deserialize_bitmask must not be changed!");
106  static_assert(0xFFFFFFFF == error_code_serialize_bitmask,
107  "error_code_serialize_bitmask must not be changed!");
108  static_assert(0x80000000ffffffff == minus_one_representation,
109  "Calculation of internal error code representation must not be changed!");
110  static_assert(0x8000000000000001 == plus_one_representation,
111  "Calculation of internal error code representation must not be changed!");
112  static_assert(0x8000000080000000 == min_val_representation,
113  "Calculation of internal error code representation must not be changed!");
114  static_assert(0x800000007fffffff == max_val_representation,
115  "Calculation of internal error code representation must not be changed!");
116 }
117 
118 template <typename DescriptionIntf>
119 inline ResultDescription<DescriptionIntf>::ResultDescription() : _pointer_to_result_or_error_code{}
120 {
121 }
122 
123 template <typename DescriptionIntf>
125  : _pointer_to_result_or_error_code{}
126 {
127  *this = other;
128 }
129 
130 template <typename DescriptionIntf>
132  const ResultDescription& other)
133 {
134  if (&other != this) {
135  if (TraitsType::isDetailedDescriptionSet(_pointer_to_result_or_error_code)) {
136  TraitsType::toInternalErrorPointer(_pointer_to_result_or_error_code)->removeReference();
137  }
138 
139  _pointer_to_result_or_error_code = other._pointer_to_result_or_error_code;
140  if (TraitsType::isDetailedDescriptionSet(_pointer_to_result_or_error_code)) {
141  TraitsType::toInternalErrorPointer(_pointer_to_result_or_error_code)->addReference();
142  }
143  }
144  return *this;
145 }
146 
147 template <typename DescriptionIntf>
149  : _pointer_to_result_or_error_code{}
150 {
151  std::swap(other._pointer_to_result_or_error_code, _pointer_to_result_or_error_code);
152 }
153 
154 template <typename DescriptionIntf>
156  ResultDescription&& other)
157 {
158  if (&other != this) {
159  if (TraitsType::isDetailedDescriptionSet(_pointer_to_result_or_error_code)) {
160  TraitsType::toInternalErrorPointer(_pointer_to_result_or_error_code)->removeReference();
161  }
162 
163  _pointer_to_result_or_error_code = {};
164  std::swap(other._pointer_to_result_or_error_code, _pointer_to_result_or_error_code);
165  }
166  return *this;
167 }
168 
169 template <typename DescriptionIntf>
171 {
172  if (TraitsType::isDetailedDescriptionSet(_pointer_to_result_or_error_code)) {
173  TraitsType::toInternalErrorPointer(_pointer_to_result_or_error_code)->removeReference();
174  }
175  // DTOR is always compiled, so put the test here
176  static_assert(sizeof(std::uintptr_t) <= 8,
177  "ResultDescription pointers must always have a size of 8 Byte or less!");
178 }
179 
180 template <typename DescriptionIntf> // class template parameter
181 template <typename DescriptionImpl, typename... Args> // method template parameters
182 typename ResultDescription<DescriptionIntf>::SelfType // return type
183 ResultDescription<DescriptionIntf>::makeResultDescription(std::int32_t error_code, Args&&... args)
184 {
185  // do not use std::nothrow overload, corresponding 'new'-operator can only be overloaded global
186  try {
188  // first parameter of the detailed description must be of type error code
189  const auto reference_counted_error_object =
190  new ReferenceCountedErrorType(error_code, std::forward<Args>(args)...);
191  SelfType result_description(*reference_counted_error_object);
192  if (TraitsType::isErrorCodeSet(result_description._pointer_to_result_or_error_code)) {
193  // not allowed, only create the error code (no assert in production code, only in debug)
194  reference_counted_error_object->removeReference();
195  assert((false) && "The MSB of the pointer to the heap allocated memory is set to 1, "
196  "but it is reserved for the error code bit. This is inplausible and "
197  "must not happen.");
198  }
199  else {
200  return result_description;
201  }
202  }
203  catch (const std::bad_alloc&) {
204  // operator new() failed to alloc
205  }
206 
207  return SelfType::makeResultDescription(error_code);
208 }
209 
210 template <typename DescriptionIntf>
211 template <typename T>
212 typename ResultDescription<DescriptionIntf>::SelfType ResultDescription<
213  DescriptionIntf>::makeResultDescription(const ::a_util::result::ResultInfo<T>&) noexcept
214 {
216  SelfType result_description(uncounted_error);
217 
218  // The address of the pointer must not occupy the MSB of the internal error, because the
219  // MSB is used to set the error code bit. In reality this should never happen.
220  if (!TraitsType::isErrorCodeSet(result_description._pointer_to_result_or_error_code)) {
221  return result_description;
222  }
223 
224  return SelfType::makeResultDescription(::a_util::result::ResultInfo<T>::getCode());
225 }
226 
227 template <typename DescriptionIntf>
229  std::int32_t error_code)
230 {
231  // use the entire pointer for the error code and set the error code bit appropriately
232  // the user doesn't want to allocate space by calling this function, so just DON'T!
233  return SelfType(error_code);
234 }
235 
236 template <typename DescriptionIntf>
238 {
239  // make sure, detailed description instead of error code is set
240  if (TraitsType::isDetailedDescriptionSet(_pointer_to_result_or_error_code)) {
241  return &TraitsType::toInternalErrorPointer(_pointer_to_result_or_error_code)->getObject();
242  }
243  return nullptr;
244 }
245 
246 template <typename DescriptionIntf>
248 {
249  // only return the error code if only the error code was set!
250  if (TraitsType::isErrorCodeSet(_pointer_to_result_or_error_code)) {
251  return TraitsType::toExternalErrorCode(_pointer_to_result_or_error_code);
252  }
253  return 0;
254 }
255 
256 template <typename DescriptionIntf>
258  std::int32_t error_code) noexcept
259  : _pointer_to_result_or_error_code(TraitsType::toInternalErrorCode(error_code))
260 {
261 }
262 
263 template <typename DescriptionIntf>
265  const ReferenceCountedObjectInterface<DescriptionIntf>& error_object) noexcept
266  : _pointer_to_result_or_error_code(reinterpret_cast<std::uintptr_t>(&error_object))
267 {
268  static_assert(sizeof(std::uintptr_t) == sizeof(decltype(&error_object)),
269  "ResultDescription pointers must match the size of std::uintptr_t!");
270  assert((!TraitsType::isErrorCodeSet(_pointer_to_result_or_error_code)) &&
271  "The cast of the pointer to the internal error code set the MSB to 1, but it is "
272  "reserved for the error code bit. This is inplausible and must not happen.");
273  error_object.addReference();
274 }
275 
276 template <typename T>
277 inline bool operator==(const ResultDescription<T>& lhs, const ResultDescription<T>& rhs)
278 {
279  // we put the tests inside this function so it will be available for all specializations
280  static_assert(sizeof(ResultDescription<T>) == 8, "Description type unsupported size!");
281 
282  return lhs.getErrorCode() == rhs.getErrorCode() &&
284 }
285 
286 template <typename T>
287 inline bool operator!=(const ResultDescription<T>& lhs, const ResultDescription<T>& rhs)
288 {
289  return !(lhs == rhs);
290 }
291 
292 } // namespace detail
293 } // namespace result
294 } // namespace a_util
295 
296 #endif // A_UTIL_UTIL_RESULT_DETAIL_DESCRIPTION_IMPL_HEADER_INCLUDED
Default implementation of a reference counter.
Wrapper for either a pointer to a detailed description object or simply a numeric error code.
ResultDescription & operator=(const ResultDescription &other)
Copy assignment operator.
std::int32_t getErrorCode() const
Get the error code if only the error code was set.
DescriptionIntf const * getDetailedDescription() const
Get the detailed description if any was allocated.
static SelfType makeResultDescription(std::int32_t error_code, Args &&... args)
Allocate an object being able to store detailed error information.
ResultDescription< DescriptionIntf > SelfType
Self type.
Default implementation of a non-counting reference counter.
tVoid swap(A_UTILS_NS::d_ptr< _PARENT, _PRIVATE_D > &i_oLHS, A_UTILS_NS::d_ptr< _PARENT, _PRIVATE_D > &i_oRHS)
std::swap specialization for AUTILSDPtr for perfect fit on ADL
Definition: d_ptr.h:237
Declare the default error description class for Result return values.
bool operator==(const ResultDescription< T > &lhs, const ResultDescription< T > &rhs)
Compare two result description objects for equality.
bool operator!=(const ResultDescription< T > &lhs, const ResultDescription< T > &rhs)
Compare two result description objects for inequality.
Serves as the root component, with common functionality documented in core functionality.
Definition: base.h:24
Implements the default reference counted object.
Private API for ResultDescription type and functions.
Basic result information template, used by a_util::result::Result.
Traits type for the result description.
static bool isDetailedDescriptionSet(std::uint64_t value)
Check whether the detailed description was allocated.
constexpr static std::int32_t toExternalErrorCode(std::uint64_t error_code) noexcept
Get the external error code from the internal error code representation.
~ResultDescriptionTraits()
To implement various static checks.
constexpr static std::uint64_t toInternalErrorCode(std::int32_t error_code) noexcept
Get the internal error code representation from the external error code.
static bool isErrorCodeSet(std::uint64_t value)
Check whether only the error code was set.