Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 111f528d authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Introducing NamedEnum to parse enum value as string names."

parents 7f8a1e6e 173871c1
Loading
Loading
Loading
Loading
+1 −32
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@
#include <string>
#include <type_traits>

#include "NamedEnum.h"
#include "utils/BitSet.h"

#ifndef __UI_INPUT_FLAGS_H
@@ -30,38 +31,6 @@
namespace android {

namespace details {
template <typename F, F V>
constexpr std::optional<std::string_view> enum_value_name() {
    // Should look something like (but all on one line):
    //   std::optional<std::string_view>
    //   android::details::enum_value_name()
    //   [F = android::test::TestFlags, V = android::test::TestFlags::ONE]
    std::string_view view = __PRETTY_FUNCTION__;
    size_t templateStart = view.rfind("[");
    size_t templateEnd = view.rfind("]");
    if (templateStart == std::string::npos || templateEnd == std::string::npos) {
        return std::nullopt;
    }

    // Extract the template parameters without the enclosing braces.
    // Example (cont'd): F = android::test::TestFlags, V = android::test::TestFlags::ONE
    view = view.substr(templateStart + 1, templateEnd - templateStart - 1);
    size_t valStart = view.rfind("V = ");
    if (valStart == std::string::npos) {
        return std::nullopt;
    }

    // Example (cont'd): V = android::test::TestFlags::ONE
    view = view.substr(valStart);
    size_t nameStart = view.rfind("::");
    if (nameStart == std::string::npos) {
        return std::nullopt;
    }

    // Chop off the initial "::"
    nameStart += 2;
    return view.substr(nameStart);
}

template <typename F>
inline constexpr auto flag_count = sizeof(F) * __CHAR_BIT__;
+128 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 The Android Open Source Project
 *
 * 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.
 */

#include <android-base/stringprintf.h>

#include <array>
#include <cstdint>
#include <optional>
#include <string>

#ifndef __UI_INPUT_NAMEDENUM_H
#define __UI_INPUT_NAMEDENUM_H

namespace android {

namespace details {
template <typename E, E V>
constexpr std::optional<std::string_view> enum_value_name() {
    // Should look something like (but all on one line):
    //   std::optional<std::string_view>
    //   android::details::enum_value_name()
    //   [E = android::test::TestEnums, V = android::test::TestEnums::ONE]
    std::string_view view = __PRETTY_FUNCTION__;
    size_t templateStart = view.rfind("[");
    size_t templateEnd = view.rfind("]");
    if (templateStart == std::string::npos || templateEnd == std::string::npos) {
        return std::nullopt;
    }

    // Extract the template parameters without the enclosing braces.
    // Example (cont'd): E = android::test::TestEnums, V = android::test::TestEnums::ONE
    view = view.substr(templateStart + 1, templateEnd - templateStart - 1);
    size_t valStart = view.rfind("V = ");
    if (valStart == std::string::npos) {
        return std::nullopt;
    }

    // Example (cont'd): V = android::test::TestEnums::ONE
    view = view.substr(valStart);
    size_t nameStart = view.rfind("::");
    if (nameStart == std::string::npos) {
        return std::nullopt;
    }

    // Chop off the initial "::"
    nameStart += 2;
    return view.substr(nameStart);
}

template <typename E, typename T, T... I>
constexpr auto generate_enum_values(std::integer_sequence<T, I...> seq) {
    constexpr size_t count = seq.size();

    std::array<E, count> values{};
    for (size_t i = 0, v = 0; v < count; ++i) {
        values[v++] = static_cast<E>(T{0} + i);
    }

    return values;
}

template <typename E, std::size_t N>
inline constexpr auto enum_values =
        generate_enum_values<E>(std::make_integer_sequence<std::underlying_type_t<E>, N>{});

template <typename E, std::size_t N, std::size_t... I>
constexpr auto generate_enum_names(std::index_sequence<I...>) noexcept {
    return std::array<std::optional<std::string_view>, sizeof...(I)>{
            {enum_value_name<E, enum_values<E, N>[I]>()...}};
}

template <typename E, std::size_t N>
inline constexpr auto enum_names = generate_enum_names<E, N>(std::make_index_sequence<N>{});

} // namespace details

class NamedEnum {
public:
    // By default allowed enum value range is 0 ~ 7.
    template <typename E>
    static constexpr size_t max = 8;

    template <auto V>
    static constexpr auto enum_name() {
        using E = decltype(V);
        return details::enum_value_name<E, V>();
    }

    template <typename E>
    static constexpr std::optional<std::string_view> enum_name(E val) {
        auto idx = static_cast<size_t>(val);
        return idx < max<E> ? details::enum_names<E, max<E>>[idx] : std::nullopt;
    }

