@@ -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.
8072inline 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+
179204inline 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.)
216240inline 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