Platform Utilities¶
The platform module provides cross-platform utilities for interacting with the operating system.
environment¶
-
class environment¶
Utility class for safe environment variable access.
The environment class provides safe, cross-platform access to environment variables using std::optional to handle cases where variables may not exist. This approach avoids the security risks and undefined behavior associated with direct getenv() usage.
Key features:
Safe environment variable access with std::optional
Cross-platform compatibility (Unix, Windows)
No undefined behavior for missing variables
Thread-safe read operations
Security considerations:
Always check if a variable exists before using it
Be aware that environment variables are visible to all processes
Avoid storing sensitive information in environment variables
Performance characteristics:
O(1) typical case for environment variable lookup
Thread-safe for concurrent read access
No dynamic memory allocation for the API itself
// Basic usage if (auto home = environment::value("HOME")) { std::cout << "Home directory: " << *home << std::endl; } else { std::cout << "HOME environment variable not set" << std::endl; } // Providing defaults std::string shell = environment::value("SHELL").value_or("/bin/sh"); // Common environment variables auto path = environment::value("PATH"); auto user = environment::value("USER"); auto temp = environment::value("TMPDIR").value_or("/tmp");
Public Static Functions
-
static std::optional<std::string> value(const std::string &name)¶
Get the value of an environment variable.
Safely retrieves the value of the specified environment variable. Returns std::nullopt if the variable is not set or is empty.
Thread safety:
Safe for concurrent read access from multiple threads
Environment modifications during execution may not be visible
Platform notes:
On Unix-like systems, uses getenv() internally
On Windows, uses GetEnvironmentVariable() internally
Variable names are case-sensitive on Unix, case-insensitive on Windows
// Check if variable exists if (auto value = environment::value("MY_VAR")) { std::cout << "MY_VAR = " << *value << std::endl; } // Use with default value std::string config_dir = environment::value("CONFIG_DIR") .value_or("/etc/myapp");
- Parameters:
name – The name of the environment variable to retrieve
- Returns:
std::optional containing the value if the variable exists, std::nullopt otherwise
The environment
class provides access to environment variables:
#include <dross/environment.h>
// Get environment variable
if (auto home = dross::environment::get("HOME")) {
std::cout << "Home directory: " << *home << std::endl;
}
// Set environment variable
dross::environment::set("MY_VAR", "my_value");
// Remove environment variable
dross::environment::unset("MY_VAR");
// Get all environment variables
auto all_vars = dross::environment::get_all();
for (const auto& [key, value] : all_vars) {
std::cout << key << "=" << value << std::endl;
}
path¶
-
class path¶
Cross-platform filesystem path operations with error handling.
The path class provides a safe, cross-platform wrapper around filesystem operations using std::filesystem as the underlying implementation. It uses std::expected for operations that may fail and std::optional for operations that may not return a value.
Key features:
Cross-platform path handling (Windows, Unix-like systems)
Safe error handling with std::expected and std::optional
Path expansion and resolution
Directory creation with proper error reporting
Integration with std::filesystem
Error handling:
Uses std::expected<path, std::filesystem::filesystem_error> for fallible operations
Uses std::optional<path> for operations that may not return a value
No exceptions thrown directly (may propagate from std::filesystem)
Performance characteristics:
Thin wrapper over std::filesystem with minimal overhead
Path operations are typically O(1) or O(path_length)
Filesystem I/O operations depend on underlying system performance
Thread safety:
Path objects are not thread-safe for modification
Const operations are thread-safe
Filesystem operations may have race conditions inherent to the filesystem
// Basic path operations path config_path{"~/.config/myapp"}; if (auto expanded = config_path.expand()) { std::cout << "Expanded path: " << expanded->string() << std::endl; } // Directory creation path new_dir{"/tmp/myapp/data"}; if (auto result = path::mkdir(new_dir.string())) { std::cout << "Directory created: " << result->string() << std::endl; } else { std::cerr << "Failed to create directory: " << result.error().what() << std::endl; } // Path building if (auto home = path::home()) { path config_file = home->append("myapp").append("config.toml"); if (config_file.exists()) { // Process config file } }
Public Functions
-
path()¶
Default constructor creating an empty path.
-
path(const std::string &path_str)¶
Construct a path from a string.
Creates a path object from the given string. The string is interpreted using the native path format for the current platform.
- Parameters:
path_str – The path string to construct from
-
path(const std::filesystem::path &fs_path)¶
Construct a path from a std::filesystem::path.
Creates a path object wrapping the given std::filesystem::path.
- Parameters:
fs_path – The filesystem path to construct from
-
bool exists() const¶
Check if the path exists in the filesystem.
Checks whether the path refers to an existing filesystem entity. This includes files, directories, symbolic links, and other filesystem objects.
- Returns:
true if the path exists (file or directory), false otherwise
-
path append(const std::string &component) const¶
Append a path component to this path.
Creates a new path by appending the given component using the platform-appropriate path separator. Does not modify this path object.
path base{"/usr/local"}; path full = base.append("bin").append("myapp"); // Result: "/usr/local/bin/myapp"
- Parameters:
component – The path component to append
- Returns:
New path object with the component appended
-
std::string string() const¶
Get the string representation of the path.
Returns the path as a string using the native format for the current platform. On Unix-like systems, uses forward slashes. On Windows, uses backslashes.
- Returns:
String representation using native path format
-
std::expected<path, std::filesystem::filesystem_error> expand() const¶
Expand user home directory (~) in the path.
Expands tilde (~) notation to the actual home directory path. Only processes paths that start with “~” or “~/”.
path user_config{"~/.config/myapp"}; if (auto expanded = user_config.expand()) { // expanded contains something like "/home/user/.config/myapp" }
- Returns:
Expected containing the expanded path on success, or filesystem_error on failure
-
std::expected<path, std::filesystem::filesystem_error> resolve() const¶
Resolve the path to an absolute, canonical form.
Converts the path to an absolute path and resolves any symbolic links, “.” and “..” components. The resulting path is in canonical form.
path relative{"../config/../data/file.txt"}; if (auto resolved = relative.resolve()) { // resolved contains the canonical absolute path }
- Returns:
Expected containing the resolved path on success, or filesystem_error on failure
-
operator std::string() const¶
Convert to string representation.
Implicit conversion to string for convenient usage with APIs that expect string paths.
- Returns:
String representation of the path
-
operator std::filesystem::path() const¶
Convert to std::filesystem::path.
Provides access to the underlying std::filesystem::path for interoperability with standard library filesystem operations.
- Returns:
The underlying std::filesystem::path object
Public Static Functions
-
static std::expected<path, std::filesystem::filesystem_error> mkdir(const std::string &dir_path)¶
Create a directory from a string path.
Creates the specified directory and any necessary parent directories. If the directory already exists, the operation succeeds.
if (auto result = path::mkdir("/tmp/myapp/data")) { std::cout << "Created: " << result->string() << std::endl; } else { std::cerr << "Error: " << result.error().what() << std::endl; }
- Parameters:
dir_path – The directory path to create as a string
- Returns:
Expected containing the created path on success, or filesystem_error on failure
-
static std::expected<path, std::filesystem::filesystem_error> mkdir(const std::filesystem::path &dir_path)¶
Create a directory from a filesystem::path.
Creates the specified directory and any necessary parent directories. If the directory already exists, the operation succeeds.
- Parameters:
dir_path – The directory path to create as a filesystem::path
- Returns:
Expected containing the created path on success, or filesystem_error on failure
-
static std::optional<path> home()¶
Get the user’s home directory.
Attempts to determine the user’s home directory using platform-appropriate methods:
Unix-like systems: $HOME environment variable
Windows: USERPROFILE% or HOMEDRIVE%HOMEPATH%
if (auto home = path::home()) { path config = home->append(".config"); } else { // Handle case where home directory cannot be determined }
- Returns:
Optional containing the home directory path, or std::nullopt if not determinable
-
static std::string separator()¶
Get the platform-appropriate path separator.
Returns the native path separator for the current platform. Useful for building paths manually or for display purposes.
- Returns:
String containing the path separator (“/” on Unix, “\” on Windows)
The path
class provides filesystem path operations:
#include <dross/path.h>
// Join path components
auto config_path = dross::path::join("/home/user", ".config", "app");
// Get absolute path
auto abs_path = dross::path::absolute("../file.txt");
// Check if path exists
if (dross::path::exists("/etc/passwd")) {
std::cout << "System has passwd file" << std::endl;
}
// Read file contents
auto result = dross::path::read_file("/path/to/file.txt");
if (result) {
std::cout << "Content: " << *result << std::endl;
} else {
std::cerr << "Error: " << result.error().message() << std::endl;
}
// Write file contents
auto write_result = dross::path::write_file("/path/to/output.txt",
"Hello, World!");
if (!write_result) {
std::cerr << "Write failed: " << write_result.error().message() << std::endl;
}
Path Operations¶
Common path operations include:
join() - Join multiple path components
dirname() - Get directory part of path
basename() - Get filename part of path
extension() - Get file extension
stem() - Get filename without extension
absolute() - Convert to absolute path
normalize() - Normalize path (remove . and ..)
relative() - Get relative path between two paths
File Operations¶
File and directory operations:
exists() - Check if path exists
is_file() - Check if path is a regular file
is_directory() - Check if path is a directory
file_size() - Get file size in bytes
read_file() - Read entire file contents
write_file() - Write data to file
create_directory() - Create directory (with parents)
remove() - Remove file or empty directory
remove_all() - Remove recursively
xdg¶
-
class xdg¶
XDG Base Directory Specification implementation for proper app data storage.
The xdg class implements the XDG Base Directory Specification, which defines standard locations for application data, configuration, cache, and state files on Unix-like systems. This ensures applications store their data in appropriate locations that integrate well with the desktop environment and user expectations.
Key features:
XDG Base Directory Specification compliance
Automatic fallback to standard directories when XDG variables are unset
Application-specific subdirectory creation
Cross-platform compatibility (Unix-like systems primarily)
XDG directories:
Config: User-specific configuration files
Data: User-specific data files
Cache: User-specific non-essential cached data
State: User-specific state data (logs, history, etc.)
Environment variables used:
XDG_CONFIG_HOME (default: ~/.config)
XDG_DATA_HOME (default: ~/.local/share)
XDG_CACHE_HOME (default: ~/.cache)
XDG_STATE_HOME (default: ~/.local/state)
// Application-specific XDG directories xdg app_dirs{"myapp"}; // Get configuration directory if (auto config_dir = app_dirs.config_home()) { path config_file = path{*config_dir} / "config.toml"; // Store configuration in ~/.config/myapp/config.toml } // Get data directory if (auto data_dir = app_dirs.data_home()) { path db_file = path{*data_dir} / "database.sqlite"; // Store data in ~/.local/share/myapp/database.sqlite } // Get cache directory if (auto cache_dir = app_dirs.cache_home()) { path cache_file = path{*cache_dir} / "thumbnails"; // Store cache in ~/.cache/myapp/thumbnails }
Platform compatibility:
Full support on Linux and other Unix-like systems
Limited support on macOS (uses similar directory structure)
Not applicable on Windows (consider using appropriate Windows APIs)
Public Functions
-
xdg(const std::string &app_name)¶
Construct XDG directory helper for a specific application.
Creates an XDG directory helper that will create application-specific subdirectories within the standard XDG base directories. The application name is used as the subdirectory name.
The application name should be:
A valid directory name (no path separators)
Unique to your application
Following naming conventions (lowercase, hyphens for separation)
xdg app_dirs{"my-awesome-app"}; // Will create directories like ~/.config/my-awesome-app/
- Parameters:
app_name – The name of the application for directory creation
-
std::optional<std::string> config_home() const¶
Get the application’s configuration directory.
Returns the application-specific configuration directory according to XDG Base Directory Specification:
Uses $XDG_CONFIG_HOME/app_name if XDG_CONFIG_HOME is set
Falls back to $HOME/.config/app_name
Returns std::nullopt if HOME cannot be determined
The returned directory may not exist yet. Use path::mkdir() to create it.
xdg app{"myapp"}; if (auto config_dir = app.config_home()) { // Typically returns something like "/home/user/.config/myapp" path config_path{*config_dir}; if (auto result = path::mkdir(config_path.string())) { // Directory created and ready for config files } }
- Returns:
Optional containing the config directory path, or std::nullopt on error
-
std::optional<std::string> data_home() const¶
Get the application’s data directory.
Returns the application-specific data directory according to XDG Base Directory Specification:
Uses $XDG_DATA_HOME/app_name if XDG_DATA_HOME is set
Falls back to $HOME/.local/share/app_name
Returns std::nullopt if HOME cannot be determined
Use this directory for application data files, databases, etc.
- Returns:
Optional containing the data directory path, or std::nullopt on error
-
std::optional<std::string> cache_home() const¶
Get the application’s cache directory.
Returns the application-specific cache directory according to XDG Base Directory Specification:
Uses $XDG_CACHE_HOME/app_name if XDG_CACHE_HOME is set
Falls back to $HOME/.cache/app_name
Returns std::nullopt if HOME cannot be determined
Use this directory for non-essential cached data that can be regenerated. Cache files may be deleted by system cleanup tools.
- Returns:
Optional containing the cache directory path, or std::nullopt on error
-
std::optional<std::string> state_home() const¶
Get the application’s state directory.
Returns the application-specific state directory according to XDG Base Directory Specification:
Uses $XDG_STATE_HOME/app_name if XDG_STATE_HOME is set
Falls back to $HOME/.local/state/app_name
Returns std::nullopt if HOME cannot be determined
Use this directory for state data like logs, history, recently used files, etc. This data should persist between application runs but is not user configuration.
- Returns:
Optional containing the state directory path, or std::nullopt on error
The xdg
class implements the XDG Base Directory Specification:
#include <dross/xdg.h>
// Get user-specific data directory
auto data_home = dross::xdg::data_home();
// Default: $HOME/.local/share
// Get user-specific configuration directory
auto config_home = dross::xdg::config_home();
// Default: $HOME/.config
// Get user-specific cache directory
auto cache_home = dross::xdg::cache_home();
// Default: $HOME/.cache
// Get user-specific state directory
auto state_home = dross::xdg::state_home();
// Default: $HOME/.local/state
// Get runtime directory
if (auto runtime_dir = dross::xdg::runtime_dir()) {
std::cout << "Runtime dir: " << *runtime_dir << std::endl;
}
// Get system data directories
auto data_dirs = dross::xdg::data_dirs();
// Default: /usr/local/share:/usr/share
// Get system config directories
auto config_dirs = dross::xdg::config_dirs();
// Default: /etc/xdg
XDG Directories¶
The XDG Base Directory Specification defines standard locations for:
Data files - Application data that should persist
Configuration - User-specific configuration files
Cache - Non-essential cached data
State - Application state data (logs, history, etc.)
Runtime - Runtime files (sockets, PIDs, etc.)
Example Usage¶
Creating application directories:
#include <dross/xdg.h>
#include <dross/path.h>
// Create app-specific directories
auto app_config = dross::path::join(dross::xdg::config_home(), "myapp");
auto app_data = dross::path::join(dross::xdg::data_home(), "myapp");
auto app_cache = dross::path::join(dross::xdg::cache_home(), "myapp");
// Create directories if they don't exist
dross::path::create_directory(app_config);
dross::path::create_directory(app_data);
dross::path::create_directory(app_cache);
// Store configuration
auto config_file = dross::path::join(app_config, "settings.json");
dross::path::write_file(config_file, config_json);
// Store application data
auto data_file = dross::path::join(app_data, "database.db");
// Store cached data
auto cache_file = dross::path::join(app_cache, "thumbnails.cache");
Platform Considerations¶
Windows Support¶
On Windows systems:
XDG directories map to appropriate Windows locations
Path separators are handled automatically
Environment variables use Windows conventions
macOS Support¶
On macOS:
XDG directories follow macOS conventions where appropriate
~/Library
paths are used for some directoriesFull POSIX compatibility is maintained
Error Handling¶
All filesystem operations return std::expected
for error handling:
auto result = dross::path::read_file("/nonexistent/file");
if (!result) {
switch (result.error().code()) {
case dross::error_code::file_not_found:
std::cerr << "File not found" << std::endl;
break;
case dross::error_code::permission_denied:
std::cerr << "Permission denied" << std::endl;
break;
default:
std::cerr << "Error: " << result.error().message() << std::endl;
}
}