Skip to main content
Glama
include_guard_checker.py8.56 kB
"""C++ 头文件包含保护检查工具""" import re from typing import Tuple, Optional, List from pathlib import Path class IncludeGuardChecker: """包含保护检查器""" def check_include_guard( self, code: str, file_path: Optional[str] = None ) -> Tuple[bool, str, List[str]]: """ 检查头文件的包含保护 Args: code: 头文件代码 file_path: 可选的文件路径,用于生成建议的保护宏名 Returns: (是否符合规范, 详细说明, 建议列表) """ lines = code.strip().split('\n') if len(lines) < 3: return False, "文件太短,无法包含有效的包含保护", [] # 检查 #pragma once has_pragma_once = self._check_pragma_once(code) # 检查传统的 #ifndef/#define/#endif 保护 has_traditional_guard, guard_name = self._check_traditional_guard(code) suggestions = [] details = "" if has_pragma_once: details = "✓ 使用了 #pragma once\n\n" details += "说明: #pragma once 是现代编译器广泛支持的简洁方式。\n" details += "优点: 简洁、避免宏名冲突\n" details += "注意: 大多数编译器支持,但不是 C++ 标准的一部分\n" return True, details, [] if has_traditional_guard: # 检查保护宏命名是否合理 is_valid_name, name_message = self._validate_guard_name(guard_name, file_path) details = f"✓ 使用了传统的包含保护\n" details += f"保护宏名: {guard_name}\n\n" if is_valid_name: details += f"命名检查: ✓ 符合规范\n" details += f"{name_message}\n" else: details += f"命名检查: ✗ 可以改进\n" details += f"{name_message}\n\n" # 生成建议的宏名 if file_path: suggested_names = self._generate_guard_names(file_path) suggestions = suggested_names details += "建议的保护宏名:\n" for name in suggested_names: details += f" • {name}\n" details += "\n包含保护规范:\n" details += " • 宏名应全大写,使用下划线分隔\n" details += " • 包含文件路径或项目名作为前缀,避免冲突\n" details += " • 以 _H、_HPP 或 _H_ 结尾\n" details += " • 避免以下划线开头(保留给编译器)\n" return True, details, suggestions # 没有任何保护 details = "✗ 缺少包含保护!\n\n" details += "头文件应使用以下方式之一防止重复包含:\n\n" details += "方式1: #pragma once (推荐)\n" details += "```cpp\n" details += "#pragma once\n\n" details += "// 头文件内容\n" details += "```\n\n" details += "方式2: 传统包含保护\n" details += "```cpp\n" if file_path: guard_name = self._generate_guard_names(file_path)[0] suggestions = self._generate_guard_names(file_path) else: guard_name = "MY_HEADER_H" details += f"#ifndef {guard_name}\n" details += f"#define {guard_name}\n\n" details += "// 头文件内容\n\n" details += f"#endif // {guard_name}\n" details += "```\n" return False, details, suggestions def _check_pragma_once(self, code: str) -> bool: """检查是否使用 #pragma once""" return bool(re.search(r'^\s*#\s*pragma\s+once\s*$', code, re.MULTILINE)) def _check_traditional_guard(self, code: str) -> Tuple[bool, Optional[str]]: """ 检查传统的 #ifndef/#define/#endif 保护 Returns: (是否存在, 保护宏名) """ lines = code.split('\n') # 查找 #ifndef ifndef_pattern = re.compile(r'^\s*#\s*ifndef\s+([A-Z_][A-Z0-9_]*)\s*$') define_pattern = re.compile(r'^\s*#\s*define\s+([A-Z_][A-Z0-9_]*)\s*$') endif_pattern = re.compile(r'^\s*#\s*endif') ifndef_macro = None define_macro = None # 找到第一个 #ifndef(跳过注释) for line in lines: stripped = line.strip() if stripped.startswith('//') or stripped.startswith('/*'): continue match = ifndef_pattern.match(line) if match: ifndef_macro = match.group(1) break if not ifndef_macro: return False, None # 检查紧跟的 #define found_ifndef = False for line in lines: if found_ifndef: match = define_pattern.match(line) if match: define_macro = match.group(1) break # 如果不是空行或注释,则失败 stripped = line.strip() if stripped and not stripped.startswith('//'): break elif ifndef_pattern.match(line): found_ifndef = True # 检查 #ifndef 和 #define 的宏名是否一致 if ifndef_macro and define_macro and ifndef_macro == define_macro: # 检查文件末尾是否有 #endif has_endif = False for line in reversed(lines): stripped = line.strip() if stripped and not stripped.startswith('//'): if endif_pattern.match(line): has_endif = True break if has_endif: return True, ifndef_macro return False, None def _validate_guard_name( self, guard_name: str, file_path: Optional[str] = None ) -> Tuple[bool, str]: """ 验证保护宏名是否合理 Returns: (是否有效, 说明信息) """ issues = [] # 检查是否以下划线开头(不推荐) if guard_name.startswith('_'): issues.append("不应以下划线开头(保留给编译器实现)") # 检查是否全大写 if not guard_name.isupper(): issues.append("应使用全大写字母") # 检查是否包含双下划线(不推荐) if '__' in guard_name: issues.append("不应包含连续的双下划线(保留标识符)") # 检查是否有合适的后缀 valid_suffixes = ['_H', '_HPP', '_H_', '_INCLUDED'] has_valid_suffix = any(guard_name.endswith(suffix) for suffix in valid_suffixes) if not has_valid_suffix: issues.append(f"建议以 {', '.join(valid_suffixes)} 之一结尾") # 检查长度 if len(guard_name) < 5: issues.append("宏名太短,容易冲突") if issues: return False, "发现以下问题:\n • " + "\n • ".join(issues) else: return True, "命名符合规范" def _generate_guard_names(self, file_path: str) -> List[str]: """根据文件路径生成建议的保护宏名""" path = Path(file_path) suggestions = [] # 获取文件名(不含扩展名) name = path.stem.upper() # 方案1: 简单的文件名 + _H suggestions.append(f"{name}_H") # 方案2: 文件名 + _HPP if path.suffix.lower() == '.hpp': suggestions.append(f"{name}_HPP") # 方案3: 包含父目录 if len(path.parts) > 1: parent = path.parts[-2].upper() suggestions.append(f"{parent}_{name}_H") # 方案4: 完整路径(适合项目层级) # 例如: src/utils/helper.h -> SRC_UTILS_HELPER_H if len(path.parts) > 2: full_path = '_'.join(part.upper() for part in path.parts[:-1]) suggestions.append(f"{full_path}_{name}_H") # 转换为合法的宏名(替换特殊字符) suggestions = [re.sub(r'[^A-Z0-9_]', '_', s) for s in suggestions] # 去重 return list(dict.fromkeys(suggestions))[:3] # 全局实例 _checker = None def get_checker() -> IncludeGuardChecker: """获取全局包含保护检查器实例""" global _checker if _checker is None: _checker = IncludeGuardChecker() return _checker

Implementation Reference

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/SongJiangzhou/cpp_guidelines'

If you have feedback or need assistance with the MCP directory API, please join our Discord server