C++怎么解决不支持字符串枚举?

news2024/11/18 18:45:19

首先,有两种方法:使用命名空间和字符串常量与使用 enum class 和辅助函数。

表格直观展示

特性使用命名空间和字符串常量使用 enum class 和辅助函数
类型安全性低 - 编译器无法检查字符串有效性,运行时发现错误高 - 编译期类型检查,非法类型在编译期即可发现
性能低 - 字符串比较时间复杂度为 O(n),逐字符比较有开销高 - 枚举值比较时间复杂度为 O(1),查找效率高,平均查找为 O(1)
可维护性中 - 易于理解和使用,但字符串容易拼写错误高 - 易读易维护,枚举类型和映射关系可以方便管理和扩展
代码简洁性中 - 可能需要反复访问命名空间中的字符串常量高 - 代码简洁且易读,枚举结合辅助函数方便处理
安全性中 - 字符串误输入导致运行时错误高 - 通过类型检查避免运行时崩溃
易用性中 - 需要记住和管理字符串常量,容易犯拼写错误高 - 枚举值可以通过智能提示和自动补全,大幅减少错误

代码展示 

使用命名空间和字符串常量 

优点

  1. 直观和易于理解:字符串表示类型或状态非常直观,容易理解和使用。
  2. 与外部系统交互方便:许多外部系统或配置文件使用字符串表示状态或类型,直接使用字符串便于与这些系统交互。

缺点

  1. 类型不安全:任何字符串都可以传入,编译器无法检查其有效性,拼写错误会导致运行时错误。
  2. 性能开销较大:字符串比较和逐字符检查的时间复杂度为 O(n),性能较低,尤其是在处理长字符串时。
  3. 可维护性差:字符串常量需要在代码中多次引用,拼写错误不易发现,维护困难。
namespace ContainerType {
    const std::string SimpleView = "SimpleView";
    const std::string SimpleViewPage = "SimpleViewPage";
    const std::string ListView = "ListView";
    const std::string ListViewPage = "ListViewPage";
    const std::string Dialog = "Dialog";
}

void handleContainerType(const std::string& type) {
    if (type == ContainerType::SimpleView) {
        std::cout << "Handling SimpleView" << std::endl;
    } else if (type == ContainerType::SimpleViewPage) {
        std::cout << "Handling SimpleViewPage" << std::endl;
    } else if (type == ContainerType::ListView) {
        std::cout << "Handling ListView" << std::endl;
    } else if (type == ContainerType::ListViewPage) {
        std::cout << "Handling ListViewPage" << std::endl;
    } else if (type == ContainerType::Dialog) {
        std::cout << "Handling Dialog" << std::endl;
    } else {
        std::cout << "Unknown type" << std::endl;
    }
}

使用 enum class 和辅助函数

优点

  1. 类型安全:编译器进行类型检查,非法类型在编译期即可发现,避免运行时错误。
  2. 性能高效:枚举值比较时间复杂度为 O(1),使用哈希表进行字符串到枚举的映射查找平均为 O(1),高效。
  3. 代码可维护性高:枚举值清晰明了,降低拼写错误风险,方便代码维护。
  4. 易于扩展:可以方便地添加新的枚举值和映射关系。

缺点

  1. 稍微复杂:需要维护枚举和映射关系,初次使用时可能需要更多理解。
enum class ContainerType {
    SimpleView,
    SimpleViewPage,
    ListView,
    ListViewPage,
    Dialog,
    Unknown
};

// 辅助函数:字符串 -> ContainerType
ContainerType stringToContainerType(const std::string& typeStr) {
    static const std::unordered_map<std::string, ContainerType> stringToEnumMap = {
        {"SimpleView", ContainerType::SimpleView},
        {"SimpleViewPage", ContainerType::SimpleViewPage},
        {"ListView", ContainerType::ListView},
        {"ListViewPage", ContainerType::ListViewPage},
        {"Dialog", ContainerType::Dialog}
    };
    auto it = stringToEnumMap.find(typeStr);
    if (it != stringToEnumMap.end()) {
        return it->second;
    }
    return ContainerType::Unknown;
}

