参考开源项目实现一个简易的C++枚举转字符串的函数

2023-05-16

文章目录

  • 前言
  • 改造
  • 函数使用
  • 各函数的作用
  • 总结

前言

前段时间接触了 magic_enum 这个开源库,代码量不算太多,是一个但头文件的枚举操作库,关于如何使用还写了一篇总结 《推荐一个C++枚举转字符串的开源项目magic_enum》,当时觉得这个库很棒,但是对于我当前枚举转化字符串的需求还说还是太臃肿了,所以决定改造一下,这不今天过来填坑了。

改造

一开始还没太理解开源库的原理,认为原来的实现限制太大,为了实现后面字符串转枚举,获取所有枚举名等需求,不得不限定一个枚举的范围,这个范围在 magic_enum 这个开源库中是 [-128, 128],所以当我开始改造时打算把这个范围去掉,但是当我真正弄懂它的原理后,才发现这个范围是必须指定的,不然无法在编译期预处理,从而达到枚举值转换成字符串的目的。

认识到这一点以后,我也不再纠结范围的限制,设定了一个 [0, 31] 的常用枚举范围,相比于原来 [-128, 128] 的范围缩小了不少,这样能加快编译的速度,参考这个开源库和一些网络上关于这个库的讲解,我也实现了一个功能单一的简洁的枚举转字符串的函数 Enum2String,大约70行代码,使用起来还是比较方便的。

#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));
}

函数使用

这个Enum2String函数使用也非常方便,直接把枚举变量作为参数传进去就可以了:

#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;
}

编译运行后的结果为:

$ g++ enumtest.cpp -std=c++17 && ./a.out
Color::BLUE

各函数的作用

前面提到过,我这个库还是参考 magic_enum 这个开源库的源码及网上对它的讲解来实现的,只不过精简了大部分我用不到的内容,仅实现了我想要的枚举转字符串的功能,并且大部分都在编译器求值,仅 Enum2String 函数中遍历的部分只能在运行时才能计算求得,所以效率还算不错,各个模板函数作用明确,下面简单描述下:

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;
}

PrettyName() 函数是利用 __PRETTY_FUNCTION__ 这个宏来截取最终我们想要的字符串,如果不做处理,__PRETTY_FUNCTION__ 的值会是这样:

constexpr auto PrettyName() [with E = Color; E V = Color::BLUE]

靠近结尾的 Color::BLUE 正是我们想要得到的字符串,所以我们可以按照自己的需要把它截取出来。

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

IsValidEnum() 函数是用于判断一个枚举名字是否有效,如果截取的最终名字为空,则认为此枚举无效。

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>());

MakeIntegerSequence() 用于生成一个范围是 [0, 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;
}

GetEnumSize() 用于遍历数列范围内的各个整数,找出有效的枚举有多少个。

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;
}

GetAllValidValues() 用于遍历数列范围内各个整数,找出全部有效枚举值,返回包含有效值的数组。

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;
}

GetAllValidNames() 用于遍历数列范围内各个整数,找出全部有效枚举值的名字,返回包含这些名字的数组。

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));
}

Enum2String() 用于从编译期生成的数组中遍历寻找枚举值等于参数的枚举值名字,如果枚举值无效或者超出范围就范围对应的整数字符串。

总结

  • magic_enum 是个很不错的库,但他相对于我的需求来说显得太大了
  • 根据自己的需求改造开源库,一方面巩固了知识,另一方面也更适合自己的要求
  • constexpr 这个东东可以在编译期求值,后面可以多花点时间研究一下

==>> 反爬链接,请勿点击,原地爆炸,概不负责!<<==

原始财富的积累的真的是太难了,那些趁着各种东风各种红利的人们是幸运的,运气也是人生的一部分,而大部分没有这运气的人们想要积累财富,必然要付出十倍甚至上百倍的努力,这些不可选择也无需抱怨,只要踏踏实实往前走就好了~

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

