Repository navigation
Open
Description
背景
在 #1 中,我统计了"使用方导入每个模块后实际能看到的名称"(自身 .inc 加上经 export import 传递可达的所有模块的 .inc),并与 main 比较。修复审查中的第 1–4 项(e1402edb)后,仍有 46 个模块、852 个不同名称(按模块累计 6867 个)在 main 中可达、在 PR 中不可达。
这些名称大多不是 #1 那四个缺陷造成的,而是 PR 有意收窄了 using namespace 注入的处理范围。tools/gen_exports.py 的 collect_injections 现在只把 ADL 屏障形态的 using 指令视为公开写法,即指令指向自身作用域内嵌套的命名空间;指向兄弟命名空间的 using 指令被当作实现细节,直接跳过。
其中多数是 detail 命名空间,丢掉合理。但 boost::dll::fs 是 Boost.DLL 的公开 API,应该算回归。
丢失的名称(按命名空间分组)
| 名称数 | 命名空间 | 受影响模块 | 来源 / 初步判断 |
|---|---|---|---|
| 58 | boost::dll::fs |
dll | boost/dll/config.hpp:namespace boost { namespace dll { namespace fs { using namespace boost::filesystem; /* 或 std::filesystem */ } } }。公开 API(如 boost::dll::fs::path、fs::exists),需要恢复。 |
| 356 | boost::numeric::ublas::detail |
15 个(accumulators、asio、beast、geometry、graph…) | boost/numeric/ublas/matrix.hpp 等:namespace detail { using namespace boost::numeric::ublas; }。detail,丢掉可以接受,需确认。 |
| 355 | boost::serialization::cpp_int_detail |
geometry、graph、multiprecision | boost/multiprecision/cpp_int/serialize.hpp:using namespace boost::multiprecision;。detail,丢掉可以接受,需确认。 |
| 55 | boost::sort::blk_detail |
sort | boost/sort/block_indirect_sort/blk_detail/block.hpp:using namespace boost::sort::common;。detail,丢掉可以接受,需确认。 |
| 16 | boost::mpl、boost::mpl::aux |
37 个 | 原因未分析;其中 mpl::aux::template_arity、mpl::order_impl 已在 skipped.txt 中。 |
| 5 | boost |
accumulators、bimap、functional、multi_array、numeric(boost::function);regex(*_archetype ×3);wave(boost::call_traits) |
原因未分析。 |
| 4 | boost::parameter |
accumulators、geometry、graph、heap、lockfree | 已在 skipped.txt 中(有策略说明)。 |
| 2 | boost::container |
flyweight、interprocess | transform_iterator / make_transform_iterator。main 只是借问题 3 的错误写法导出了它们;interprocess 现在导出的是正确写法 boost::interprocess::transform_iterator。可以接受。 |
| 1 | boost::fusion |
hana、yap | boost::fusion::deque,原因未分析。 |
建议方向
boost::dll::fs: 在collect_injections中允许"目标命名空间由别的目标库拥有、且指令所在命名空间不是 detail"的 using 指令,或者用策略逐条放行(例如在[dll]中声明inject-directives = ["boost::dll::fs"]),按目标库的公开名称导出到boost::dll::fs。- detail 命名空间组: 确认没有公开 API 依赖这些 detail 写法后,在
skipped.txt中按命名空间记录原因,不要静默丢弃。 - 未分析的小组(
boost::function、mpl、archetype、call_traits、fusion::deque):逐个定位原因,判断是该补回,还是应该写进skipped.txt。 - 把"可达导出集合"检查接进 verify-scripts: 与基线比较时,少掉的名称必须出现在
skipped.txt中,否则报错,防止今后再静默丢失。
验收标准
-
import mcpplibs.mwrap.boost.dll;之后可以使用boost::dll::fs::path等名称,并在tests/dll.cpp中加断言覆盖 - detail 命名空间组确认可以丢弃,并在
skipped.txt中说明原因 - 未分析的小组逐个确认,补回或说明原因
- verify-scripts 中加入可达导出集合检查
复现脚本(可达导出集合)
用法:python3 reach.py <rev|WORKTREE> > out.json,然后比较两个 rev 每个模块名称集合的差集。
"""Reachable export set per module: own .inc names + transitively export-imported modules.
usage: reach.py <rev|WORKTREE> -> prints JSON {lib: [names]} to stdout"""
import json, re, subprocess, sys
REPO = "/home/linuxbrew/tfh/boost-m"
rev = sys.argv[1]
def files(prefix):
if rev == "WORKTREE":
out = subprocess.run(["git", "-C", REPO, "ls-files", "-co", "--exclude-standard", prefix], capture_output=True, text=True).stdout
else:
out = subprocess.run(["git", "-C", REPO, "ls-tree", "-r", "--name-only", rev, prefix], capture_output=True, text=True).stdout
return out.split()
def read(p):
if rev == "WORKTREE":
try: return open(REPO + "/" + p, encoding="utf-8").read()
except FileNotFoundError: return ""
return subprocess.run(["git", "-C", REPO, "show", f"{rev}:{p}"], capture_output=True, text=True).stdout
NSOPEN = re.compile(r"namespace\s+(\w+)\s*\{")
def inc_names(text):
names, key = set(), None
for ln in text.splitlines():
s = ln.strip()
if s.startswith("export namespace") or (s.startswith("namespace") and key is None and s.endswith("{")):
key = NSOPEN.findall(s); continue
if s and set(s) == {"}"}:
key = None; continue
m = re.match(r"using\s+(?!namespace)([\w:]+)\s*;", s)
if m and key is not None:
names.add("::".join(key) + "::" + m.group(1).rsplit("::", 1)[-1])
return names
own, imports = {}, {}
for p in files("generated/exports"):
if p.endswith(".inc"):
own[p.split("/")[-1][:-4]] = inc_names(read(p))
PFX = "mcpplibs.mwrap.boost."
for p in files("src"):
if p.endswith(".cppm"):
lib = p.split("/")[-1][:-5]
body = read(p).partition("export module ")[2]
imports[lib] = [i[len(PFX):] for i in re.findall(r"export\s+import\s+([\w.]+)\s*;", body) if i.startswith(PFX)]
memo = {}
def reach(lib, stack=()):
if lib in memo: return memo[lib]
s = set(own.get(lib, ()))
for d in imports.get(lib, ()):
if d not in stack: s |= reach(d, stack + (lib,))
memo[lib] = s
return s
print(json.dumps({lib: sorted(reach(lib)) for lib in imports}))Metadata
Metadata
Assignees
Labels
No labels
Activity