Vix.cpp v2.7.0 is here Read the blog
Skip to content

Separators and Styles

The Path module supports POSIX, Windows, and native path styles. This matters because Vix can run on one platform while preparing paths for another. A command running on Linux may still need to generate Windows-style paths, and a project tool may want stable POSIX output even when it is executed on Windows.

For normal use, include the public header:

cpp
#include <vix/path.hpp>

All public APIs live in the vix::path namespace.

Path styles

PathStyle controls the syntax rules used by path operations.

cpp
enum class PathStyle
{
  Native,
  Posix,
  Windows
};

PathStyle::Native follows the platform where the program is running. PathStyle::Posix uses POSIX-style output and prefers /. PathStyle::Windows uses Windows-style output and prefers \.

cpp
vix::path::PathOptions options;
options.style = vix::path::PathStyle::Posix;

Choosing the style explicitly is usually better in tooling code. It makes the expected output clear and avoids accidental differences between machines.

Preferred separator

Use preferred_separator() when code needs the separator character for a style.

cpp
char sep = vix::path::preferred_separator(
  vix::path::PathStyle::Posix
);

// sep == '/'
cpp
char sep = vix::path::preferred_separator(
  vix::path::PathStyle::Windows
);

// sep == '\\'

For PathStyle::Native, the result depends on the current platform.

cpp
char sep = vix::path::preferred_separator(
  vix::path::PathStyle::Native
);

This helper is useful when a caller needs to build a small display string or align custom logic with the same separator style used by join() and normalize().

Recognized separators

The module recognizes both / and \ as separators during lexical processing.

cpp
bool slash = vix::path::is_separator('/');
// slash == true
cpp
bool backslash = vix::path::is_separator('\\');
// backslash == true

A normal character is not a separator.

cpp
bool regular = vix::path::is_separator('x');
// regular == false

Recognizing both separator characters makes the module tolerant when reading path strings. The selected style still controls which separator is preferred in generated output.

Leading and trailing separators

Use has_leading_separator() to check whether a path starts with a separator.

cpp
bool leading = vix::path::has_leading_separator("/tmp");

// leading == true

Use has_trailing_separator() to check whether a path ends with a separator.

cpp
bool trailing = vix::path::has_trailing_separator("/tmp/");

// trailing == true
cpp
bool trailing = vix::path::has_trailing_separator("/tmp/file.txt");

// trailing == false

These helpers are simple lexical checks. They do not inspect the filesystem and they do not decide whether the path is a directory.

POSIX style

POSIX style uses / as the preferred separator. A path starting with a separator is treated as absolute.

cpp
bool absolute = vix::path::is_absolute(
  "/usr/bin",
  vix::path::PathStyle::Posix
);

// absolute == true

A path without a leading separator is relative.

cpp
bool relative = vix::path::is_relative(
  "usr/bin",
  vix::path::PathStyle::Posix
);

// relative == true

When POSIX style is used with join() or normalize(), generated output uses /.

cpp
vix::path::PathOptions options;
options.style = vix::path::PathStyle::Posix;

auto joined = vix::path::join("/usr/", "/bin", options);

if (!joined) {
  return joined.error();
}

// joined.value() == "/usr/bin"

Windows style

Windows style uses \ as the preferred separator. The module recognizes drive-root paths such as C:\Windows.

cpp
bool absolute = vix::path::is_absolute(
  "C:\\Windows",
  vix::path::PathStyle::Windows
);

// absolute == true

It also recognizes UNC-style paths.

cpp
bool absolute = vix::path::is_absolute(
  "\\\\server\\share",
  vix::path::PathStyle::Windows
);

// absolute == true

A path without a Windows root is relative.

cpp
bool relative = vix::path::is_relative(
  "docs\\readme.md",
  vix::path::PathStyle::Windows
);

// relative == true

When Windows style is selected, join() and normalize() produce Windows-style output.

cpp
vix::path::PathOptions options;
options.style = vix::path::PathStyle::Windows;

auto joined = vix::path::join(
  "C:\\Users\\",
  "\\gaspard",
  options
);

if (!joined) {
  return joined.error();
}

// joined.value() == "C:\\Users\\gaspard"

Native style

PathStyle::Native follows the platform where the program is compiled and executed.

cpp
vix::path::PathOptions options;
options.style = vix::path::PathStyle::Native;

