以前一直没用过标准库的regex,今天写一个hlsl的解析工具的时候用了一下,发现用字符串字面值写regular expression的时候非常不方便,特别是每个“\”字符都要被识别为转义,只能写成“\\”。比如,一系列空白字符加一个数字要写成这样:

std::regex reg("\\s*\\d");

这样一来,稍微一复杂的regular expression就完全没法看了。理所当然地,可以用raw string literal来解决这个问题。C++11中raw string literal的写法是这样的:

R"delimiter(string)delimiter"

其中delimiter是用来标识字符串的边界的。比方说——

std::cout << R"__("minecraft")__"

输出的结果就是"minecraft",注意输出的结果是有引号的。

用raw string literal的时候我就觉得,能不能把它搞得更直观一点,比如上面那个regular expression能不能写成

#define RAW_STR(str) something...
std::regex reg(RAW_STR(\s*\d));

于是我试着写了一个

#define RAW_STR(str, delimiter) R##"##delimiter##(##str##)##delimiter##"

结果发现##delimiter##(##str##)##delimiter##直接被两边的引号界定为了字符串字面值,str和delimiter作为宏参数根本没被展开出来。于是加了一层宏——

#define __ADD_QUOT(s) #s

专门用来添加引号。这时候又想起在有##或#的时候宏参数不会被展开,所以加了个中间层来展开参数:

#define __ADD_QUOT(s) __ADD_QUOT_AUX(s)
#define __ADD_QUOT_AUX(s) #s

就这样来来回回加了几个中间层,最后就变成了这样:

#define RAW_STR(str) \
    __RAW_STR_AUX_ADD_R(__RAW_STR_AUX_MAKE_STR(__RAW_STR_AUX_CAT(str)))
#define __RAW_STR_AUX_ADD_R(str) __RAW_STR_AUX_ADD_R1(str)
#define __RAW_STR_AUX_ADD_R1(str) R##str
#define __RAW_STR_AUX_MAKE_STR(cont) __RAW_STR_AUX_MAKE_STR1(cont)
#define __RAW_STR_AUX_MAKE_STR1(cont) #cont
#define __RAW_STR_AUX_CAT(str) __RAW_STR_AUX_CAT1(str)
#define __RAW_STR_AUX_CAT1(str) _____##(##str##)##_____

这里我用四个下划线作为delimiter,一般来说够用了。然后试了一下,还算能用。但是有个毛病没有解决:字面值的两端如果有“,”字符,会被预处理器当做宏参数的分隔符,而非宏参本身的一部分,导致不正确的结果。在写regular expression的时候,把这样的特殊字符用圆括号包起来就行了(反正对语义没有影响),比如

std::regex reg(RAW_STR(minecraft(,)));

当然,这个问题也许有更好的解决方法,以后再慢慢考虑。严格来说,这一堆宏在理论和工程上都没有什么用,就当自己寒假玩一玩了。

