0

I'm working with C++17 on Windows. As far as I can see, std::filesystem doesn't have an is_root() function or something similar that tells me if a path refers directly to C: or D: or any other volumen. Am I missing something?

Currently I'm doing this:

if (path.parent_path() == path) { // } 

It looks like it's working, but I don't know if this misses any edge cases. Is there a better way?

EDIT:

I want to check if the entirety of the path is just the volume name (maybe followed by an optional slash or backslash).

So, if there was a function like this, I'd like it to behave as follows:

namespace fs = std::filesystem; is_root(fs::path{ "C:" }); // true is_root(fs::path{ "D:\\"}); // true is_root(fs::path{ "C:/users" }) // false is_root(fs::current_path()) // usually false, unless the executable was started directly in C: or D: or any other drive 
10
  • Are you asking if a path has a volume name in it, or if the entirety of the path is just a volume name? Commented Apr 17, 2020 at 14:23
  • @NicolBolas the entirety of the path shall be just that volumen name. I will clarify that in my question. Thanks Commented Apr 17, 2020 at 14:24
  • Does this answer the question? Obviously use find instead of rfind. Commented Apr 17, 2020 at 14:27
  • @Cortex not really. I want to know if my path is the volume (the root of it), not if it contains a volume. Commented Apr 17, 2020 at 14:28
  • 2
    I don't have a windows installation right here, but wouldn't it be better to use std::filesystem::path::root_path on path and then check if they are equal? Commented Apr 17, 2020 at 14:31

1 Answer 1

1

First, check to see if it has_root_name(); without one, it obviously doesn't specify a volume.

The hard part is to figure out if it only has a root-name. That's complicated because you also want to ignore the root-directory of the path if it specifies one.

The iterator range is a good, performance-friendly solution here. If it has a root-name, then begin() points to that root-name. So if you increment it, it points to the next component in the path.

If the path has both a root-name and a root-directory, then the component after the root-name is the root-directory. So if you increment past that, the iterator either is pointing to an additional component or it has reached the end of the range. And if it's at the end, then there's nothing left and you know that the path is "just a volume".

The code would look like this:

bool is_volume(const fs::path &p) { if(!p.has_root_name()) return false; auto it = p.begin(); ++it; //Skip the root-name if(p.has_root_directory()) ++it; //Skip the root-directory. if(it == p.end()) return true; return false; } 
Sign up to request clarification or add additional context in comments.

6 Comments

I was not aware that the slash behind the root name matters. I always thought it was optional. Then I need to clarify if for my task if I really need to cover "C:" as well. Either way, this looks good. I will test it.
@sebrockm: The path "c:" specifies the volume "c:". The path "c:\" specifies the root directory of the volume "c:". Each volume has its own current directory; the path "c:name" specifies the "name" file/directory in the current directory of the "c:" volume. By contrast, "c:\name" specifies the "name" file/directory in the root directory of the "c:" volume.
Thanks for the clarification!
Note that drive "X:" is implemented as a link in the object namespace. The link usually targets a volume device, but it may target an arbitrary path on a local or remote device (e.g. a subst or mapped drive). So not all root paths on a DOS drive, e.g. "X:\", are actually volume mountpoints.
Also, a volume device has other usable names, such as "\\?\BootPartition" or "\\?\Volume{GUID}", set via DefineDosDeviceW. So some paths without a drive-letter name are actually volume mountpoint paths, e.g. "\\?\BootPartition\".
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.