This is useful when the path should match the current machine. It is less useful for generated project files, cross-platform manifests, or documentation examples where the expected output should be the same everywhere.

For stable tooling behavior, prefer PathStyle::Posix or PathStyle::Windows when the target format is known.

Separator conversion through normalization

Normalization can read mixed separators and rebuild the path with the preferred separator for the selected style.

cpp
vix::path::PathOptions options;
options.style = vix::path::PathStyle::Posix;

auto path = vix::path::normalize(
  "src\\core//main.cpp",
  options
);

if (!path) {
  return path.error();
}

// path.value() == "src/core/main.cpp"

With Windows style, the generated output uses \.

cpp
vix::path::PathOptions options;
options.style = vix::path::PathStyle::Windows;

auto path = vix::path::normalize(
  "C:/temp//foo\\bar",
  options
);

if (!path) {
  return path.error();
}

// path.value() == "C:\\temp\\foo\\bar"

This is one of the reasons the style belongs in PathOptions. The same input string can be normalized into different output forms depending on the target syntax.

Repeated separators

By default, repeated separators are collapsed during normalization.

cpp
vix::path::PathOptions options;
options.style = vix::path::PathStyle::Posix;

auto path = vix::path::normalize(
  "/a//b///c",
  options
);

if (!path) {
  return path.error();
}

// path.value() == "/a/b/c"

This keeps generated paths clean and avoids carrying accidental separator noise from user input, configuration files, or string concatenation.

Trailing separators

By default, normalization does not preserve a trailing separator.

cpp
vix::path::PathOptions options;
options.style = vix::path::PathStyle::Posix;

auto path = vix::path::normalize(
  "build/generated/",
  options
);

if (!path) {
  return path.error();
}

// path.value() == "build/generated"

Set preserve_trailing_separator when the trailing separator should remain part of the output representation.

cpp
vix::path::PathOptions options;
options.style = vix::path::PathStyle::Posix;
options.preserve_trailing_separator = true;

auto path = vix::path::normalize(
  "build/generated/",
  options
);

if (!path) {
  return path.error();
}

// path.value() == "build/generated/"

This option preserves a trailing separator only when the input already had one. It does not add a separator to a path that did not end with one.

Style and roots

The selected style also affects how roots are understood. In POSIX style, / is the main root form.

cpp
bool root = vix::path::is_absolute(
  "/project",
  vix::path::PathStyle::Posix
);

// root == true

In Windows style, drive-root paths and UNC-style paths are recognized.

cpp
bool drive = vix::path::is_absolute(
  "C:\\project",
  vix::path::PathStyle::Windows
);

// drive == true
cpp
bool unc = vix::path::is_absolute(
  "\\\\server\\share",
  vix::path::PathStyle::Windows
);

// unc == true

Roots matter most when computing relative paths. A relative path can only be computed when the target and base have compatible roots.

cpp
vix::path::PathOptions options;
options.style = vix::path::PathStyle::Windows;

auto rel = vix::path::relative(
  "D:\\docs\\file.txt",
  "C:\\base",
  options
);

if (!rel) {
  // incompatible roots
}

When incompatible roots are acceptable and the caller still wants a usable path, use lexically_proximate() instead.

A practical style workflow

The following example normalizes the same conceptual path for POSIX and Windows output.

cpp
#include <iostream>
#include <vix/path.hpp>

int main()
{
  vix::path::PathOptions posix;
  posix.style = vix::path::PathStyle::Posix;

  auto a = vix::path::normalize(
    "src\\core//main.cpp",
    posix
  );

  if (!a) {
    std::cerr << a.error().message() << '\n';
    return 1;
  }

  vix::path::PathOptions windows;
  windows.style = vix::path::PathStyle::Windows;

  auto b = vix::path::normalize(
    "src\\core//main.cpp",
    windows
  );

  if (!b) {
    std::cerr << b.error().message() << '\n';
    return 1;
  }

  std::cout << "posix: " << a.value() << '\n';
  std::cout << "windows: " << b.value() << '\n';

  return 0;
}

The input path is only text. The selected style decides how that text is interpreted and rebuilt.

Practical rule

Use explicit styles when the output format matters. Use POSIX for project metadata, generated documentation examples, or portable internal paths. Use Windows when generating or validating Windows-style paths. Use Native only when the path should follow the machine running the code.

The next page explains PathOptions in detail.

Released under the MIT License.