【问题标题】:empty character literal in stringified block字符串化块中的空字符文字
【发布时间】:2014-12-15 12:15:20
【问题描述】:

为了简化用 C++ 编写 SQL 查询,我使用宏

#define SQL(...) #__VA_ARGS__

它工作正常,除非出现空字符串文字(在类似

SQL(select * from foo where bar = '')

),clang(并且没有其他编译器)给出警告

warning: empty character constant [-Winvalid-pp-token]

我正在做的是否违反标准或此处过于关键(字符文字未出现在预处理器指令中)的严重破解(我仍然保留,因为它允许多行 sql 语句可读)?

编辑: 忘了提到并非所有平台都支持 C++11,因此我们不能使用原始字符串文字。我们可能会在 VisualC++ 9.0 中停留几年,而 Gcc 4.6-somesnapshot 可能会更少,但仍然是几年。该 hack 也早于 C++11。

【问题讨论】:

  • 这是一个严重的 hack :o) 我经常做的是:std::ostringstream sql; sql << "select * from table"; sql << " where id = '"; sql << id; sql << "'"; mysql_execute(&mysql, sql.str().c_str());
  • @Galik:不敢提sql的字符串操作。那应该是开火!我所说的技巧只是用合适的多行结构替换双引号,因为我们的 SQL 语句在正确分解和缩进以提高可读性时通常有 30 行以上。
  • 如果您拆分字符串文字,则很容易拥有可读的多行 sql 语句 - 即。 "select *" " from foo" " where bar = ''" 可以分成 3 行(并缩进),但仍算作一个字符串文字。
  • @SanderDeDycker:是的。除了,好吧,假设你有超过 30 行的语句,你需要更改它,包括删除、添加、连接和拆分行。添加和删​​除引号是一件很痛苦的事,更不用说大多数编辑器不再自动缩进和括号匹配了。
  • @Angew:因为试图确保插入的参数不包含任何特殊字符或正确引用很容易出错,如果参数来自用户且难以诊断,错误会导致 SQL 注入漏洞否则错误。所有数据库都支持参数绑定,它负责正确引用,而且通常这样的语句只解析一次,并且可以使用多个参数替换运行,从而节省大量时间,因为查询不需要重新解析,尤其是重新优化。跨度>

标签: c++ clang language-lawyer c-preprocessor c++03


【解决方案1】:

你正在做的是一个严重的黑客攻击,如果你正在使用 Clang(和其他现代编译器)进行编译,但不需要支持旧编译器,有一个更简单的方法:原始字符串文字。

std::string query = R"sql(
  select * from foo where bar = ''
)sql";

这甚至允许您在查询中嵌入引号和反斜杠而无需转义,如果您愿意的话。

作为参考,从 Clang 3.0、GCC 4.5 和 Visual Studio 2013 开始支持原始字符串文字。

从语言律师的角度来看,Clang 非常慷慨地允许您的代码完全编译。这是无效的。 C++14 2.2 表示在第 3 阶段(宏替换发生在第 4 阶段),源文件被分解为预处理标记和空白序列。

2.5 列出了预处理标记的语法产生。那里只有一个以单引号开头的选项,即字符文字。字符字面量在 2.14.3 中定义为

character-literal:
    ' c-char-sequence '
    <prefixed versions>

c-char-sequence:
    c-char
    c-char-sequence c-char

c-char:
    any member of the source character set except
        the single-quote ' , backslash \, or new-line character
    escape-sequence
    universal-character-name

请注意,c-char-sequence 至少包含一个 c-char,并且 c-char 不能是单引号。因此,C++ 中没有语法产生匹配''。这个序列不能出现在更大的东西(如字符串文字)之外的符合程序中。

宏处理在字符级别上是行不通的,毕竟它是在令牌级别上工作的。一切都需要先被标记化。

【讨论】:

  • 谢谢。我忘了提到它必须编译到 Visual Studio 2008 和 Gcc 4.5.0。哦,这个问题被标记为语言律师。
  • 添加了律师的东西。
猜你喜欢
  • 2012-11-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-27
  • 2020-10-30
  • 2011-11-27
  • 1970-01-01
相关资源
最近更新 更多