    // Helper function for parsing enum value to string.
    // Example : enum class TestEnums { ZERO = 0x0 };
    // NamedEnum::string(TestEnums::ZERO) returns string of "ZERO".
    // Note the default maximum enum is 8, if the enum ID to be parsed if greater than 8 like 16,
    // it should be declared to specialized the maximum enum by below:
    // template <> constexpr size_t NamedEnum::max<TestEnums> = 16;
    // If the enum class definition is sparse and contains enum values starting from a large value,
    // Do not specialize it to a large number to avoid performance issues.
    // The recommended maximum enum number to specialize is 64.
    template <typename E>
    static const std::string string(E val) {
        std::string result;
        std::optional<std::string_view> enumString = enum_name(val);
        result += enumString ? enumString.value() : base::StringPrintf("0x%08x", val);
        return result;
    }
};

} // namespace android

#endif // __UI_INPUT_NAMEDENUM_H
 No newline at end of file
+1 −0
Original line number Diff line number Diff line
@@ -2,6 +2,7 @@
cc_test {
    name: "libinput_tests",
    srcs: [
        "NamedEnum_test.cpp",
        "Flags_test.cpp",
        "IdGenerator_test.cpp",
        "InputChannel_test.cpp",
+101 −0
Original line number Diff line number Diff line
/*
 * Copyright 2020 The Android Open Source Project
 *
 * 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.
 */

#include <gtest/gtest.h>
#include <input/NamedEnum.h>

namespace android {

// Test enum class maximum enum value smaller than default maximum of 8.
enum class TestEnums { ZERO = 0x0, ONE = 0x1, TWO = 0x2, THREE = 0x3, SEVEN = 0x7 };
// Big enum contains enum values greater than default maximum of 8.
enum class TestBigEnums { ZERO = 0x0, FIFTEEN = 0xF };

// Declared to specialize the maximum enum since the enum size exceeds 8 by default.
template <>
constexpr size_t NamedEnum::max<TestBigEnums> = 16;

namespace test {
using android::TestBigEnums;
using android::TestEnums;

TEST(NamedEnum, RuntimeNamedEnum) {
    TestEnums e = TestEnums::ZERO;
    ASSERT_EQ(NamedEnum::enum_name(e), "ZERO");

    e = TestEnums::ONE;
    ASSERT_EQ(NamedEnum::enum_name(e), "ONE");

    e = TestEnums::THREE;
    ASSERT_EQ(NamedEnum::enum_name(e), "THREE");

    e = TestEnums::SEVEN;
    ASSERT_EQ(NamedEnum::enum_name(e), "SEVEN");
}

// Test big enum
TEST(NamedEnum, RuntimeBigNamedEnum) {
    TestBigEnums e = TestBigEnums::ZERO;
    ASSERT_EQ(NamedEnum::enum_name(e), "ZERO");

    e = TestBigEnums::FIFTEEN;
    ASSERT_EQ(NamedEnum::enum_name(e), "FIFTEEN");
}

TEST(NamedEnum, RuntimeNamedEnumAsString) {
    TestEnums e = TestEnums::ZERO;
    ASSERT_EQ(NamedEnum::string(e), "ZERO");

    e = TestEnums::ONE;
    ASSERT_EQ(NamedEnum::string(e), "ONE");

    e = TestEnums::THREE;
    ASSERT_EQ(NamedEnum::string(e), "THREE");

    e = TestEnums::SEVEN;
    ASSERT_EQ(NamedEnum::string(e), "SEVEN");
}

TEST(NamedEnum, RuntimeBigNamedEnumAsString) {
    TestBigEnums e = TestBigEnums::ZERO;
    ASSERT_EQ(NamedEnum::string(e), "ZERO");

    e = TestBigEnums::FIFTEEN;
    ASSERT_EQ(NamedEnum::string(e), "FIFTEEN");
}

TEST(NamedEnum, RuntimeUnknownNamedEnum) {
    TestEnums e = static_cast<TestEnums>(0x5);
    ASSERT_EQ(NamedEnum::enum_name(e), std::nullopt);
    e = static_cast<TestEnums>(0x9);
    ASSERT_EQ(NamedEnum::enum_name(e), std::nullopt);
}

TEST(NamedEnum, RuntimeUnknownNamedEnumAsString) {
    TestEnums e = static_cast<TestEnums>(0x5);
    ASSERT_EQ(NamedEnum::string(e), "0x00000005");
    e = static_cast<TestEnums>(0x9);
    ASSERT_EQ(NamedEnum::string(e), "0x00000009");
}

TEST(NamedEnum, CompileTimeFlagName) {
    static_assert(NamedEnum::enum_name<TestEnums::TWO>() == "TWO");
    static_assert(NamedEnum::enum_name<TestEnums::THREE>() == "THREE");
}

} // namespace test

} // namespace android