1 /* Proposed SG14 status_code
2 (C) 2018 - 2020 Niall Douglas <http://www.nedproductions.biz/> (5 commits)
3 File Created: May 2020
4 
5 
6 Licensed under the Apache License, Version 2.0 (the "License");
7 you may not use this file except in compliance with the License.
8 You may obtain a copy of the License in the accompanying file
9 Licence.txt or at
10 
11 http://www.apache.org/licenses/LICENSE-2.0
12 
13 Unless required by applicable law or agreed to in writing, software
14 distributed under the License is distributed on an "AS IS" BASIS,
15 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 See the License for the specific language governing permissions and
17 limitations under the License.
18 
19 
20 Distributed under the Boost Software License, Version 1.0.
21 (See accompanying file Licence.txt or copy at
22 http://www.boost.org/LICENSE_1_0.txt)
23 */
24 
25 #ifndef BOOST_OUTCOME_SYSTEM_ERROR2_QUICK_STATUS_CODE_FROM_ENUM_HPP
26 #define BOOST_OUTCOME_SYSTEM_ERROR2_QUICK_STATUS_CODE_FROM_ENUM_HPP
27 
28 #include "generic_code.hpp"
29 
30 BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_BEGIN
31 
32 template <class Enum> class _quick_status_code_from_enum_domain;
33 //! A status code wrapping `Enum` generated from `quick_status_code_from_enum`.
34 template <class Enum> using quick_status_code_from_enum_code = status_code<_quick_status_code_from_enum_domain<Enum>>;
35 
36 //! Defaults for an implementation of `quick_status_code_from_enum<Enum>`
37 template <class Enum> struct quick_status_code_from_enum_defaults
38 {
39   //! The type of the resulting code
40   using code_type = quick_status_code_from_enum_code<Enum>;
41   //! Used within `quick_status_code_from_enum` to define a mapping of enumeration value with its status code
42   struct mapping
43   {
44     //! The enumeration type
45     using enumeration_type = Enum;
46 
47     //! The value being mapped
48     const Enum value;
49     //! A string representation for this enumeration value
50     const char *message;
51     //! A list of `errc` equivalents for this enumeration value
52     const std::initializer_list<errc> code_mappings;
53   };
54   //! Used within `quick_status_code_from_enum` to define mixins for the status code wrapping `Enum`
55   template <class Base> struct mixin : Base
56   {
57     using Base::Base;
58   };
59 };
60 
61 /*! The implementation of the domain for status codes wrapping `Enum` generated from `quick_status_code_from_enum`.
62  */
63 template <class Enum> class _quick_status_code_from_enum_domain : public status_code_domain
64 {
65   template <class DomainType> friend class status_code;
66   template <class StatusCode> friend class detail::indirecting_domain;
67   using _base = status_code_domain;
68   using _src = quick_status_code_from_enum<Enum>;
69 
70 public:
71   //! The value type of the quick status code from enum
72   using value_type = Enum;
73   using _base::string_ref;
74 
_quick_status_code_from_enum_domain()75   constexpr _quick_status_code_from_enum_domain()
76       : status_code_domain(_src::domain_uuid, _uuid_size<detail::cstrlen(_src::domain_uuid)>())
77   {
78   }
79   _quick_status_code_from_enum_domain(const _quick_status_code_from_enum_domain &) = default;
80   _quick_status_code_from_enum_domain(_quick_status_code_from_enum_domain &&) = default;
81   _quick_status_code_from_enum_domain &operator=(const _quick_status_code_from_enum_domain &) = default;
82   _quick_status_code_from_enum_domain &operator=(_quick_status_code_from_enum_domain &&) = default;
83   ~_quick_status_code_from_enum_domain() = default;
84 
85 #if __cplusplus < 201402L && !defined(_MSC_VER)
get()86   static inline const _quick_status_code_from_enum_domain &get()
87   {
88     static _quick_status_code_from_enum_domain v;
89     return v;
90   }
91 #else
92   static inline constexpr const _quick_status_code_from_enum_domain &get();
93 #endif
94 
name() const95   virtual string_ref name() const noexcept override { return string_ref(_src::domain_name); }
96 
97 protected:
98   // Not sure if a hash table is worth it here, most enumerations won't be long enough to be worth it
99   // Also, until C++ 20's consteval, the hash table would get emitted into the binary, bloating it
_find_mapping(value_type v)100   static BOOST_OUTCOME_SYSTEM_ERROR2_CONSTEXPR14 const typename _src::mapping *_find_mapping(value_type v) noexcept
101   {
102     for(const auto &i : _src::value_mappings())
103     {
104       if(i.value == v)
105       {
106         return &i;
107       }
108     }
109     return nullptr;
110   }
111 
_do_failure(const status_code<void> & code) const112   virtual bool _do_failure(const status_code<void> &code) const noexcept override
113   {
114     assert(code.domain() == *this);  // NOLINT
115     // If `errc::success` is in the generic code mapping, it is not a failure
116     const auto *mapping = _find_mapping(static_cast<const quick_status_code_from_enum_code<value_type> &>(code).value());
117     assert(mapping != nullptr);
118     if(mapping != nullptr)
119     {
120       for(errc ec : mapping->code_mappings)
121       {
122         if(ec == errc::success)
123         {
124           return false;
125         }
126       }
127     }
128     return true;
129   }
_do_equivalent(const status_code<void> & code1,const status_code<void> & code2) const130   virtual bool _do_equivalent(const status_code<void> &code1, const status_code<void> &code2) const noexcept override
131   {
132     assert(code1.domain() == *this);                                                                   // NOLINT
133     const auto &c1 = static_cast<const quick_status_code_from_enum_code<value_type> &>(code1);  // NOLINT
134     if(code2.domain() == *this)
135     {
136       const auto &c2 = static_cast<const quick_status_code_from_enum_code<value_type> &>(code2);  // NOLINT
137       return c1.value() == c2.value();
138     }
139     if(code2.domain() == generic_code_domain)
140     {
141       const auto &c2 = static_cast<const generic_code &>(code2);  // NOLINT
142       const auto *mapping = _find_mapping(c1.value());
143       assert(mapping != nullptr);
144       if(mapping != nullptr)
145       {
146         for(errc ec : mapping->code_mappings)
147         {
148           if(ec == c2.value())
149           {
150             return true;
151           }
152         }
153       }
154     }
155     return false;
156   }
_generic_code(const status_code<void> & code) const157   virtual generic_code _generic_code(const status_code<void> &code) const noexcept override
158   {
159     assert(code.domain() == *this);  // NOLINT
160     const auto *mapping = _find_mapping(static_cast<const quick_status_code_from_enum_code<value_type> &>(code).value());
161     assert(mapping != nullptr);
162     if(mapping != nullptr)
163     {
164       if(mapping->code_mappings.size() > 0)
165       {
166         return *mapping->code_mappings.begin();
167       }
168     }
169     return errc::unknown;
170   }
_do_message(const status_code<void> & code) const171   virtual string_ref _do_message(const status_code<void> &code) const noexcept override
172   {
173     assert(code.domain() == *this);  // NOLINT
174     const auto *mapping = _find_mapping(static_cast<const quick_status_code_from_enum_code<value_type> &>(code).value());
175     assert(mapping != nullptr);
176     if(mapping != nullptr)
177     {
178       return string_ref(mapping->message);
179     }
180     return string_ref("unknown");
181   }
182 #if defined(_CPPUNWIND) || defined(__EXCEPTIONS) || defined(BOOST_OUTCOME_STANDARDESE_IS_IN_THE_HOUSE)
_do_throw_exception(const status_code<void> & code) const183   BOOST_OUTCOME_SYSTEM_ERROR2_NORETURN virtual void _do_throw_exception(const status_code<void> &code) const override
184   {
185     assert(code.domain() == *this);                                                                  // NOLINT
186     const auto &c = static_cast<const quick_status_code_from_enum_code<value_type> &>(code);  // NOLINT
187     throw status_error<_quick_status_code_from_enum_domain>(c);
188   }
189 #endif
190 };
191 
192 #if __cplusplus >= 201402L || defined(_MSC_VER)
193 template <class Enum> constexpr _quick_status_code_from_enum_domain<Enum> quick_status_code_from_enum_domain = {};
get()194 template <class Enum> inline constexpr const _quick_status_code_from_enum_domain<Enum> &_quick_status_code_from_enum_domain<Enum>::get()
195 {
196   return quick_status_code_from_enum_domain<Enum>;
197 }
198 #endif
199 
200 namespace mixins
201 {
202   template <class Base, class Enum> struct mixin<Base, _quick_status_code_from_enum_domain<Enum>> : public quick_status_code_from_enum<Enum>::template mixin<Base>
203   {
204     using quick_status_code_from_enum<Enum>::template mixin<Base>::mixin;
205   };
206 }  // namespace mixins
207 
208 BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_END
209 
210 #endif
211