void handleContainerType(ContainerType type) {
    switch (type) {
        case ContainerType::SimpleView:
            std::cout << "Handling SimpleView" << std::endl;
            break;
        case ContainerType::SimpleViewPage:
            std::cout << "Handling SimpleViewPage" << std::endl;
            break;
        case ContainerType::ListView:
            std::cout << "Handling ListView" << std::endl;
            break;
        case ContainerType::ListViewPage:
            std::cout << "Handling ListViewPage" << std::endl;
            break;
        case ContainerType::Dialog:
            std::cout << "Handling Dialog" << std::endl;
            break;
        default:
            std::cout << "Unknown type" << std::endl;
            break;
    }
}

int main() {
    ContainerType type = ContainerType::SimpleView;
    handleContainerType(type);

    type = stringToContainerType("Dialog");
    handleContainerType(type);

    return 0;
}

开源项目magic_enum

magic_enum 是一个 C++17 的头文件库,提供了对枚举的静态反射功能。它允许你在不使用宏或样板代码的情况下,轻松地将枚举类型与字符串进行转换,并实现一系列有关枚举的高效操作。有如下类:

enum class Color { RED = 1, BLUE, GREEN, NONE };
功能描述示例代码
枚举值转字符串将枚举值转换为字符串auto color_name = magic_enum::enum_name(color);
字符串转枚举值将字符串转换为枚举值(不区分大小写、二元谓词比较)auto color = magic_enum::enum_cast<Color>(color_name);
auto color = magic_enum::enum_cast<Color>("green", magic_enum::case_insensitive);
auto color = magic_enum::enum_cast<Color>("green", [](char lhs, char rhs) { return std::tolower(lhs) == std::tolower(rhs); });
整数转枚举值将整数转换为枚举值auto color = magic_enum::enum_cast<Color>(color_integer);
通过索引访问枚举值根据索引获取枚举值Color color = magic_enum::enum_value<Color>(i);
获取枚举值序列获取所有枚举值的序列constexpr auto colors = magic_enum::enum_values<Color>();
获取枚举元素数量获取枚举的元素个数constexpr std::size_t color_count = magic_enum::enum_count<Color>();
获取枚举值的整数获取枚举值对应的整数auto color_integer = magic_enum::enum_integer(color);
auto color_integer = magic_enum::enum_underlying(color);
获取枚举名称序列获取所有枚举值的名称序列constexpr auto color_names = magic_enum::enum_names<Color>();
获取枚举条目序列获取所有枚举值和名称的序列constexpr auto color_entries = magic_enum::enum_entries<Color>();
检查枚举是否包含某个值检查枚举类型是否包含某个值magic_enum::enum_contains(Color::GREEN);
magic_enum::enum_contains<Color>(2);
magic_enum::enum_contains<Color>("GREEN");
获取枚举值的索引获取枚举值在序列中的索引constexpr auto color_index = magic_enum::enum_index(Color::BLUE);
枚举的位运算支持对枚举类型进行位运算using namespace magic_enum::bitwise_operators;
`Flags flags = Flags::A
标志枚举支持对标志枚举进行特殊处理template <> struct magic_enum::customize::enum_range<Directions> { static constexpr bool is_flags = true; };
`auto name = magic_enum::enum_flags_name(Directions::Up
枚举类型的名称获取枚举类型的名称auto type_name = magic_enum::enum_type_name<decltype(color)>();
ENUM 类型换为在IO流的操作符为枚举添加 ostream 和 istream 的支持using magic_enum::iostream_operators::operator<<;
using magic_enum::iostream_operators::operator>>;
检查是否为无范围枚举判断类型是否为无范围枚举magic_enum::is_unscoped_enum<color>::value;
magic_enum::is_unscoped_enum_v<color>;
检查是否为范围枚举判断类型是否为范围枚举magic_enum::is_scoped_enum<direction>::value;
magic_enum::is_scoped_enum_v<direction>;
Bitwise 操作符为枚举类型提供位操作符using namespace magic_enum::bitwise_operators;
`Flags flags = Flags::A
静态存储枚举变量到字符串更轻量的编译时间和不限于 enum_range 的限制constexpr auto color_name = magic_enum::enum_name<color>();

一个简单的示例:

#include <iostream>
#include <string>
#include <magic_enum.hpp>

enum class Color { RED = 1, BLUE, GREEN, NONE };

int main() {
    // 枚举值转字符串
    Color color1 = Color::RED;
    std::cout << "Color 1: " << magic_enum::enum_name(color1) << std::endl;

    // 字符串转枚举值
    std::string color_name{"GREEN"};
    auto color2 = magic_enum::enum_cast<Color>(color_name);
    if (color2.has_value()) {
        std::cout << "Color 2: " << magic_enum::enum_name(color2.value()) << std::endl;
    }

    return 0;
}

遇到的问题

main.cpp:3:10: fatal error: 'magic_enum.hpp' file not found #include <magic_enum.hpp> ^~~~~~~~~~~~~~~~ 1 error generated.

解决方法:

  1. 下载并包含 magic_enum 库。你可以从 magic_enum 的 GitHub 页面上下载最新的版本:GitHub - Neargye/magic_enum: Static reflection for enums (to string, from string, iteration) for modern C++, work with any enum type without any macro or boilerplate code
  2. magic_enum.hpp 头文件放在你的项目的合适目录中,例如 include 目录。
  3. 编译 
g++ -std=c++17 -Iinclude main.cpp -o main

如果你使用 CMake 进行项目构建,可以通过 FetchContentmegic_enum 集成到你的项目中。

   cmake_minimum_required(VERSION 3.11)

   project(MyProject)

   include(FetchContent)
   FetchContent_Declare(
     magic_enum
     GIT_REPOSITORY https://github.com/Neargye/magic_enum.git
     GIT_TAG v0.7.3
   )
   FetchContent_MakeAvailable(magic_enum)

   add_executable(main src/main.cpp)
   target_link_libraries(main PRIVATE magic_enum::magic_enum)

模拟实现magic_enum

#include <array>
#include <string>
#include <utility>
#include <string_view>

template <typename E, E V>
constexpr auto PrettyName()
{
    std::string_view name{__PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 2};
    name.remove_prefix(name.find_last_of(" ") + 1);
    if (name.front() == '(') name.remove_prefix(name.size());
    return name;
}

template <typename E, E V>
constexpr bool IsValidEnum()
{
    return !PrettyName<E, V>().empty();
}

template <int... Seq>
constexpr auto MakeIntegerSequence(std::integer_sequence<int, Seq...>)
{
    return std::integer_sequence<int, (Seq)...>();
}

constexpr auto NormalIntegerSequence = MakeIntegerSequence(std::make_integer_sequence<int, 32>());

template <typename E, int... Seq>
constexpr size_t GetEnumSize(std::integer_sequence<int, Seq...>)
{
    constexpr std::array<bool, sizeof...(Seq)> valid{IsValidEnum<E, static_cast<E>(Seq)>()...};
    constexpr std::size_t count = [](decltype((valid)) v) constexpr noexcept->std::size_t
    {
        auto cnt = std::size_t{0};
        for (auto b : v) if (b) ++cnt;
        return cnt;
    }(valid);
    return count;
}

template <typename E, int... Seq>
constexpr auto GetAllValidValues(std::integer_sequence<int, Seq...>)
{
    constexpr std::size_t count = sizeof...(Seq);
    constexpr std::array<bool, count> valid{IsValidEnum<E, static_cast<E>(Seq)>()...};
    constexpr std::array<int, count> seq{Seq...};
    std::array<int, GetEnumSize<E>(NormalIntegerSequence)> values{};

    for (std::size_t i = 0, v = 0; i < count; ++i) if (valid[i]) values[v++] = seq[i];
    return values;
}

template <typename E, int... Seq>
constexpr auto GetAllValidNames(std::integer_sequence<int, Seq...>)
{
    constexpr std::array<std::string_view, sizeof...(Seq)> names{PrettyName<E, static_cast<E>(Seq)>()...};
    std::array<std::string_view, GetEnumSize<E>(NormalIntegerSequence)> validNames{};

    for (std::size_t i = 0, v = 0; i < names.size(); ++i) if (!names[i].empty()) validNames[v++] = names[i];
    return validNames;
}

template <typename E>
constexpr std::string_view Enum2String(E V)
{
    constexpr auto names = GetAllValidNames<E>(NormalIntegerSequence);
    constexpr auto values = GetAllValidValues<E>(NormalIntegerSequence);
    constexpr auto size = GetEnumSize<E>(NormalIntegerSequence);

    for (size_t i = 0; i < size; ++i) if (static_cast<int>(V) == values[i]) return names[i];
    return std::to_string(static_cast<int>(V));
}
#include "myenum.h"
#include <iostream>

enum class Color
{
    RED,
    GREEN,
    BLUE,
};

int main()
{
    Color c = Color::BLUE;
    std::cout << Enum2String(c) << std::endl;

    return 0;
}

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1900170.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

爆破片和安全阀

一、爆破片介绍 爆破片是一种用于安全释放压力的结构&#xff0c;通常应用于压力容器、管道和设备中&#xff0c;以防止由于压力过高而导致的灾难性故障。在压力超过设定值时&#xff0c;爆破片会破裂&#xff0c;从而迅速将过压泄放&#xff0c;保护设备和人员安全 爆破片通常…

Java——IO流(二)-(4/7):不同编码读取乱码的问题,转换流-InputStreamReader、OutputStreamWriter

目录 不同编码读取出现乱码的问题 转换流 InputStreamReader(字符输入转换流) 实例演示(1) OutputStreamWriter(字符输出转换流) 实例演示(2) 不同编码读取出现乱码的问题 如果代码编码和被读取的文本文件的编码是一致的&#xff0c;使用字符流读取文本文件时不会出现乱…

达梦数据库系列—22. DPC集群原理

目录 DPC原理 1、系统原理 2、元数据服务 3、数据存储 4、执行计划的生成 5、计算与存储分离 6、动态增删节点 7、分布式事务一致性 第一阶段 预提交 第二阶段 提交 8、RAFT 归档 9、自动故障处理 DPC原理 计划生成节点&#xff08;SP&#xff09;&#xff1a;对外…

猫狗图像分类-划分数据集

&#x1f4da;博客主页&#xff1a;knighthood2001 ✨公众号&#xff1a;认知up吧 &#xff08;目前正在带领大家一起提升认知&#xff0c;感兴趣可以来围观一下&#xff09; &#x1f383;知识星球&#xff1a;【认知up吧|成长|副业】介绍 ❤️如遇文章付费&#xff0c;可先看…

[FreeRTOS 基础知识] 互斥量 概念

文章目录 基础知识互斥量互斥量与信号量区别优先级反转优先级继承小结 基础知识 [FreeRTOS 基础知识] 信号量 概念 互斥量 互斥量&#xff08;Mutex&#xff0c;全称&#xff1a;Mutual Exclusion&#xff09;&#xff0c;在计算机科学中&#xff0c;是一种用于防止多个进程同…

大学教师门诊预约小程序-计算机毕业设计源码73068

摘要 在当今数字化、信息化的浪潮中&#xff0c;大学校园的服务管理正朝着智能化、便捷化的方向迈进。为了优化大学教师的医疗体验&#xff0c;提升门诊预约的效率和便捷性&#xff0c;我们基于Spring Boot框架设计并实现了一款大学教师门诊预约小程序。该小程序不仅提供了传统…

压测工具---Ultron

压测工具&#xff1a;Ultron 类型&#xff1a;接口级和全链路 接口级 对于接口级别的压测我们可以进行 http接口压测、thrift压测、redis压测、kafka压测、DDMQ压测、MySQL压测等&#xff0c;选对对应的业务线、选择好压测执行的时间和轮数就可以执行压测操作了 全链路 对…

SAP PS学习笔记01 - PS概述,创建Project和WBS

本章开始学习PS&#xff08;Project System&#xff09;。 1&#xff0c;PS的概述 PS&#xff08;Project System&#xff09;是SAP企业资源规划系统中的一个关键模块&#xff0c;主要用于项目管理。 它提供了一个全面的框架来规划、控制和执行项目&#xff0c;涵盖了从项目启…

游戏AI的创造思路-技术基础-强化学习(1)

我们“强化”一下机器的“学习”&#xff0c;让机器变得更强~~~~ 目录 1. 强化学习的定义 2. 发展历史 3. 强化学习的基本概念和函数 3.1. 基本概念和函数 3.1.1. 基本概念和函数 3.1.2. Q函数 3.1.2.1. 定义与作用 3.1.2.2. 数学表示 3.1.2.3. 更新规则 3.1.2.4. 算…

3D Web轻量化平台HOOPS Web Platform的功能与应用分析

随着3D技术在多个行业的广泛应用&#xff0c;对于3D模型轻量化的需求日益增长。HOOPS Web Platform作为一个先进的3D模型轻量化平台&#xff0c;为开发人员提供了一整套工具来构建和部署基于Web的工程应用程序。本文将分析HOOPS Web Platform的核心功能和它在不同领域的应用情况…

ORB 特征点提取

FAST关键点 选取像素p&#xff0c;假设它的亮度为Ip&#xff1b; . 设置一个阈值T&#xff08;比如Ip的20%&#xff09;&#xff1b; 以像素p为中心&#xff0c;选取半径为3的圆上的16个像素点&#xff1b; 假如选取的圆上&#xff0c;有连续的N个点的亮度大于IpT或小于…

CSS position属性之relative和absolute

目录 1 参考文章2 五个属性值3 position:static4 position:relative&#xff08;相对&#xff09;5 position:absolute&#xff08;绝对&#xff09; 1 参考文章 https://blog.csdn.net/lalala_dxf/article/details/123566909 https://blog.csdn.net/WangMinGirl/article/deta…

【C语言】字符函数和字符串函数的介绍和模拟实现

介绍处理字符和字符串的库函数的使用和注意事项 求字符串长度 strlen 长度不受限制的字符串函数 strcpy strcat strcmp 长度受限制的字符串函数介绍 strncpy strncat strncmp 0. 前言 C语言中对字符和字符串的处理很是频繁&#xff0c;但是C语言本身是没有字符串类型的&am…

实现沉浸式体验的秘诀:深入了解折幕投影技术!

在当今多媒体技术的浪潮中&#xff0c;投影技术已蜕变成为超越传统内容展示范畴的非凡工具&#xff0c;它深度融合了互动性与沉浸感&#xff0c;成为连接观众与虚拟世界的桥梁。折幕投影技术&#xff0c;作为这一领域的璀璨明珠&#xff0c;更是以其独特而神奇的手法&#xff0…

超过GPT-4V,国产开源多模态大模型来了!支持视频理解/超高分辨率图片理解/多轮对话...

扫码领取享50优惠&#xff01;随时可用&#xff0c;先到先得&#xff01; 大家好&#xff0c;开源多模态大模型真的是每天都在疯狂的涌现&#xff0c;今天分享一个国产大模型 InternLM-XComposer-2.5 中文名&#xff1a;浦语灵笔2.5 仅使用 7B LLM 后端就达到了 GPT-4V 级别的能…

第一百四十五节 Java数据类型教程 - Java字符串类型

Java数据类型教程 - Java字符串类型 零个或多个字符的序列称为字符串。 在Java程序中&#xff0c;字符串由java.lang.String类的对象表示。 String类是不可变的。 String对象的内容在创建后无法修改。 String类有两个伴随类&#xff0c;java.lang.StringBuilder和java.lang.…

独立开发者系列(20)——扫码登录

&#xff08;1&#xff09;网页端的安全登录设计 很多大型网站都有登录限制。这里以一个案例作为例子完整解析。理解安全的登录设计方式&#xff0c;无论对于以后做自动化&#xff0c;自动登录网站&#xff0c;获取数据&#xff0c;还是自己开发月租类型的系统非常有用。当前一…

在AvaotaA1全志T527开发板上使用AvaotaOS 部署 Docker 服务

Docker 是一个开源的应用容器引擎&#xff0c;让开发者可以打包他们的应用以及依赖包到一个可移植的镜像中&#xff0c;然后发布到任何流行的 Linux或Windows操作系统的机器上&#xff0c;也可以实现虚拟化。容器是完全使用沙箱机制&#xff0c;相互之间不会有任何接口。 准备…

高考选专业,兴趣与就业前景该如何平衡?

从高考结束的那一刻开始&#xff0c;有些家长和学生就已经变得焦虑了&#xff0c;因为他们不知道成绩出来的时候学生应该如何填报志愿&#xff0c;也不知道选择什么样的专业&#xff0c;毕竟大学里面的专业丰富多彩&#xff0c;如何选择确实是一门学问&#xff0c;而对于学生们…

8分钟带你快速了解Connector/Catalog API的核心设计

引言 在现代大数据应用中&#xff0c;数据集成和同步成为企业数据管理的关键环节。随着数据源和数据库的多样化&#xff0c;如何高效地进行数据集成成为企业面临的重要挑战。 Apache SeaTunnel 作为一款开源的数据集成工具&#xff0c;致力于解决这一问题。本文将详细介绍 Sea…