利用宏方便地书写raw string literals的更多相关文章

  1. C++11中的raw string literals

    作为一名C++书看得少得可怜的新手,我一直没有勇气去系统地学习一下C++ 11添加的新特性.不过,平日里逛论坛,阅读大犇们的博客,倒是了解了一些.比如,这个帖子: 如何绕过g++ 4.8.1那个不能在 ...

  2. 指针直接赋值为整型AND利用宏定义求结构体成员偏移量

    首先我们要更正一个很熟悉的概念,那就是指针不仅仅是“地址”,指针还有一个很重要的特性,那就是“类型”. 指针初始化时,“=”的右操作数; 除外,该语句表示指针为空): 所以 ; 这样的代码是不允许的. ...

  3. python raw String 获取字符串变量中的反斜杠

    常用的获取raw string的方式为: >>>r'\n' \n 不能用在字符串变量中,获取字符串变量中的反斜杠如下: tab = '\n' >>>tab.enco ...

  4. react 使用 ref 报错 ,[eslint] Using string literals in ref attributes is deprecated. (react/no-string-refs)

    react 项目中给指定元素加事件,使用到 react 的 ref 属性,Eslink 报错 [eslint] Using string literals in ref attributes is d ...

  5. Visual Studio 2010如何利用宏

    最近在做后台代码的拆分,由于机器升级,原来装的添加注释的插件不能用了. 看来只有自己想办法了,看了下利用宏添加注释与把项目展开.折叠的方式: 参考了以下几个内容: 1.Visual Studio 20 ...

  6. (转)react 使用 ref 报错 ,[eslint] Using string literals in ref attributes is deprecated. (react/no-string-refs)

    原文地址:https://www.cnblogs.com/gangerdai/p/7396226.html react 项目中给指定元素加事件,使用到 react 的 ref 属性,Eslink 报错 ...

  7. python中的raw string的使用

    背景 我们经常需要使用raw string,在应用过程中,比如要使字符串中带一些转义字符或者其他的一些符号,我们就需要保持我们的字符成为raw string. 实例 输入 s = 'fadfafa\n ...

  8. C++ raw string literal

    raw string literal 以   R"(  开头, )" 结束,是可以跨越多行的字符串字面值,转义字符如 \t\n 在raw string literal中是普通的文本 ...

  9. word中利用宏替换标点标点全角与半角

    Alt+F11,然后插入-模块: 复制下面代码到编辑窗口: Sub 半角标点符号转换为全角标点符号() '中英互译文档中将中文段落中的英文标点符号替换为中文标点符号 Dim i As Paragrap ...

随机推荐

  1. CI框架对HTML输入的处理/CI框架引用ueditor时对提交内容的默认处理

    项目里近期用到了富文本编辑器,可是写入数据的时候总是写入, <p xss="removed">内容</p> 所有的样式都会被改写成这样,xss=" ...

  2. 服务器开启FTP功能

    介绍几个比较完整的教程链接 Windows Server 2012 之文件服务器(FTP)

  3. 【JavaScript】全面解析offsetLeft、offsetTop

    假设 obj 为某个 HTML 控件.obj.offsetLeft 指 obj 距离左方或上层控件的位置,整型,单位像素. obj.offsetRight 指 obj 距离右方或上层控件的位置,整型, ...

  4. [hibernate]save()与persist()区别

    Hibernate 之所以提供与save()功能几乎完全类似的persist()方法,一方面是为了照顾JPA的用法习惯.另一方面,save()和 persist()方法还有一个区别:使用 save() ...

  5. Django Model模型

    Model简介 模型准确且唯一的描述了数据.它包含您储存的数据的重要字段和行为.一般来说,每一个模型都映射一张数据库表. 每个模型都是一个 Python 的类,这些类继承 django.db.mode ...

  6. R语言与概率统计(二) 假设检验

    > ####################5.2 > X<-c(159, 280, 101, 212, 224, 379, 179, 264, + 222, 362, 168, 2 ...

  7. spring-kafka —— 生产者消费者重要配置

    一.生产者配置 # 以逗号分隔的主机:端口对列表,用于建立与Kafka群集的初始连接 spring.kafka.producer.bootstrap-servers=TopKafka1:9092,To ...

  8. python 学习记录1

    存储 序号   分类    技术      用途 01      存储     Number  数字(不可变) String     字符串(不可变) List          列表 Tuple   ...

  9. 深入理解JVM(二)JVM内存模型

    一.前言 上文讲过了虚拟机的内存划分,即,我们将内存分为线程共享和线程私有. 线程共享的即java堆,和方法区.java堆大家可能都不会陌生:而方法区中包含了常量池,他也被称为永久代.通常方法区也会被 ...

  10. Go语言中的打包和工具链

    包 所有Go语言的程序都会组织成若干组文件,每组文件被称为一个包.这样每个包的代码都可以作为很小的复用单元,被其他项目引用. 包名惯例 给包命名的惯例是使用包所在目录的名字.并不需要所有包的名字都与别 ...