cwenzi/neuroflow-cpp
1
1import os
2import re
3import json
4import argparse
5import logging
6
7logger = logging.getLogger(__name__)
8
9HEADER_ONLY_PROS = [
10 "无需编译.cpp文件,包含即可用",
11 "模板代码可内联优化",
12 "分发简单(单头文件)",
13 "适合小型模板库",
14 "避免链接顺序问题",
15]
16HEADER_ONLY_CONS = [
17 "编译时间随包含次数线性增长",
18 "二进制体积膨胀(重复实例化)",
19 "循环依赖风险高",
20 "调试困难(模板展开复杂)",
21 "IDE代码补全和跳转受限",
22 "修改头文件触发全量重编译",
23]
24COMPILED_SEP_PROS = [
25 "编译时间可控(修改cpp仅重编译单文件)",
26 "二进制体积小(单次实例化)",
27 "可隐藏实现细节(Pimpl模式)",
28 "循环依赖易解(前向声明)",
29 "调试友好(独立编译单元)",
30 "增量编译高效",
31]
32COMPILED_SEP_CONS = [
33 "需维护hpp/cpp文件对",
34 "模板代码仍需在头文件",
35 "构建系统更复杂",
36 "分发需同时提供头文件和库文件",
37 "链接顺序可能出错",
38]
39
40
41def analyze_stub_file(file_path):
42 with open(file_path, "r", encoding="utf-8", errors="ignore") as f:
43 lines = f.readlines()
44 total = len(lines)
45 effective = sum(1 for l in lines if l.strip() and not l.strip().startswith("//"))
46 has_include = any("#include" in l for l in lines)
47 has_namespace = any("namespace" in l for l in lines)
48 has_impl = any(re.search(r'\w+::\w+', l) for l in lines if not l.strip().startswith("//"))
49 is_stub = effective <= 5 and not has_impl
50 return {
51 "path": file_path, "total_lines": total, "effective_lines": effective,
52 "is_stub": is_stub, "has_include": has_include, "has_namespace": has_namespace,
53 "has_implementation": has_impl,
54 }
55
56
57def analyze_hpp_template_ratio(hpp_dir):
58 reports = {}
59 for fname in os.listdir(hpp_dir):
60 if not fname.endswith(".hpp"):
61 continue
62 fpath = os.path.join(hpp_dir, fname)
63 with open(fpath, "r", encoding="utf-8", errors="ignore") as f:
64 lines = f.readlines()
65 total = len(lines)
66 template_lines = sum(1 for l in lines if "template" in l)
67 reports[fname] = {"total_lines": total, "template_lines": template_lines,
68 "ratio": template_lines / total if total > 0 else 0}
69 return reports
70
71
72def make_mode_decision(stub_reports, template_reports):
73 decision = {"mode": "MIXED", "details": []}
74 for fname, info in template_reports.items():
75 if info["ratio"] > 0.05:
76 decision["details"].append((fname, "HEADER_ONLY", "模板占比高,保持Header-Only"))
77 else:
78 decision["details"].append((fname, "COMPILED_SEP", "模板占比低,迁移到编译分离"))
79 return decision
80
81
82def generate_cmake_patch(current_cmake_path):
83 patch_lines = [
84 "# === NeuroFlow 文件完整性补丁 ===",
85 "# 在neuroflow_core源文件列表中添加:",
86 "# src/weight_io.cpp",
87 "# 新增训练可执行目标:",
88 "# add_executable(neuroflow_train_v2 src/train_v2.cpp)",
89 "# target_link_libraries(neuroflow_train_v2 PRIVATE neuroflow_core OpenMP::OpenMP_CXX)",
90 ]
91 return "\n".join(patch_lines)
92
93
94def generate_evaluation_report(stub_reports, template_reports, decision, cmake_patch):
95 lines = ["# NeuroFlow 空壳源文件评估报告\n"]
96 lines.append("## 空壳文件分析\n")
97 for r in stub_reports:
98 status = "✅ 空壳" if r["is_stub"] else "❌ 非空壳"
99 lines.append(f"- `{r['path']}`: {r['total_lines']}行, 有效{r['effective_lines']}行, {status}")
100 lines.append("\n## Header-Only vs 编译分离\n")
101 lines.append("### Header-Only 优点\n")
102 for p in HEADER_ONLY_PROS:
103 lines.append(f"- {p}")
104 lines.append("\n### Header-Only 缺点\n")
105 for c in HEADER_ONLY_CONS:
106 lines.append(f"- {c}")
107 lines.append("\n### 编译分离 优点\n")
108 for p in COMPILED_SEP_PROS:
109 lines.append(f"- {p}")
110 lines.append("\n### 编译分离 缺点\n")
111 for c in COMPILED_SEP_CONS:
112 lines.append(f"- {c}")
113 lines.append(f"\n## 混合模式决策: **{decision['mode']}**\n")
114 for fname, mode, reason in decision["details"]:
115 lines.append(f"- `{fname}`: **{mode}** — {reason}")
116 lines.append("\n## CMakeLists.txt 修改方案\n")
117 lines.append(f"```cmake\n{cmake_patch}\n```")
118 return "\n".join(lines)
119
120
121if __name__ == "__main__":
122 logging.basicConfig(level=logging.INFO)
123 parser = argparse.ArgumentParser(description="NeuroFlow空壳源文件评估器")
124 parser.add_argument("--source-dir", type=str, default="src", help="源文件目录")
125 parser.add_argument("--include-dir", type=str, default="include/neuroflow", help="头文件目录")
126 parser.add_argument("--cmake", type=str, default="CMakeLists.txt", help="CMakeLists.txt路径")
127 parser.add_argument("--output", type=str, default="report_stub_evaluation.md", help="输出报告路径")
128 args = parser.parse_args()
129
130 stub_reports = []
131 for f in os.listdir(args.source_dir):
132 if f.endswith(".cpp"):
133 stub_reports.append(analyze_stub_file(os.path.join(args.source_dir, f)))
134 template_reports = analyze_hpp_template_ratio(args.include_dir)
135 decision = make_mode_decision(stub_reports, template_reports)
136 cmake_patch = generate_cmake_patch(args.cmake)
137 report = generate_evaluation_report(stub_reports, template_reports, decision, cmake_patch)
138 with open(args.output, "w", encoding="utf-8") as f:
139 f.write(report)
140 logger.info(f"评估报告已保存到 {args.output}")