数学表达式中奇怪的 clang 格式

Strange clang formatting in mathematical expression

我想知道如何解决我在使用 clang-format 和 C++ 中更长的数学表达式时遇到的一些奇怪问题。假设我们在 test.h 中保存了以下示例代码:

void TestFunction() {
  int long_int_variable_name = 1;
  int result = (long_int_variable_name + long_int_variable_name) * long_int_variable_name - 1;
}

我们可以用 Google 样式格式化它:

clang-format -style=google -dump-config > .clang-format
clang-format -i -style=file test.h

这会为 result:

生成一个格式相当笨拙的表达式
void TestFunction() {
  int long_int_variable_name = 1;
  int result = (long_int_variable_name + long_int_variable_name) *
                   long_int_variable_name -
               1;
}

看起来它在乘法的第二个操作数之前添加了一个行继续缩进,但是在减法的第二个操作数之前对齐回到行的开头。除此之外,为什么它甚至把 1 放到了新的一行?看起来它试图根据数学优先级在视觉上耦合操作数 tighter/looser,但它已经过火了。

所以这里的主要问题是

我认为仅当其余代码无法放入当前行时才换行的内容将是一个很好的起点。之后,决定在哪里缩进有助于完善它。

Short and dirty answer: there's a trick that works in this case.

{
    int result = -d + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa *
                          cccccccccccccccccccccccccccccccccc;
}

如果您不喜欢这样,请为计算的中间结果命名。


这个答案是从哪里来的?有更好的答案吗?

通过一些实验,我们可以找出 clang 在这里做了什么。让我们比较一下 AlignOperands: truefalse:

// AlignOperands: true
{
    int result = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa *
                     cccccccccccccccccccccccccccccccccc -
                 d;
}

// AlignOperands: false
{
    int result = aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa *
            cccccccccccccccccccccccccccccccccc -
        d;
}

// Note: these examples use IndentWidth: 4, ContinuationIndentWidth: 8

在这两种情况下,乘法的第二个操作数比加法的第二个操作数多了一个缩进。所以看起来 clang 试图表达乘法比加法更紧密的事实。这在这里并不是真正的问题。

这里的问题是 d 被放在了它自己的行上。为什么?

看来clang-format已经决定,由于加法的第一个操作数跨越多行,所以第二个操作数之后必须有一个换行符。而且因为乘法的第二个操作数有一个额外的缩进,它看起来格外奇怪。

clang-format option 中搜索 "operand"、"indent"、"precedence"、"multiplication" 等,不会显示任何相关选项。这给我们留下了几个潜在的方法。


选项 1:找到一些创造性的技巧来编写代码,这样 clang-format 就不会搞砸了。在这种情况下,切换被加数的顺序就是诀窍:

{
    int result = -d + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa *
                          cccccccccccccccccccccccccccccccccc;
}

不幸的是,现在我们正在根据我们的格式化工具做出代码编写决定,这有点倒退并导致混淆(自然的问题出现了:为什么它 -d + x 而不是更简单的 x - d?).


选项 2:通常还具有增加代码可读性的额外好处的另一个选项是为计算的中间结果创建名称。例如:

{
    double revenue = price_per_unit * num_units_sold;
    double profit = revenue - cost;
}

这真的可以帮助那些本来会问 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa * cccccccccccccccccccccccccccccccccc 到底是什么意思的读者?


默认选项:最后一个选项是在相关代码块周围添加 clang-format offclang-format on 注释。我不太喜欢这个,因为它给源代码增加了很多混乱(on/off 的 2 行,加上另一行解释你为什么要关闭它),但它始终是一个考虑的选项基于根据上下文。