当前位置: 首页 > news >正文

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

文章目录

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

前言

前段时间接触了 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 这个东东可以在编译期求值,后面可以多花点时间研究一下

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

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

相关文章:

  • git查看历史记录及修改内容
  • rm -rf 真是删库跑路的一把好手
  • 智能指针(三):weak_ptr浅析
  • float的精度和取值范围
  • linux环境下常用的打包、压缩、解压命令(tar、gzip、bzip2、zip)
  • MySQL数据库导入、导出、复制表、重命名表
  • Python操作Excel工作簿(\*.xlsx)
  • java对接银联商务公众号+服务窗支付(1)
  • java对接银联商务公众号+服务窗支付(2)
  • java对接银联商务公众号+服务窗支付(3)
  • java对接银联商务公众号+服务窗支付(4)
  • java对接银联商务公众号+服务窗支付(5)
  • java对接银联商务扫码支付
  • 报错:Invalid bound statement (not found): com.wshy.example.dao.UserDao.getUsers
  • Field userDao ....service.impl...'com.lzj.springcloud.dao.UserDao' that could not be found
  • [deviceone开发]-do_Webview的基本示例
  • [译] 怎样写一个基础的编译器
  • 【个人向】《HTTP图解》阅后小结
  • 11111111
  • Fundebug计费标准解释:事件数是如何定义的?
  • Javascript弹出层-初探
  • Python实现BT种子转化为磁力链接【实战】
  • React as a UI Runtime(五、列表)
  • SpingCloudBus整合RabbitMQ
  • spring boot下thymeleaf全局静态变量配置
  • 从@property说起(二)当我们写下@property (nonatomic, weak) id obj时,我们究竟写了什么...
  • 给新手的新浪微博 SDK 集成教程【一】
  • 检测对象或数组
  • 解决iview多表头动态更改列元素发生的错误
  • 聚类分析——Kmeans
  • 前端性能优化——回流与重绘
  • 听说你叫Java(二)–Servlet请求
  • 小李飞刀:SQL题目刷起来!
  • 学习JavaScript数据结构与算法 — 树
  • Spring第一个helloWorld
  • !!【OpenCV学习】计算两幅图像的重叠区域
  • #QT(智能家居界面-界面切换)
  • #Spring-boot高级
  • (2)(2.10) LTM telemetry
  • (3)选择元素——(17)练习(Exercises)
  • (32位汇编 五)mov/add/sub/and/or/xor/not
  • (delphi11最新学习资料) Object Pascal 学习笔记---第7章第3节(封装和窗体)
  • (MIT博士)林达华老师-概率模型与计算机视觉”
  • (分布式缓存)Redis哨兵
  • (十二)devops持续集成开发——jenkins的全局工具配置之sonar qube环境安装及配置
  • (原創) 未来三学期想要修的课 (日記)
  • (转)EXC_BREAKPOINT僵尸错误
  • (转)创业的注意事项
  • ***详解账号泄露:全球约1亿用户已泄露
  • ./和../以及/和~之间的区别
  • .NET Core 将实体类转换为 SQL(ORM 映射)
  • .net core 源码_ASP.NET Core之Identity源码学习
  • .NET Project Open Day(2011.11.13)
  • .NET Standard、.NET Framework 、.NET Core三者的关系与区别?
  • .NET精简框架的“无法找到资源程序集”异常释疑