Newer
Older
mbed-os / platform / include / platform / mbed_enum_flags.h
/*
 * Copyright (c) 2020 ARM Limited. All rights reserved.
 * SPDX-License-Identifier: Apache-2.0
 * Licensed 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.
 */

#ifndef MBED_SCOPED_ENUM_FLAGS_H
#define MBED_SCOPED_ENUM_FLAGS_H

#include <type_traits>

#define ENUM_FLAG_BITWISE_OPERATOR(T, OP) \
inline constexpr T operator OP(T lhs, T rhs) \
{ \
    return T (static_cast<std::underlying_type_t<T>>(lhs) OP \
        static_cast<std::underlying_type_t<T>>(rhs)); \
}


#define ENUM_FLAG_COMPOUND_ASSIGNMENT_OPERATOR(T, OP) \
inline constexpr T &operator OP(T &lhs, T rhs) \
{ \
    return lhs = lhs OP rhs; \
}


/**
 * @brief Applies bitwise operators to a enum class defined elsewhere.
 *
 * @param T             The enum class typename
 *
 * This macro applies the bitwise negate, AND, OR, XOR operators and the
 * corresponding assignment operators to an existing enum class. The macro
 * uses underlying type traits to convert back and forth.
 *
 * Usage:
 * @code
 * external_enum.h:
 *
 * enum class SpokenLanguages : uint8_t {
 *     Sindarin    = (1 << 0),
 *     Westron     = (1 << 1),
 *     Rohirric    = (1 << 2),
 *     BlackSpeech = (1 << 3)
 * };
 *
 * my_code.cpp:
 *
 * #include "mbed_enum_flags.h"
 * #include "external_enum.h"
 *
 * MBED_ENUM_FLAG_APPLY_OPERATORS(SpokenLanguages);
 *
 * SpokenLanguages gandalf = SpokenLanguages::Sindarin | SpokenLanguages::Westron |
 *     SpokenLanguages::Rohirric | SpokenLanguages::BlackSpeech;
 * @endcode
 *
 */
#define MBED_ENUM_FLAG_APPLY_OPERATORS(T) \
    inline constexpr T operator ~(T lhs) \
    { \
        return T(~static_cast<std::underlying_type_t<T>>(lhs)); \
    } \
    ENUM_FLAG_BITWISE_OPERATOR(T, |) \
    ENUM_FLAG_BITWISE_OPERATOR(T, ^) \
    ENUM_FLAG_BITWISE_OPERATOR(T, &) \
    ENUM_FLAG_COMPOUND_ASSIGNMENT_OPERATOR(T, |=) \
    ENUM_FLAG_COMPOUND_ASSIGNMENT_OPERATOR(T, ^=) \
    ENUM_FLAG_COMPOUND_ASSIGNMENT_OPERATOR(T, &=) \
    static_assert(true, "This assert true is to require a semicolon to terminate the macro.") \


/** @private
 *
 * @brief Bitwise definition macro with underlying type.
 *
 * Not part of public API. Do not use directly. Use MBED_SCOPED_ENUM_FLAGS instead.
 */
#define SCOPED_ENUM_FLAGS_TYPED(T, UNDERLYING_T) \
    enum class T : UNDERLYING_T; \
    MBED_ENUM_FLAG_APPLY_OPERATORS(T); \
    enum class T : UNDERLYING_T

/** @private
 *
 * @brief Bitwise definition macro with default underlying type.
 *
 * Not part of public API. Do not use directly. Use MBED_SCOPED_ENUM_FLAGS instead.
 */
#define SCOPED_ENUM_FLAGS_UNTYPED(T) \
    enum class T; \
    MBED_ENUM_FLAG_APPLY_OPERATORS(T); \
    enum class T

#define MBED_SCOPED_ENUM_FLAGS_CHOOSER(_1, _2, NAME, ...) NAME

/**
 * @brief Creates an enum class with bitwise operator overloads.
 *
 * @param T             The enum class typename
 * @param UNDERLYING_T  Optional: specify the underlying integral type. If
 *                      omitted, the enum class underlying type will be the
 *                      compiler default.
 *
 * This macro creates both the enum class type and defines NOT, AND, OR, and
 * XOR operators. It also defines as three bitwise assignment operators, AND,
 * OR, and XOR. It allows for the scoped nature of the enum class, but adds back
 * the bitwise operators that were missing.
 *
 * This macro uses type traits to convert between the underlying type and back
 * again for the bitwise operations.
 *
 * Usage:
 * @code
 * my_nice_enum_class_with_flags.h:
 *
 * MBED_SCOPED_ENUM_FLAGS(MyFlagName) {
 *     HasEars    = (1 << 0),
 *     HasFur     = (1 << 1),
 *     LaysEggs   = (1 << 2),
 *     Meows      = (1 << 3),
 *     Polydactyl = (1 << 30)
 * };
 *
 * MBED_SCOPED_ENUM_FLAGS(SpokenLanguages, uint8_t) {
 *     Sindarin    = (1 << 0),
 *     Westron     = (1 << 1),
 *     Rohirric    = (1 << 2),
 *     BlackSpeech = (1 << 3)
 * };
 *
 * my_enum_class_flag_consumer.h:
 *
 * class EnumEater {
 * public:
 *     EnumEater(MyFlagName flags) : _flags(flags) {}
 *
 *     static MyFlagName DefaultFlags = MyFlagName::HasEars | MyFlagName::Meows;
 *
 *     bool is_cat() const {
 *         return ((_flags & DefaultFlags) == DefaultFlags) &&
 *             ((_flags & MyFlagName::LaysEggs) == MyFlagName());
 *     }
 * private:
 *     MyFlagName _flags;
 * };
 *
 * bool is_Gandalf(SpokenLanguages flags) {
 *     return flags == (SpokenLanguages::Sindarin | SpokenLanguages::Westron |
 *         SpokenLanguages::SpeaksRohirric | SpokenLanguages::BlackSpeech);
 * }
 * @endcode
 *
 */
#define MBED_SCOPED_ENUM_FLAGS(...) MBED_SCOPED_ENUM_FLAGS_CHOOSER(__VA_ARGS__, SCOPED_ENUM_FLAGS_TYPED, SCOPED_ENUM_FLAGS_UNTYPED)(__VA_ARGS__)

#endif //MBED_SCOPED_ENUM_FLAGS_H