参考开源项目实现一个简易的C++枚举转字符串的函数 的相关文章

  • 考勤查询统计SQL脚本。

    本文主要记录下平时工作中考勤统计中的SQL脚本 以便于后续翻阅 同时和大家分享一下 不足的地方还请大牛多多给与点评 nbsp 1 首先是查询某员工的考勤记录 可以根据年份 月份 或者时间段查询结果 同时也可以去掉人员筛选条件 查询多个人的考
  • SQL语句删除具有外键约束(foreign key)的表。因为该对象正由一个 FOREIGN KEY 约束引用。

    关于包含外键的表 xff0c 清理数据的时候 xff0c 如truncateTable xff0c 网上大部分的解决办法是 xff0c 删除外键 删除数据 再新建表 这里介绍一种不需要删除外键 xff0c 只需要修改外键属性就可以删除数据的
  • Unicode&ASCII中双向控制字符 U+202D和U+202C

    ASCII编码对照表 911查询 ASCII编码转换 xff0c ASCII码在线查询工具 ASCII 在线转换器 xff0c ASCII码 xff0c ASCII 转码 在线工具 Unicode 中的 BIDI 双向性算法 掘金 1 场景
  • C++魔方阵

    问题描述 输入一个自然数 xff2e xff08 1 amp le N amp le 9 xff09 xff0c 要求输出如下的魔方阵 xff0c 即边长为2 N 1 xff0c xff2e 在中心出现一次 xff0c 其余位置上的数字从外
  • PCM和WAV音频格式的区别,以及python自动转换

    目录 WAV和PCM的简单介绍PCMWAV 关于音频的基础知识声道数channels采样位数bits采样频率sample rate 进阶内容互相转换代码 WAV和PCM的简单介绍 PCM pcm xff1a pulse code modul
  • 解决:“操作无法完成因为其中的文件夹或文件已在另一程序中打开”无法删除文件的问题

    1 xff0c 利用快捷键 xff08 Ctrl 43 Alt 43 delete xff09 打开任务管理器 xff0c 选择其中的性能 xff0c 打开 资源管理器 2 xff0c 搜索下面关联的句柄 xff08 可搜索文件储存的路径
  • windows下使用C++操作MySQL数据库

    系统环境 操作系统 xff1a windows 7 64位 编译环境 xff1a visual studio 2015 MySQL版本 xff1a 5 6 31 log MySQL Community Server GPL 环境设置 1 将
  • kali重启网络服务

    kali的命令和一般的重启网络命令稍有不同 普通系统 xff1a systemctl restart network xff08 有补全 xff09 kali xff1a systemctl restart networking xff08
  • 安全设备默认地址账密总结

    防火墙 厂商默认地址用户名密码天融信192 168 1 254supermantalent talent 64 123华为192 168 0 1 8443adminHuawei 64 123 Admin 64 123网御星云一代防火墙htt
  • Android开发——实现背景颜色渐变效果

    前言 在Android开发当中 xff0c 我们肯定会接到有业务需求是 xff1a 让APP的某一些背景颜色产生渐变效果 那我们应该怎么去实现呢 xff1f 接下就是我要为大家介绍的了 效果图 这是需求要达到的效果 接下来说一下是怎么实现的
  • Java常见设计模式总结

    一 设计模式总述 xff1a 1 什么是设计模式 xff1a 设计模式是一套经过反复使用的代码设计经验 xff0c 目的是为了重用代码 让代码更容易被他人理解 保证代码可靠性 设计模式于己于人于系统都是多赢的 xff0c 它使得代码编写真正
  • ensp解决virtualbox不兼容问题

    virtualbox版本5 2 44 这个版本很讲究 xff0c 太高太低都不行 windows版本为20H2亲测有效 防火墙默认全关 另一台windows配置 系统型号virtualbox型号windows20h25 2 44window
  • 关于元宇宙

    元宇宙融合了信息技术 xff08 5G 6G xff09 互联网时代 xff08 web3 0 xff09 人工智能 云算力 大数据 区块链以及 VR AR MR xff0c 游戏引擎在内的虚拟现实技术的成果 它将引发基础数学 xff08
  • 关于oracle预言机

    oracle预言机和oracle数据库没有任何原因 在其他语种中oracle有预言的意思 区块链预言机 xff08 Oracle xff09 是区块链与外部世界交互的一种实现机制 xff0c 它在区块链与外部世界间建立一种可信任的桥接机制
  • Windows10 系统安装微软商店(ms-windows-store)

    在下载日历是显示没有应用 xff0c 应从ms windows store下载 在此记录windows10安装ms windows store步骤 步骤如下 xff1a 1 使用win 43 x打开菜单后 xff0c 选择powershel
  • 谷歌浏览器打开本地堡垒机应用发布服务器cmd的方法

    齐治堡垒机是业界中较为出名的堡垒机 xff0c 但是依旧存在一些bug 堡垒机是通过应用发布服务器访问web的 xff0c 如果托管了web且堡垒机管理员没有加固应用发布服务器本地策略 xff0c 我们可以通过浏览器调用本地的cmd进行一系
  • 2022复盘&2023计划

    个人成长计划 2022复盘 自媒体 B站 4月10日成为UP主 发布了35个视频 播放量13 6w 累计直播431h 粉丝量1160 获赞量2058 公众号 1053关注 36篇内容 小红书 136粉丝 1167赞 知乎 85关注 48赞
  • 使用集简云将UpTimer同步到Notion

    使用UpTimer同步到Notion 对于集简云我们应当非常熟悉了 xff0c 之前讲过很多流程啦 利用集简云将Notion数据库更新订阅到Outlook和微信 1 干货分享 集简云 2步轻松定制个人RSS阅读器 高效获取信息 2 释放双手
  • MySQL分组查询语句

    文章目录 1 需求2 表结构与部分数据3 查询语句4 结果5 前端显示 1 需求 根据账单表 tb bookkeeping 中的用户ID user id xff0c 按时间倒序查询该用户所在房间所有支出 xff08 bk type 61 0
  • 使用pyinstaller将具有多个python文件的项目打包为exe(含依赖库)

    1 将需要打包有python文件放到一个文件夹 xff0c 例如下图所示的Demo文件夹 xff0c 其中ClickEveryDay py为主文件 xff0c telegram ico为图标文件 2 生成主函数对应的spec文件 命令 xf

