| /** |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #pragma once |
| |
| #include <cstring> |
| #include <optional> |
| #include <set> |
| #include <string> |
| |
| #include "StringUtils.h" |
| #include "Macro.h" |
| |
| namespace org { |
| namespace apache { |
| namespace nifi { |
| namespace minifi { |
| namespace utils { |
| |
| #define INCLUDE_BASE_FIELD(x) \ |
| x = Base::x |
| |
| // [[maybe_unused]] on public members to avoid warnings when used inside an anonymous namespace |
| #define SMART_ENUM_BODY(Clazz, ...) \ |
| constexpr Clazz(Type value = static_cast<Type>(-1)) : value_{value} {} \ |
| explicit Clazz(const std::string& str) : value_{parse(str.c_str()).value_} {} \ |
| explicit Clazz(const char* str) : value_{parse(str).value_} {} \ |
| private: \ |
| Type value_; \ |
| public: \ |
| [[maybe_unused]] Type value() const { \ |
| return value_; \ |
| } \ |
| struct detail : Base::detail { \ |
| static std::set<std::string> values() { \ |
| static constexpr const char* ownValues[]{ \ |
| FOR_EACH(SECOND, COMMA, (__VA_ARGS__)) \ |
| }; \ |
| std::set<std::string> values = Base::detail::values(); \ |
| for (auto value : ownValues) { \ |
| values.emplace(value); \ |
| } \ |
| return values; \ |
| } \ |
| static const char* toStringImpl(Type a, const char* DerivedName) { \ |
| static constexpr const char* values[]{ \ |
| FOR_EACH(SECOND, COMMA, (__VA_ARGS__)) \ |
| }; \ |
| int index = static_cast<int>(a); \ |
| if (Base::length <= index && index < length) { \ |
| return values[index - Base::length]; \ |
| } \ |
| return Base::detail::toStringImpl(static_cast<Base::Type>(a), DerivedName); \ |
| } \ |
| }; \ |
| static constexpr int length = Base::length + COUNT(__VA_ARGS__); \ |
| [[maybe_unused]] friend const char* toString(Type a) { \ |
| return detail::toStringImpl(a, #Clazz); \ |
| } \ |
| [[maybe_unused]] const char* toString() const { \ |
| return detail::toStringImpl(value_, #Clazz); \ |
| } \ |
| [[maybe_unused]] const char* toStringOr(const char* fallback) const { \ |
| if (*this) { \ |
| return toString(); \ |
| } \ |
| return fallback; \ |
| } \ |
| [[maybe_unused]] static std::set<std::string> values() { \ |
| return detail::values(); \ |
| } \ |
| [[maybe_unused]] friend bool operator==(Clazz lhs, Clazz rhs) { \ |
| return lhs.value_ == rhs.value_; \ |
| } \ |
| [[maybe_unused]] friend bool operator!=(Clazz lhs, Clazz rhs) { \ |
| return lhs.value_ != rhs.value_; \ |
| } \ |
| [[maybe_unused]] friend bool operator<(Clazz lhs, Clazz rhs) { \ |
| return lhs.value_ < rhs.value_;\ |
| } \ |
| [[maybe_unused]] explicit operator bool() const { \ |
| int idx = static_cast<int>(value_); \ |
| return 0 <= idx && idx < length; \ |
| } \ |
| [[maybe_unused]] static Clazz parse(const char* str, const ::std::optional<Clazz>& fallback = {}, bool caseSensitive = true) { \ |
| for (int idx = 0; idx < length; ++idx) { \ |
| if (::org::apache::nifi::minifi::utils::StringUtils::equals(str, detail::toStringImpl(static_cast<Type>(idx), #Clazz), caseSensitive)) \ |
| return static_cast<Type>(idx); \ |
| } \ |
| if (fallback) { \ |
| return fallback.value(); \ |
| } \ |
| throw std::runtime_error(std::string("Cannot convert \"") + str + "\" to " #Clazz); \ |
| } \ |
| template<typename T, typename = typename std::enable_if<std::is_base_of<typename T::detail, detail>::value>::type> \ |
| [[maybe_unused]] T cast() const { \ |
| if (0 <= value_ && value_ < T::length) { \ |
| return static_cast<typename T::Type>(value_); \ |
| } \ |
| return {}; \ |
| } |
| |
| /** |
| * These macros provide an encapsulation of enum-like behavior offering the following: |
| * - switch safety: the compiler can detect if some enum cases are not handled |
| * - string conversion: convert between enum instances and their string representations (toString, parse) |
| * - validity: check if it contains a value that is an invalid enum value |
| * - extensibility: extend an enum with new values and safely* cast from derived to base |
| * (* "safely" here means that there is no casting between unrelated enums) |
| * - reflection: access the set of all string representations |
| */ |
| |
| #define SMART_ENUM(Clazz, ...) \ |
| struct Clazz { \ |
| using Base = ::org::apache::nifi::minifi::utils::EnumBase; \ |
| enum Type { \ |
| FOR_EACH(FIRST, COMMA, (__VA_ARGS__)) \ |
| }; \ |
| SMART_ENUM_BODY(Clazz, __VA_ARGS__) \ |
| }; |
| |
| #define SMART_ENUM_EXTEND(Clazz, base, base_fields, ...) \ |
| struct Clazz { \ |
| using Base = base; \ |
| enum Type { \ |
| FOR_EACH(INCLUDE_BASE_FIELD, COMMA, base_fields), \ |
| FOR_EACH(FIRST, COMMA, (__VA_ARGS__)) \ |
| }; \ |
| static_assert((COUNT base_fields) == Base::length, "Must enumerate all base instance values"); \ |
| SMART_ENUM_BODY(Clazz, __VA_ARGS__) \ |
| }; |
| |
| struct EnumBase { |
| enum Type {}; |
| static constexpr int length = 0; |
| struct detail { |
| static std::set<std::string> values() { |
| return {}; |
| } |
| static const char* toStringImpl(Type a, const char* DerivedName) { |
| throw std::runtime_error(std::string("Cannot stringify unknown instance in enum \"") + DerivedName + "\" : \"" |
| + std::to_string(static_cast<int>(a)) + "\""); |
| } |
| }; |
| }; |
| |
| } // namespace utils |
| } // namespace minifi |
| } // namespace nifi |
| } // namespace apache |
| } // namespace org |