You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用clang-format将预处理指令(#)与代码块对齐?

如何让clang-format对齐代码块内的预处理指令

我在用clang-format格式化C++代码,希望把带#的预处理指令和周围代码块对齐,但clang-format的默认规则让这些指令的缩进和函数、循环、条件语句不匹配。

当前格式化效果

#pragma region WIFI {
#ifdef __CONFIG_WIFI__

void wifi_handler()
{
  switch(wifi.connectionMode)
  {
    case WIFI_STA:
    {
  #ifdef __CONFIG_WEBSOCKET__
      if(WiFi.status() == WL_CONNECTED && webSocketClient.isConnected()) webSocketClient.loop();
  #endif
      break;
    }
    case WIFI_AP:
    {
  #ifdef __CONFIG_FTP__
      ftpSrv.handleFTP();
  #endif

  #ifdef __CONFIG_WEB__
      webServer.handleClient();
  #endif

  #ifdef __CONFIG_WEBSOCKET__
      webSocketServer.loop();
  #endif
      break;
    }
  }
}

#endif
#pragma endregion }

期望效果

#pragma region WIFI {
#ifdef __CONFIG_WIFI__

void wifi_handler()
{
  switch(wifi.connectionMode)
  {
    case WIFI_STA:
    {
      #ifdef __CONFIG_WEBSOCKET__
      if(WiFi.status() == WL_CONNECTED && webSocketClient.isConnected()) webSocketClient.loop();
      #endif
      break;
    }
    case WIFI_AP:
    {
      #ifdef __CONFIG_FTP__
      ftpSrv.handleFTP();
      #endif
      
      #ifdef __CONFIG_WEB__
      webServer.handleClient();
      #endif
      
      #ifdef __CONFIG_WEBSOCKET__
      webSocketServer.loop();
      #endif
      break;
    }
  }
}

#endif
#pragma endregion }

我的.clang-format配置

---
BasedOnStyle: LLVM
AccessModifierOffset: -2
AlignAfterOpenBracket: Align
AlignArrayOfStructures: Right
AlignConsecutiveAssignments:
  Enabled: true
  AcrossEmptyLines: false
  AcrossComments: false
  AlignCompound: true
  PadOperators: true
AlignConsecutiveBitFields:
  Enabled: true
  AcrossEmptyLines: false
  AcrossComments: false
  AlignCompound: true
  PadOperators: true
AlignConsecutiveDeclarations:
  Enabled: true
  AcrossEmptyLines: false
  AcrossComments: false
  AlignCompound: true
  PadOperators: true
AlignConsecutiveMacros:
  Enabled: true
  AcrossEmptyLines: false
  AcrossComments: false
  AlignCompound: true
  PadOperators: true
AlignConsecutiveShortCaseStatements:
  Enabled: true
  AcrossEmptyLines: false
  AcrossComments: true
  AlignCaseColons: true
AlignEscapedNewlines: Left
AlignOperands: AlignAfterOperator
AlignTrailingComments:
  Kind: Always
  OverEmptyLines: 0
AllowAllArgumentsOnNextLine: false
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: Always
AllowShortCaseLabelsOnASingleLine: false
AllowShortEnumsOnASingleLine: true
AllowShortFunctionsOnASingleLine: All
AllowShortIfStatementsOnASingleLine: AllIfsAndElse
AllowShortLambdasOnASingleLine: All
AllowShortLoopsOnASingleLine: true
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: MultiLine
AttributeMacros:
  - __capability
BinPackArguments: false
BinPackParameters: false
BitFieldColonSpacing: Both
BraceWrapping:
  AfterCaseLabel: true
  AfterClass: true
  AfterControlStatement: Always
  AfterEnum: true
  AfterFunction: true
  AfterNamespace: true
  AfterObjCDeclaration: false
  AfterStruct: true
  AfterUnion: true
  AfterExternBlock: true
  BeforeCatch: true
  BeforeElse: true
  BeforeLambdaBody: true
  BeforeWhile: true
  IndentBraces: false
  SplitEmptyFunction: true
  SplitEmptyRecord: true
  SplitEmptyNamespace: true
BreakAfterAttributes: Never
BreakAfterJavaFieldAnnotations: false
BreakArrays: true
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Custom
BreakBeforeConceptDeclarations: Allowed
BreakBeforeInlineASMColon: OnlyMultiline
BreakBeforeTernaryOperators: false
BreakConstructorInitializers: AfterColon
BreakInheritanceList: AfterComma
BreakStringLiterals: false
ColumnLimit: 180
CommentPragmas: "^ IWYU pragma:"
CompactNamespaces: false
ConstructorInitializerIndentWidth: 2
ContinuationIndentWidth: 2
Cpp11BracedListStyle: true
DerivePointerAlignment: false
DisableFormat: false
EmptyLineAfterAccessModifier: Always
EmptyLineBeforeAccessModifier: LogicalBlock
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: true
ForEachMacros:
  - foreach
  - Q_FOREACH
  - BOOST_FOREACH
IfMacros:
  - KJ_IF_MAYBE
IncludeBlocks: Preserve
IncludeCategories:
  - Regex: ^"(llvm|llvm-c|clang|clang-c)/
    Priority: 2
    SortPriority: 0
    CaseSensitive: false
  - Regex: ^(<|"gtest|gmock|isl|json)/)
    Priority: 3
    SortPriority: 0
    CaseSensitive: false
  - Regex: .*
    Priority: 1
    SortPriority: 0
    CaseSensitive: false
IncludeIsMainRegex: (Test)?$
IncludeIsMainSourceRegex: ""
IndentAccessModifiers: false
IndentCaseBlocks: false
IndentCaseLabels: true
IndentExternBlock: AfterExternBlock
IndentGotoLabels: true
IndentPPDirectives: BeforeHash
IndentRequiresClause: true
IndentWidth: 2
IndentWrappedFunctionNames: true
InsertBraces: false
InsertNewlineAtEOF: true
InsertTrailingCommas: None
IntegerLiteralSeparator:
  Binary: 0
  BinaryMinDigits: 0
  Decimal: 0
  DecimalMinDigits: 0
  Hex: 0
  HexMinDigits: 0
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtEOF: true
KeepEmptyLinesAtTheStartOfBlocks: true
LambdaBodyIndentation: Signature
Language: Cpp
LineEnding: DeriveLF
MacroBlockBegin: ""
MacroBlockEnd: ""
MaxEmptyLinesToKeep: 1
NamespaceIndentation: Inner
ObjCBinPackProtocolList: Auto
ObjCBlockIndentWidth: 2
ObjCBreakBeforeNestedBlockParam: true
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PPIndentWidth: -1
PackConstructorInitializers: NextLine
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 19
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakOpenParenthesis: 0
PenaltyBreakString: 1000
PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 1000000
PenaltyIndentedWhitespace: 0
PenaltyReturnTypeOnItsOwnLine: 60
PointerAlignment: Right
QualifierAlignment: Leave
ReferenceAlignment: Pointer
ReflowComments: false
RemoveBracesLLVM: false
RemoveParentheses: Leave
RemoveSemicolon: false
RequiresClausePosition: SingleLine
RequiresExpressionIndentation: OuterScope
SeparateDefinitionBlocks: Leave
ShortNamespaceLines: 1
SortIncludes: Never
SortJavaStaticImport: Before
SortUsingDeclarations: LexicographicNumeric
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: true
SpaceAroundPointerQualifiers: Default
SpaceBeforeAssignmentOperators: true
SpaceBeforeCaseColon: false
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeJsonColon: false
SpaceBeforeParens: Custom
SpaceBeforeParensOptions:
  AfterControlStatements: false
  AfterForeachMacros: true
  AfterFunctionDeclarationName: false
  AfterFunctionDefinitionName: false
  AfterIfMacros: true
  AfterOverloadedOperator: false
  AfterRequiresInClause: true
  AfterRequiresInExpression: true
  BeforeNonEmptyParentheses: false
SpaceBeforeRangeBasedForLoopColon: false
SpaceBeforeSquareBrackets: false
SpaceInEmptyBlock: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: Never
SpacesInContainerLiterals: false
SpacesInLineCommentPrefix:
  Minimum: 1
  Maximum: -1
SpacesInParens: Never
SpacesInParensOptions:
  InConditionalStatements: false
  InCStyleCasts: false
  InEmptyParentheses: false
  Other: false
SpacesInSquareBrackets: false
Standard: Latest
StatementAttributeLikeMacros:
  - Q_EMIT
StatementMacros:
  - Q_UNUSED
  - QT_REQUIRE_VERSION
TabWidth: 8
UseTab: Never
VerilogBreakBetweenInstancePorts: true
WhitespaceSensitiveMacros:
  - BOOST_PP_STRINGIZE
  - CF_SWIFT_NAME
  - NS_SWIFT_NAME
  - PP_STRINGIZE
  - STRINGIZE
BracedInitializerIndentWidth: 2

能不能通过clang-format配置实现?

不能。clang-format对预处理指令的缩进逻辑是独立的,没有配置项能让#ifdef/#endif这类指令自动跟随所在代码块的缩进级别。现有的IndentPPDirectives选项只有三个可选值:

  • None:预处理指令无缩进
  • AfterHash:#号无缩进,指令内容缩进
  • BeforeHash:#号前添加缩进,指令内容和#对齐

这些选项都无法让预处理指令和周围的代码块(比如case块内的代码)保持一致的缩进。

替代方案:用脚本调整缩进

可以用Python脚本遍历代码行,跟踪当前的缩进级别,然后调整预处理指令的缩进。以下是一个示例脚本:

import re

def adjust_pp_directive_indent(file_path, indent_width=2):
    indent_level = 0
    indent_char = ' ' * indent_width
    modified_lines = []
    
    with open(file_path, 'r') as f:
        lines = f.readlines()
    
    for line in lines:
        stripped_line = line.strip()
        # 处理增加缩进的情况:遇到{且不是预处理指令的一部分
        if stripped_line.endswith('{') and not stripped_line.startswith('#'):
            modified_lines.append(line)
            indent_level += 1
            continue
        # 处理减少缩进的情况:遇到}且不是预处理指令的一部分
        if stripped_line.startswith('}') and not stripped_line.startswith('#'):
            indent_level = max(0, indent_level - 1)
            modified_lines.append(line)
            continue
        # 处理预处理指令(跳过region相关pragma)
        if stripped_line.startswith('#') and not stripped_line.startswith('#pragma region') and not stripped_line.startswith('#pragma endregion'):
            # 去掉原有缩进
            raw_pp_line = re.sub(r'^\s+', '', line)
            # 添加当前级别缩进
            indented_pp_line = indent_char * indent_level + raw_pp_line
            modified_lines.append(indented_pp_line)
            continue
        # 其他行保持原样
        modified_lines.append(line)
    
    with open(file_path, 'w') as f:
        f.writelines(modified_lines)

# 使用示例:替换为你的代码文件路径
adjust_pp_directive_indent('your_code.cpp', indent_width=2)

脚本说明

  1. 通过代码中的{和}跟踪当前缩进级别
  2. 跳过#pragma region和#pragma endregion(可根据需求修改过滤规则)
  3. 对目标预处理指令,清除原有缩进后添加当前级别对应的空格
  4. 其他代码行保持不变

使用步骤

  1. 先运行clang-format完成基础格式化
  2. 再运行该脚本调整预处理指令的缩进

这样就能让预处理指令和周围代码块的缩进对齐了。

内容的提问来源于stack exchange,提问作者Mahdi Ghasemi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 06:47:33