Skip to content

Commit 8050b59

Browse files
committed
the lib root reads paths apart as strings; its BMI no longer carries _Path_iterator
Removing the iterator-instantiating calls from the members changed nothing: the rule failed with the same C2801 with nothing but create_directories and path construction left, while declare.cppm -- same import set, no std::filesystem use at all -- compiled. What every failure shared was importing both std and mcpp.plugins, whose BMI carried _Path_iterator instantiated by common_base_dir / namespace_of, and then touching path. components() splits on both separators, which is the component comparison 0.5.2 introduced lexically_relative for; nothing in the lib root instantiates the iterator now. create_directories stays: the STL walks wchar_t pointers there, not the iterator.
1 parent fdb8b1a commit 8050b59

1 file changed

Lines changed: 50 additions & 27 deletions

File tree

‎src/plugins.cppm‎

Lines changed: 50 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -66,17 +66,9 @@ export namespace mcpp::plugins::names {
6666
// `a` made relative to the directory `b`, as strings: separators unified, and
6767
// the prefix stripped when `a` lies under `b`; `a` unchanged otherwise.
6868
//
69-
// A MEMBER MUST NOT INSTANTIATE `std::filesystem::path`'s ITERATOR. This lib
70-
// root may -- `common_base_dir` below calls `lexically_relative` and compiles
71-
// -- but a member that imports this module and reaches the same iterator again
72-
// fails under MSVC 14.52 (36629 and 36725, measured on xrgui's CI):
73-
//
74-
// include\filesystem(1572): error C2801: '..._Path_iterator<...>::operator =='
75-
// must be a non-static member
76-
//
77-
// the STL's hidden-friend comparison, refused when it is instantiated a second
78-
// time behind `import std` plus this module's BMI. So the members' relative-path
79-
// arithmetic is this string function, defined once here.
69+
// NOTHING IN THIS PACKAGE INSTANTIATES `std::filesystem::path`'s ITERATOR --
70+
// see `components()` below for the compiler that refuses it. The members'
71+
// relative-path arithmetic is this string function, defined once here.
8072
inline std::string relative_to(std::string a, std::string b) {
8173
for (auto& c : a) if (c == '\\') c = '/';
8274
for (auto& c : b) if (c == '\\') c = '/';
@@ -176,13 +168,44 @@ inline std::vector<std::string> split_module_name(std::string_view name) {
176168
// A single path has no common prefix with anything, so its own directory is the
177169
// base and its namespace is empty -- which is the same answer the general case
178170
// gives once a second file appears beside it.
171+
// A path's components as strings: both separators split, empty and `.`
172+
// components dropped. This is the one place the lib root reads a path apart,
173+
// and it does so WITHOUT `std::filesystem::path`'s iterator on purpose:
174+
//
175+
// A MODULE THAT INSTANTIATES `_Path_iterator` POISONS ITS IMPORTERS UNDER MSVC
176+
// 14.52. Measured on xrgui's CI (14.52.36629 and .36725): this unit compiled
177+
// while it iterated paths, and every member importing it that then touched
178+
// `std::filesystem` at all failed inside the STL --
179+
//
180+
// include\filesystem(1572): error C2801: '..._Path_iterator<...>::operator =='
181+
// must be a non-static member
182+
//
183+
// -- the iterator's hidden-friend comparison, refused when the importer meets
184+
// it both through `import std` and through this module's BMI. Removing the
185+
// calls from the members changed nothing; the instantiation had to leave the
186+
// lib root. Component comparison is what `lexically_relative` bought in 0.5.2
187+
// (a Windows separator bug), and splitting on both separators keeps that.
188+
inline std::vector<std::string> components(std::string_view path) {
189+
std::vector<std::string> out;
190+
std::string cur;
191+
auto flush = [&] { if (!cur.empty() && cur != ".") out.push_back(cur); cur.clear(); };
192+
for (char c : path) { if (c == '/' || c == '\\') flush(); else cur += c; }
193+
flush();
194+
return out;
195+
}
196+
197+
// The directory part of a path, as written: everything before the last
198+
// separator, or empty when there is none.
199+
inline std::string_view parent_of(std::string_view path) {
200+
const auto slash = path.find_last_of("/\\");
201+
return slash == std::string_view::npos ? std::string_view{} : path.substr(0, slash);
202+
}
203+
179204
inline std::string common_base_dir(std::span<const std::string> paths) {
180205
std::vector<std::string> prefix;
181206
bool first = true;
182207
for (auto const& src : paths) {
183-
std::vector<std::string> segs;
184-
for (auto const& part : std::filesystem::path(src).parent_path())
185-
if (auto s = part.string(); !s.empty() && s != ".") segs.push_back(s);
208+
auto segs = components(parent_of(src));
186209
if (first) { prefix = std::move(segs); first = false; continue; }
187210
std::size_t keep = 0;
188211
while (keep < prefix.size() && keep < segs.size() && prefix[keep] == segs[keep]) ++keep;
@@ -208,29 +231,29 @@ inline std::string common_base_dir(std::span<const std::string> paths) {
208231
// 'image' in namespace 'island_interface::kernels'`, while the same fixture
209232
// passed on Linux and macOS.
210233
//
211-
// `lexically_relative` compares COMPONENTS, so the separator a caller happened
212-
// to write is not part of the question. A base that is not a prefix yields a
213-
// path starting `..`, which is a caller error rather than a namespace; it
214-
// answers with no segments rather than with the whole absolute path, which is
215-
// what the string form produced.
234+
// COMPONENTS are compared, so the separator a caller happened to write is not
235+
// part of the question -- `components()` splits on both. A base that is not a
236+
// prefix is a caller error rather than a namespace; it answers with no
237+
// segments rather than with the whole absolute path, which is what the first
238+
// string form produced. (Through `components()` rather than
239+
// `lexically_relative` for the reason stated on it.)
216240
inline std::vector<std::string> namespace_of(std::string_view src, std::string_view base) {
217241
std::vector<std::string> out;
218-
const auto dir = std::filesystem::path(src).parent_path();
219-
auto rel = dir;
242+
auto dir = components(parent_of(src));
220243
if (!base.empty()) {
221-
rel = dir.lexically_relative(std::filesystem::path(base));
222-
if (rel.empty() || rel.begin()->string() == "..") return out;
244+
const auto b = components(base);
245+
if (b.size() > dir.size()) return out;
246+
for (std::size_t i = 0; i < b.size(); ++i) if (b[i] != dir[i]) return out;
247+
dir.erase(dir.begin(), dir.begin() + static_cast<std::ptrdiff_t>(b.size()));
223248
}
224-
for (auto const& part : rel) {
225-
auto s = part.string();
226-
if (s.empty() || s == "." || s == ".." || s == "/" || s == "\\") continue;
249+
for (auto const& s : dir) {
250+
if (s == "..") continue;
227251
// `shaders/default/` is an ordinary directory name and
228252
// `namespace default {` is not a namespace.
229253
out.push_back(identifier(s, "dir"));
230254
}
231255
return out;
232256
}
233-
234257
} // namespace mcpp::plugins::names
235258

236259

0 commit comments

Comments
 (0)