Skip to content

生成器:using namespace 注入收窄后,boost::dll::fs 等名称对模块使用者不可达 #2

Open

Description

@ZheFeng7110

背景

在 #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,原因未分析。

建议方向

  1. boost::dll::fs: 在 collect_injections 中允许"目标命名空间由别的目标库拥有、且指令所在命名空间不是 detail"的 using 指令,或者用策略逐条放行(例如在 [dll] 中声明 inject-directives = ["boost::dll::fs"]),按目标库的公开名称导出到 boost::dll::fs。
  2. detail 命名空间组: 确认没有公开 API 依赖这些 detail 写法后,在 skipped.txt 中按命名空间记录原因,不要静默丢弃。
  3. 未分析的小组(boost::function、mpl、archetype、call_traits、fusion::deque):逐个定位原因,判断是该补回,还是应该写进 skipped.txt。
  4. 把"可达导出集合"检查接进 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}))

来源:#1 代码审查,基于 e1402ed。

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions