blob: 9921030e7c3b150466c2f1be6b3b321c2cac016b [file] [log] [blame]
/**
* 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