OR-Tools  9.6
enums.h
Go to the documentation of this file.
1 // Copyright 2010-2022 Google LLC
2 // Licensed under the Apache License, Version 2.0 (the "License");
3 // you may not use this file except in compliance with the License.
4 // You may obtain a copy of the License at
5 //
6 // http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13 
14 // IWYU pragma: private, include "ortools/math_opt/cpp/math_opt.h"
15 // IWYU pragma: friend "ortools/math_opt/cpp/.*"
16 
17 // The MathOpt C++ API defines enums that are used in parameters and results and
18 // that corresponds to Proto generated enums.
19 //
20 // The tools in this header make sure the C++ enums provide the following
21 // features:
22 // * enumerating all enum values
23 // * bidirectional string conversion
24 // * operator<< stream support
25 // * bidirectional proto generated enum conversion
26 //
27 // Example declaration:
28 //
29 // my_file.proto:
30 // enum MyEnumProto {
31 // MY_ENUM_UNSPECIFIED = 0;
32 // MY_ENUM_FIRST_VALUE = 1;
33 // MY_ENUM_SECOND_VALUE = 2;
34 // }
35 //
36 // my_file.h:
37 // enum class MyEnum {
38 // kFirstValue = MY_ENUM_FIRST_VALUE,
39 // kSecondValue = MY_ENUM_SECOND_VALUE,
40 // };
41 //
42 // MATH_OPT_DEFINE_ENUM(MyEnum, MY_ENUM_UNSPECIFIED);
43 //
44 // my_file.cc:
45 // std::optional<absl::string_view>
46 // Enum<MyEnum>::ToOptString(MyEnum value) {
47 // switch (value) {
48 // case MyEnum::kFirstValue:
49 // return "first_value";
50 // case MyEnum::kSecondValue:
51 // return "second_value";
52 // }
53 // return std::nullopt;
54 // }
55 //
56 // absl::Span<const MyEnum> Enum<MyEnum>::AllValues() {
57 // static constexpr MyEnum kMyEnumValues[] = {MyEnum::kFirstValue,
58 // MyEnum::kSecondValue};
59 // return absl::MakeConstSpan(kMyEnumValues);
60 // }
61 //
62 // my_file_test.cc:
63 // #include "ortools/math_opt/cpp/enums_testing.h"
64 // ...
65 // INSTANTIATE_TYPED_TEST_SUITE_P(MyEnum, EnumTest, MyEnum);
66 //
67 // Once this is done, the following functions are available:
68 // * absl::Span<MyEnum> Enum<MyEnum>::AllValues()
69 // * optional<MyEnum> EnumFromString<MyEnum>(string_view)
70 // * string_view EnumToString(MyEnum)
71 // * optional<string_view> EnumToOptString(MyEnum)
72 // * optional<MyEnum> EnumFromProto(MyEnumProto)
73 // * MyEnumProto EnumToProto(optional<MyEnum>)
74 // * MyEnumProto EnumToProto(MyEnum)
75 // * operator<<(MyEnum)
76 // * operator<<(std::optional<MyEnum>)
77 //
78 // See examples of usage in the Enum struct documentation below.
79 #ifndef OR_TOOLS_MATH_OPT_CPP_ENUMS_H_
80 #define OR_TOOLS_MATH_OPT_CPP_ENUMS_H_
81 
82 #include <optional>
83 #include <ostream>
84 #include <type_traits>
85 
86 #include "absl/strings/string_view.h"
87 #include "absl/types/span.h"
88 #include "absl/log/check.h"
89 
91 
92 // This template is specialized for each enum in the C++ API.
93 //
94 // It provides a standard way to query properties of those enums and it is used
95 // by some global functions below to implement conversion from/to string or
96 // proto enum.
97 //
98 // Usage example:
99 //
100 // // Iterating on all enum values.
101 // for (const auto solver_type : Enum<SolverType>::AllValues()) {
102 // ...
103 // }
104 //
105 // // Parsing a flag as an enum.
106 // const std::optional<SolverType> solver_type =
107 // EnumFromString(absl::GetFlag(FLAGS_solver_type));
108 // if (!solver_type) {
109 // return util::InvalidArgumentErrorBuilder()
110 // _ << "failed to parse --solver_type value: "
111 // << absl::GetFlag(FLAGS_solver_type);
112 // }
113 //
114 // // Conversion to string.
115 // const SolverType solver_type = ...;
116 // LOG(INFO) << "solver: " << solver_type;
117 // absl::StrCat(EnumToString(solver_type), "_test");
118 // absl::StrCat(EnumToOptString(solver_type).value(), "_test");
119 //
120 // // Conversion to Proto.
121 // const std::optional<SolverType> opt_solver_type = ...;
122 // const SolverTypeProto solver_type_proto = EnumToProto(opt_solver_type);
123 //
124 // // Conversion from Proto.
125 // const SolverTypeProto solver_type_proto = ...;
126 // const std::optional<SolverType> opt_solver_type =
127 // EnumFromProto(solver_type_proto);
128 //
129 // Implementation note: don't specialize directly and instead use the
130 // MATH_OPT_DEFINE_ENUM macro.
131 template <typename E>
132 struct Enum {
133  // Must be true in all implementation. This is used with std::enable_if to
134  // condition the implementation of some overloads.
135  static constexpr bool kIsImplemented = false;
136 
137  // The type of the Proto equivalent to this enum.
138  //
139  // (Here we use int as a placeholder so that the code compiles.)
140  using Proto = int;
141 
142  // The value Proto enum that represents the unspecified case.
143  static constexpr Proto kProtoUnspecifiedValue = {};
144 
145  // Returns a unique string that represent the enum. Returns nullopt if the
146  // input value is not a valid value of the enum.
147  //
148  // The returned string should not include the enum name and should be in
149  // snake_case (e.g. is the enum is kNoSolutionFound, this should return
150  // "no_solution_found").
151  //
152  // Please prefer using the global functions EnumToString() (or
153  // EnumToOptString() if support for invalid values is needed) instead to
154  // benefit from automatic template type deduction.
155  static std::optional<absl::string_view> ToOptString(E value);
156 
157  // Returns all possible values of the enum.
158  static absl::Span<const E> AllValues();
159 };
160 
161 using ProtoEnumIsValid = bool (*)(int);
162 
163 // This template is specialized for each enum in the Proto API. It
164 // defines the correspondence with the C++ enum.
165 //
166 // Implementation note: don't specialize directly and instead use the
167 // MATH_OPT_DEFINE_ENUM macro.
168 template <typename P>
169 struct EnumProto {
170  // The type of the C++ enum equivalent to the P proto enum.
171  //
172  // (Here we use void as a placeholder so that the code compiles.)
173  using Cpp = void;
174 
175  // The smallest valid enum value.
176  static constexpr P kMin = {};
177 
178  // The largest valid enum value.
179  static constexpr P kMax = {};
180 
181  // Proto function returning the true if the input integer matches a valid
182  // value (some values may be missing in range [kMin, kMax]).
183  static constexpr ProtoEnumIsValid kIsValid = nullptr;
184 };
185 
186 // Returns the Proto enum that matches the input C++ proto, returns
187 // Enum<E>::kProtoUnspecifiedValue if the input is std::nullopt.
188 template <typename E>
189 typename Enum<E>::Proto EnumToProto(const std::optional<E> value);
190 
191 // Returns the Proto enum that matches the input C++ proto.
192 //
193 // Implementation note: this overload is necessary for EnumToProto(Xxx::kXxx)
194 // since C++ won't deduce E in std::optional<E> with the other overload.
195 template <typename E>
196 typename Enum<E>::Proto EnumToProto(const E value);
197 
198 // Returns the C++ enum that matches the input Proto enum, returns
199 // std::nullopt if the input is kProtoUnspecifiedValue.
200 template <typename P>
201 std::optional<typename EnumProto<P>::Cpp> EnumFromProto(const P proto_value);
202 
203 // Returns a unique string that represent the enum.
204 //
205 // It CHECKs that the input is a valid enum value. For most users this should
206 // always be the case since MathOpt don't generates invalid data.
207 //
208 // Prefer using operator<< when possible though. As a side benefice it does not
209 // CHECK but instead prints the integer value of the invalid input.
210 template <typename E>
211 absl::string_view EnumToString(const E value);
212 
213 // Returns a unique string that represent the enum. Returns nullopt if the input
214 // value is not a valid value of the enum.
215 template <typename E>
216 std::optional<absl::string_view> EnumToOptString(const E value);
217 
218 // Returns the enum value that corresponds to the input string or nullopt if no
219 // enum matches.
220 //
221 // The expected strings are the one returned by EnumToString().
222 //
223 // This is O(n) in complexity so use with care.
224 template <typename E>
225 std::optional<E> EnumFromString(const absl::string_view str);
226 
227 // Overload of operator<< for enum types that implements Enum<E>.
228 //
229 // It calls EnumToOptString(), printing the returned value if not nullopt. When
230 // nullopt it prints the enum numeric value instead.
231 template <typename E,
232  // We must use enable_if here to prevent this overload to be selected
233  // for other types than ones that implement Enum<E>.
234  typename = std::enable_if_t<Enum<E>::kIsImplemented>>
235 std::ostream& operator<<(std::ostream& out, const E value) {
236  const std::optional<absl::string_view> opt_str = EnumToOptString(value);
237  if (opt_str.has_value()) {
238  out << *opt_str;
239  } else {
240  out << "<invalid enum (" << static_cast<std::underlying_type_t<E>>(value)
241  << ")>";
242  }
243  return out;
244 }
245 
246 // Overload of operator<< for std::optional<E> when Enum<E> is implemented.
247 //
248 // When the value is nullopt, it prints "<unspecified>", else it prints the enum
249 // value.
250 template <typename E,
251  // We must use enable_if here to prevent this overload to be selected
252  // for other types than ones that implement Enum<E>.
253  typename = std::enable_if_t<Enum<E>::kIsImplemented>>
254 std::ostream& operator<<(std::ostream& out, const std::optional<E> opt_value) {
255  if (opt_value.has_value()) {
256  out << *opt_value;
257  } else {
258  out << "<unspecified>";
259  }
260  return out;
261 }
262 
264 // Template functions implementations after this point.
266 
267 template <typename E>
268 typename Enum<E>::Proto EnumToProto(const std::optional<E> value) {
269  return value ? static_cast<typename Enum<E>::Proto>(*value)
271 }
272 
273 template <typename E>
274 typename Enum<E>::Proto EnumToProto(const E value) {
275  return EnumToProto(std::make_optional(value));
276 }
277 
278 template <typename P>
279 std::optional<typename EnumProto<P>::Cpp> EnumFromProto(const P proto_value) {
280  if (proto_value == Enum<typename EnumProto<P>::Cpp>::kProtoUnspecifiedValue) {
281  return std::nullopt;
282  }
283  return static_cast<typename EnumProto<P>::Cpp>(proto_value);
284 }
285 
286 template <typename E>
287 absl::string_view EnumToString(const E value) {
288  std::optional<absl::string_view> opt_str = Enum<E>::ToOptString(value);
289  CHECK(opt_str.has_value())
290  << "invalid value: " << static_cast<std::underlying_type_t<E>>(value);
291  return *opt_str;
292 }
293 
294 template <typename E>
295 std::optional<absl::string_view> EnumToOptString(const E value) {
296  return Enum<E>::ToOptString(value);
297 }
298 
299 template <typename E>
300 std::optional<E> EnumFromString(const absl::string_view str) {
301  for (const E value : Enum<E>::AllValues()) {
302  if (EnumToOptString(value) == str) {
303  return value;
304  }
305  }
306  return std::nullopt;
307 }
308 
309 // Macros that defines the templates specializations for Enum and EnumProto.
310 //
311 // The CppEnum parameter is the name of the C++ enum class which values are the
312 // Proto enum values. The C++ enum must contain a value for each value of the
313 // Proto enum but the UNSPECIFIED one. The proto_unspecified_value is the
314 // UNSPECIFIED one.
315 //
316 // It leaves two functions to be implemented in the .cc file:
317 //
318 // absl::string_view Enum<CppEnum>::ToOptString(CppEnum value) {
319 // absl::Span<const CppEnum> Enum<CppEnum>::AllValues();
320 //
321 // See the comment at the top of this file for an example. See the comment on
322 // Enum struct for the functions that can then be used on enums.
323 #define MATH_OPT_DEFINE_ENUM(CppEnum, proto_unspecified_value) \
324  template <> \
325  struct Enum<CppEnum> { \
326  static constexpr bool kIsImplemented = true; \
327  using Proto = CppEnum##Proto; \
328  static constexpr Proto kProtoUnspecifiedValue = proto_unspecified_value; \
329  static std::optional<absl::string_view> ToOptString(CppEnum value); \
330  static absl::Span<const CppEnum> AllValues(); \
331  }; \
332  \
333  template <> \
334  struct EnumProto<CppEnum##Proto> { \
335  using Cpp = CppEnum; \
336  static constexpr CppEnum##Proto kMin = CppEnum##Proto##_MIN; \
337  static constexpr CppEnum##Proto kMax = CppEnum##Proto##_MAX; \
338  static constexpr ProtoEnumIsValid kIsValid = CppEnum##Proto##_IsValid; \
339  } /* missing semicolon to force adding it at the invocation site */
340 
341 } // namespace operations_research::math_opt
342 
343 #endif // OR_TOOLS_MATH_OPT_CPP_ENUMS_H_
int64_t value
std::optional< E > EnumFromString(const absl::string_view str)
Definition: enums.h:300
absl::string_view EnumToString(const E value)
Definition: enums.h:287
bool(*)(int) ProtoEnumIsValid
Definition: enums.h:161
std::optional< absl::string_view > EnumToOptString(const E value)
Definition: enums.h:295
std::optional< typename EnumProto< P >::Cpp > EnumFromProto(const P proto_value)
Definition: enums.h:279
std::ostream & operator<<(std::ostream &ostr, const IndicatorConstraint &constraint)
Enum< E >::Proto EnumToProto(const std::optional< E > value)
Definition: enums.h:268
static std::optional< absl::string_view > ToOptString(E value)
Definition: basis_status.cc:24
static constexpr bool kIsImplemented
Definition: enums.h:135
static absl::Span< const E > AllValues()
Definition: basis_status.cc:41
static constexpr Proto kProtoUnspecifiedValue
Definition: enums.h:143
static constexpr ProtoEnumIsValid kIsValid
Definition: enums.h:183