随机推荐

  • 在win10下使用PowerShell批量替换文件名

    步骤 通过PowerShell ISE来创建扩展名为 ps1的脚本文件 具体操作过程参考 xff1a https www ithome com html win10 250196 htm编辑新建的 ps1文件 xff0c 举个栗子进行简单说
  • Kotlin笔记15——字符串转数字类型

    前言 在使用Java编程语言开发的 xff0c 我们会经常遇到字符串转数字的需求 那么在Kotlin中是怎么实现的呢 xff1f 接下来跟大家分享一下 字符串转数字 首先我们必须保证字符串是数字类型 xff0c 不能出现a3这种数字与字符混
  • 使用gitLab过程中遇到的一些问题

    之前由于疫情 xff0c 电脑放在公司 xff0c 有一些数据需要其他同事帮忙提交 xff0c 怎知居然连了他的git账号 xff0c 搞得我自己代码提交拉取老有问题 xff0c 一开始没有意识到是这个原因 xff0c 知道打开了自己git
  • 【Micropython】肝货~使用USB_VCP通过USB串口与树莓派或PC端通信

    为什么要使用USB VCP xff1f Micropython有很多串口 xff0c 例如PYboard xff0c 有5个串口可以使用 xff0c 但是 xff0c 平时我们在做一些项目的时候 xff0c 需要使用的引脚较多 xff0c
  • ROS中安装配置并使用VScode(持续更新)

    1 为什么使用VScode VSCode 全称 Visual Studio Code xff0c 是微软出的一款轻量级代码编辑器 xff0c 免费 开源而且功能强大 它支持几乎所有主流的程序语言的语法高亮 智能代码补全 自定义热键 括号匹配
  • pip install airsim问题

    直接使用pip install airsim安装airsim包会失败 airsim C Users DELL gt pip install airsim Collecting airsim Using cached airsim 1 8 1
  • vm虚拟机无法拖拽文件和复制粘贴解决办法

    sudo apt install open vm tools sudo apt install open vm tools desktop
  • PX4和Airsim通信操作流程

    坑真几把多 先在Windows上安装UE4和Airsim不再赘述 xff0c 官网都有 虚拟机或其他计算机安装好ubuntu并安装PX4 1 安装PX4的ROS相关包 xff08 mavros xff09 1 第一种 xff1a 进入官网安
  • mavros安装流程(超简单)

    只适用于Ubuntu18 04 在Ubuntu中新建一个空白文本 xff0c 命名为123 sh bin bash Bash script for setting up ROS Melodic with Gazebo 9 developme
  • 安装WSL2+Ubuntu18.04(慢慢更新记录)

    1 安装WSL和Ubuntu WSL官网在此 安装 WSL Microsoft Learn Windows下CMD xff0c 先安装WSL2 wsl install 然后进入Microsoft Store xff0c 搜索Ubuntu然后
  • -bash: ./Setup.sh: Permission denied

    sudo chmod 777 xxx
  • Linux 给文件夹或者文件添加权限

    chmod R 777 文件夹 参数 R是递归的意思 777表示开放所有权限 chmod 777 test sh chmod 43 x 某文件 如果给所有人添加可执行权限 xff1a chmod a 43 x 文件名 xff1b 如果给文件
  • Postman使用笔记——Postman发送get请求

    前言 在实际的开发当中 xff0c 我们经常用到get或者post请求 在这篇博客里面分享一下 xff0c 如何在Postman中发送get请求 发送get请求 1 在Postman工作空间选定get请求 图中我们可以看到很多请求方式 xf
  • jdbc连接mysql数据库的详细步骤

    标题 jdbc连接mysql数据库 1 首先在项目根目录创lLib文件夹 xff0c 放入jdbc驱动程序 xff0c 然后Add As Library 2 建包 bean包 xff1a 专门放置属性类 dao包 xff1a 进行数据操作的
  • css高度从0到auto的transition动画

    如题 xff0c 想实现css高度从0到auto的transition动画 xff0c 发现直接写没有效果 查了一下 xff0c 发现可以用max height解决 xff0c 代码如下 lt DOCTYPE html gt lt html
  • beego打包

    beego打包 在main go 对应的目录下 windows平台 xff1a bee pack be GOOS 61 windows 打包后生成一个tar gz文件 xff0c 发送到部署服务器 xff0c 解压gz为tar xff0c
  • C++求解组合数的具体实现

    文章目录 前言问题起因组合公式公式变形递推公式递归实现备忘递归动态规划压缩DP其他优化 总结补充反向递归正向递推 前言 很少写关于具体算法的总结笔记 xff0c 因为很难把一个算法从头到尾的叙述清晰并且完整 xff0c 容易造成误解 这次想
  • protobuf中SerializeToString和SerializePartialToString的区别

    文章目录 前言proto2message定义message扩展注意事项 proto3序列化SerializeToString和SerializeAsString区别SerializeToString和SerializePartialToSt
  • epoll的LT模式(水平触发)和ET模式(边沿触发)

    文章目录 前言名称的记忆状态变化LT模式ET模式数据的读取和发送代码实践基础代码测试分类怎么解决ET触发了一次就不再触发了 总结 前言 epoll的触发模式是个引发讨论非常多的话题 xff0c 网络上这方面总结的文章也很多 xff0c 首先
  • 参考开源项目实现一个简易的C++枚举转字符串的函数

    文章目录 前言改造函数使用各函数的作用总结 前言 前段时间接触了 magic enum 这个开源库 xff0c 代码量不算太多 xff0c 是一个但头文件的枚举操作库 xff0c 关于如何使用还写了一篇总结 推荐一个C 43 43 枚举转字