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