Skip to content

Commit aca6f34

Browse files
Matt Hayniemhaynie
authored andcommitted
Force enable MH_BROKEN_UNICODE and suppress codecvt deprecation warnings
1 parent 17bf9b9 commit aca6f34

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

60 files changed

+4609
-497
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,5 @@ out_test_linux/
1414
CMakeSettings.json
1515
out_test/
1616
build/
17+
.claude/
18+
.cache/

.gitmodules

Whitespace-only changes.

CLAUDE.md

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Project Overview
6+
7+
This is **mh_stuff**, a C++20 utility library containing reusable components for system programming. The library operates in either header-only mode or as a compiled static/shared library, with minimal interdependencies between modules to support selective usage.
8+
9+
**Important**: This library experiences significant API churn with no stability guarantees. It's designed for the author's personal projects and regular structural changes should be expected.
10+
11+
## Build and Test Commands
12+
13+
This project uses CMake with CPM (CMake Package Manager) for dependency management and Just for build task automation. The main dependencies are `mh-cmake-common` for build utilities and `Catch2` for testing.
14+
15+
### Building the Project
16+
17+
```bash
18+
# Using justfile (recommended)
19+
just build
20+
21+
# Or using CMake presets directly
22+
cmake --preset default
23+
cmake --build --preset default
24+
25+
# Or standard CMake build
26+
mkdir build && cd build
27+
cmake .. -G Ninja
28+
cmake --build .
29+
```
30+
31+
### Running Tests
32+
33+
The project uses Catch2 for testing:
34+
35+
```bash
36+
# Using justfile (recommended)
37+
just test
38+
39+
# Or using CMake presets
40+
ctest --preset default
41+
42+
# Or from build directory
43+
cd build && ctest --output-on-failure
44+
45+
# Or run the test executable directly
46+
./build/mh_stuff_tests
47+
```
48+
49+
### Build Configuration Options
50+
51+
- `MH_STUFF_BUILD_SHARED_LIBS=ON/OFF` - Build as shared library instead of static
52+
- `MH_STUFF_COMPILE_LIBRARY=ON/OFF` - Compile implementations vs header-only mode
53+
- `BUILD_TESTING=ON/OFF` - Enable/disable test compilation
54+
55+
## Architecture Overview
56+
57+
### Module Organization
58+
59+
The library is organized into logical modules under `cpp/include/mh/`:
60+
61+
- **`coroutine/`** - C++20 coroutine infrastructure including `task<T>` for async operations
62+
- **`error/`** - Error handling with `mh_ensure()` assertions and `expected<T,E>` monadic error types
63+
- **`concurrency/`** - Thread pools, async operations, and synchronization primitives
64+
- **`io/`** - Platform-agnostic async I/O with `source`/`sink` abstractions
65+
- **`memory/`** - Smart pointers, buffers, and memory management utilities
66+
- **`text/`** - String processing, formatting (fmtlib/std::format), and text utilities
67+
- **`data/`** - Data structures like `lazy<T>`, optional references, and bit manipulation
68+
- **`process/`** - Process management and async process I/O
69+
- **`math/`** - Mathematical utilities including interpolation and random number generation
70+
- **`reflection/`** - Compile-time reflection for enums and structs
71+
72+
### Key Design Patterns
73+
74+
1. **Header-Only with Selective Compilation**: `.hpp` files contain interfaces, `.inl` files contain implementations that can be compiled into a library or included directly
75+
2. **C++20 Coroutine-Based Async**: The `task<T>` type provides future-like semantics with coroutine support for async operations
76+
3. **Platform Abstraction**: Cross-platform code with platform-specific implementations hidden behind clean interfaces
77+
4. **Compile-Time Feature Detection**: Extensive use of `__has_include` and feature test macros for conditional compilation
78+
5. **RAII and Exception Safety**: Strong exception safety guarantees throughout with automatic resource management
79+
80+
### Build System Notes
81+
82+
- Uses CPM (CMake Package Manager) for automatic dependency fetching (Catch2 testing framework)
83+
- Automatically generates a library.cpp file from all .inl files when in compiled library mode
84+
- Tests include compile-time verification that all headers can be included independently
85+
- Supports coverage reporting with gcov/gcovr on GCC/Clang
86+
87+
### Important Compile-Time Configurations
88+
89+
- `MH_COMPILE_LIBRARY` - Controls whether implementations are compiled into a library or included header-only
90+
- `MH_COROUTINES_SUPPORTED` - Enables C++20 coroutine functionality
91+
- `MH_FORMATTER` - Selects formatting backend (fmtlib, std::format, or none)
92+
- `MH_BROKEN_UNICODE=1` - Forced workaround for compiler Unicode issues
93+
94+
## Development Guidelines
95+
96+
- This library requires C++20 and uses modern C++ features extensively
97+
- Platform support includes Windows (MSVC), Linux (GCC), and macOS (Clang)
98+
- Code follows RAII principles with move semantics preferred over copying
99+
- Exception safety is critical - all code should provide strong exception safety guarantees
100+
- The library is designed for zero-overhead abstractions with extensive use of `constexpr`
101+
102+
## Testing
103+
104+
Tests are located in the `test/` directory and use Catch2. The build system:
105+
- Automatically discovers all `*_test.cpp` files
106+
- Creates a unified test executable `mh_stuff_tests`
107+
- Includes compile-time tests to verify all headers can be included independently
108+
- Conditionally excludes platform-specific tests (e.g., `io_getopt_test.cpp` on systems without getopt)

CMakeLists.txt

Lines changed: 2 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,6 @@ add_library(${PROJECT_NAME} ${MH_INTERFACE_OR_EMPTY}
3535
)
3636
add_library(mh::stuff ALIAS ${PROJECT_NAME})
3737

38-
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/mh-cmake-common")
39-
include(mh-BasicInstall)
40-
include(mh-CheckCoroutineSupport)
41-
4238
if (MH_STUFF_COMPILE_LIBRARY)
4339
find_package(CURL REQUIRED)
4440

@@ -85,9 +81,6 @@ else()
8581
)
8682
endif()
8783

88-
mh_check_cxx_coroutine_support(SUPPORTS_COROUTINES COROUTINES_FLAGS)
89-
target_compile_options(${PROJECT_NAME} ${MH_PUBLIC_OR_INTERFACE} ${COROUTINES_FLAGS})
90-
9184
target_include_directories(${PROJECT_NAME} ${MH_PUBLIC_OR_INTERFACE}
9285
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/cpp/include>"
9386
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
@@ -135,6 +128,8 @@ message(STATUS "Forcing MH_BROKEN_UNICODE=1 to work around compiler Unicode issu
135128
if (NOT DEFINED BUILD_TESTING)
136129
if (CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR)
137130
set(BUILD_TESTING ON)
131+
# Define MH_STUFF_STANDALONE_BUILD when building as main project
132+
target_compile_definitions(${PROJECT_NAME} ${MH_PUBLIC_OR_INTERFACE} "MH_STUFF_STANDALONE_BUILD")
138133
else()
139134
set(BUILD_TESTING OFF)
140135
endif()
@@ -146,40 +141,4 @@ if (BUILD_TESTING)
146141
add_subdirectory("test")
147142
endif()
148143

149-
mh_basic_install(
150-
PROJ_INCLUDE_DIRS "cpp/include/"
151-
)
152144

153-
###########################################
154-
# "install" is intended for vcpkg support #
155-
###########################################
156-
# include(CMakePackageConfigHelpers)
157-
# configure_package_config_file(
158-
# cmake/config.cmake.in
159-
# "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config.cmake"
160-
# INSTALL_DESTINATION "${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}"
161-
# )
162-
163-
# include(GNUInstallDirs)
164-
165-
# write_basic_package_version_file(
166-
# "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config-version.cmake"
167-
# VERSION ${PROJECT_VERSION}
168-
# COMPATIBILITY SameMajorVersion
169-
# )
170-
171-
# install(
172-
# FILES
173-
# "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config.cmake"
174-
# "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config-version.cmake"
175-
# DESTINATION
176-
# "${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}"
177-
# )
178-
179-
# install(TARGETS stuff EXPORT ${PROJECT_NAME}_targets)
180-
# install(DIRECTORY cpp/include/ DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}")
181-
# install(
182-
# EXPORT ${PROJECT_NAME}_targets
183-
# NAMESPACE mh::
184-
# DESTINATION "${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}"
185-
# )

CMakePresets.json

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
{
2+
"version": 3,
3+
"cmakeMinimumRequired": {
4+
"major": 3,
5+
"minor": 16,
6+
"patch": 3
7+
},
8+
"configurePresets": [
9+
{
10+
"name": "default",
11+
"displayName": "Default Configuration",
12+
"description": "Default build configuration with Ninja generator",
13+
"generator": "Ninja",
14+
"binaryDir": "${sourceDir}/build/default",
15+
"cacheVariables": {
16+
"CMAKE_BUILD_TYPE": "Debug",
17+
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON"
18+
},
19+
"condition": {
20+
"type": "notEquals",
21+
"lhs": "${hostSystemName}",
22+
"rhs": "Windows"
23+
}
24+
},
25+
{
26+
"name": "debug",
27+
"displayName": "Debug Configuration",
28+
"description": "Debug build with coverage support",
29+
"generator": "Ninja",
30+
"binaryDir": "${sourceDir}/build/debug",
31+
"cacheVariables": {
32+
"CMAKE_BUILD_TYPE": "Debug",
33+
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON"
34+
}
35+
},
36+
{
37+
"name": "shared",
38+
"displayName": "Shared Library",
39+
"description": "Build as shared library",
40+
"generator": "Ninja",
41+
"binaryDir": "${sourceDir}/build/shared",
42+
"cacheVariables": {
43+
"CMAKE_BUILD_TYPE": "Release",
44+
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON",
45+
"MH_STUFF_BUILD_SHARED_LIBS": "ON"
46+
}
47+
},
48+
{
49+
"name": "header-only",
50+
"displayName": "Header-Only Mode",
51+
"description": "Build in header-only mode",
52+
"generator": "Ninja",
53+
"binaryDir": "${sourceDir}/build/header-only",
54+
"cacheVariables": {
55+
"CMAKE_BUILD_TYPE": "Release",
56+
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON",
57+
"MH_STUFF_COMPILE_LIBRARY": "OFF"
58+
}
59+
},
60+
{
61+
"name": "coverage",
62+
"displayName": "Coverage Build",
63+
"description": "Debug build optimized for coverage reporting",
64+
"generator": "Ninja",
65+
"binaryDir": "${sourceDir}/build/coverage",
66+
"cacheVariables": {
67+
"CMAKE_BUILD_TYPE": "Debug",
68+
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON",
69+
"ENABLE_COVERAGE": "ON",
70+
"CMAKE_CXX_FLAGS": "-g -O0 --coverage -fprofile-arcs -ftest-coverage",
71+
"CMAKE_C_FLAGS": "-g -O0 --coverage -fprofile-arcs -ftest-coverage",
72+
"CMAKE_EXE_LINKER_FLAGS": "--coverage"
73+
},
74+
"condition": {
75+
"type": "notEquals",
76+
"lhs": "${hostSystemName}",
77+
"rhs": "Windows"
78+
}
79+
}
80+
],
81+
"buildPresets": [
82+
{
83+
"name": "default",
84+
"configurePreset": "default"
85+
},
86+
{
87+
"name": "debug",
88+
"configurePreset": "debug"
89+
},
90+
{
91+
"name": "shared",
92+
"configurePreset": "shared"
93+
},
94+
{
95+
"name": "header-only",
96+
"configurePreset": "header-only"
97+
},
98+
{
99+
"name": "coverage",
100+
"configurePreset": "coverage"
101+
}
102+
],
103+
"testPresets": [
104+
{
105+
"name": "default",
106+
"configurePreset": "default",
107+
"output": {
108+
"outputOnFailure": true
109+
}
110+
},
111+
{
112+
"name": "debug",
113+
"configurePreset": "debug",
114+
"output": {
115+
"outputOnFailure": true
116+
}
117+
},
118+
{
119+
"name": "coverage",
120+
"configurePreset": "coverage",
121+
"output": {
122+
"outputOnFailure": true
123+
}
124+
}
125+
]
126+
}

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
MIT License
22

3-
Copyright (c) 2019 Matt Haynie
3+
Copyright (c) 2025 Matt Haynie
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

cpp/include/mh/containers/heap.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ namespace mh
3030
explicit heap(std::initializer_list<T> values, TComparator comparator = TComparator{}) :
3131
m_Container(values.begin(), values.end()), m_Comparator(std::move(comparator))
3232
{
33+
std::make_heap(m_Container.begin(), m_Container.end(), m_Comparator);
3334
}
3435

3536
void push(const T& value)

cpp/include/mh/io/fd_sink.inl

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,18 @@ namespace mh::io
1515
{
1616
#ifdef __unix__
1717
MH_COMPILE_LIBRARY_INLINE fd_sink::fd_sink(native_handle fd, bool take_ownership)
18-
: fd_(take_ownership ? unique_native_handle(fd) : unique_native_handle(dup(fd))),
18+
: fd_(take_ownership ? unique_native_handle(fd) : unique_native_handle(dup(fd))),
1919
is_open_(fd >= 0)
2020
{
21+
// Prevent multiple instantiations of standard streams
22+
static bool stdin_created = false;
23+
24+
if (fd == STDIN_FILENO) {
25+
if (stdin_created) {
26+
throw std::runtime_error("Attempt to create multiple fd_sink instances for STDIN_FILENO");
27+
}
28+
stdin_created = true;
29+
}
2130
}
2231

2332
MH_COMPILE_LIBRARY_INLINE fd_sink::~fd_sink() = default;
@@ -26,11 +35,11 @@ namespace mh::io
2635
{
2736
if (!is_open_)
2837
throw std::runtime_error("fd_sink is not open");
29-
38+
3039
ssize_t bytes_written = ::write(fd_.value(), buffer, size);
3140
if (bytes_written < 0)
3241
throw std::runtime_error("Failed to write to file descriptor");
33-
42+
3443
co_return static_cast<size_t>(bytes_written);
3544
}
3645

@@ -53,4 +62,20 @@ namespace mh::io
5362
return is_open_ && fd_;
5463
}
5564
#endif
56-
}
65+
66+
MH_COMPILE_LIBRARY_INLINE sink_ptr sink::create_file(const std::filesystem::path& filepath, bool append)
67+
{
68+
#ifdef __unix__
69+
int flags = O_WRONLY | O_CREAT | (append ? O_APPEND : O_TRUNC);
70+
int fd = open(filepath.c_str(), flags, 0644);
71+
if (fd == -1)
72+
{
73+
throw std::runtime_error("Failed to open file for writing: " + filepath.string());
74+
}
75+
76+
return std::make_shared<fd_sink>(fd, true);
77+
#else
78+
throw mh::not_implemented_error();
79+
#endif
80+
}
81+
}

0 commit comments

Comments
 (0)