【问题标题】:Strange clang formatting in mathematical expression数学表达式中奇怪的 clang 格式
【发布时间】:2019-09-03 21:12:24
【问题描述】:

我想知道如何解决 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 带到了新的行列?看起来它试图根据数学优先级在视觉上将操作数更紧密/更松散地耦合,但它已经过火了。

所以这里的主要问题是

  • 为什么 clang 决定以一种看似尴尬的方式来格式化它?
  • 如何才能使这种类型的表达式格式更清晰,或者我可以使用哪些选项来使这种表达式看起来合理?

我认为仅当其余代码无法放入当前行时才换行将是一个很好的起点。之后,决定缩进的位置可能有助于改进它。

【问题讨论】:

  • 您可以找到所有Clang-Format Style Options in the documentation,因此您可以编写自己的.clang-format 文件以获得您想要的任何行为(在工具的功能范围内)。您是否已经阅读过文档?如果不;这样做。
  • 读过文档了吗?
  • 是的。当然。详细地。 多次。我们在我的工作场所使用 clang 格式,我们的 .clang-format 文件经过多次修改和审查,我是它的主要作者。如果我没有仔细地彻底地阅读文档,那我就是疏忽了。
  • 通常最好使用简单的格式化规则(像这样)来帮助处理 DIFFICULT 代码,即使对于您展示的简单代码示例来说它看起来有点过分。从长远来看,对于简单代码看起来“不错”但对复杂代码具有误导性的代码格式就没有多大用处了。如果您想要更复杂的格式(比如说)对于简单的代码看起来“不错”,但也有助于理解更复杂的代码,然后自己编写规则。您会发现对格式化规则本身进行编码会更加复杂。
  • 阅读文档是一件非常痛苦的事情:阅读它一次需要大约 3 小时。如果 Stack Overflow 上有一个 5 分钟的答案,而我不是配置 .clang-format 文档的人,那么我会在任何一天阅读文档。我现在几乎完成了文档的阅读......所以回到它......

标签: c++ clang-format


【解决方案1】:

简短而肮脏的答案:在这种情况下有一个技巧。

{
    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 中搜索“操作数”、“缩进”、“优先级”、“乘法”等,不会显示任何相关选项。这给我们留下了几种可能的方法。


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

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

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


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

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

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


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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-18
    • 2014-08-26
    • 1970-01-01
    • 2019-11-08
    相关资源
    最近更新 更多