"override members" idea as a gateway to UFCS (language evolution)
(UFCS backgrounder: https://isocpp.org/files/papers/N4174.pdf )
I have two functions that tell me if a string contains the
characters of a particular integer. They're called
(
They looks like this:
bool hasInt(const string s, int n)// does s have n?
{
return s.contains(tostring(n));
}
bool intIn(int n, const string s)// is n in s?
{
return s.contains(tostring(n));
}
It would be convenient if I could add
bool string::hasInt(int n)
{
return ::hasInt(this, n);
}
Then I could use "member syntax" to call the function, like `text.hasInt(123)`.
Of course, that's not possible, because then I'd be changing the header files
in the standard libraries.
Here's an idea for a new language feature:
let's use the `override` keyword to allow us to "inject" member functions
into an existing class, without modifying the class definition. So the code:
override bool string::hasInt(int n)
{
return ::hasInt(this, n);
}
will (in effect) add
Thus, this "override member function" feature has a syntax like:
ReturnType ClassName::function(args){...etc...}
HOWEVER..... what if ClassName doesn't necessarily need to be a class, and could be
other types? Then you open the door to override members like:
override bool int::intIn(const string s)
{
return ::intIn(this, s);
}
Which allows code like `(123).intIn(text)`.
This is halfway to UFCS!
Using some macro magic and helper templates, we could define a
MAKE_UFCS macro to convert a non-member function into a member function:
#define MAKE_UFCS(f) \
override \
retType(f) argType1(f)::f(argType2(f) x)\
{ \
return f(this, x); \
}
Thus the non-member functions
by the macro calls:
MAKEUFCS(hasInt);
MAKEUFCS(intIn);
Or maybe, if this override-to-UFCS is useful enough, the override feature can be
applied to a collection of functions at once, like:
override hasInt, intIn;
or
override {
#include <cstdlib>
}
To UFCS-ify an entire header file at the same time.
EDIT: this idea would be similar to Scala's "Extension Methods": https://docs.scala-lang.org/scala3/book/ca-extension-methods.html
or C#'s "Extension Members": https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/extension-methods
https://redd.it/1qyikcg
@r_cpp
(UFCS backgrounder: https://isocpp.org/files/papers/N4174.pdf )
I have two functions that tell me if a string contains the
characters of a particular integer. They're called
hasInt and intIn. (
intIn is inspired by the python keyword in.)They looks like this:
bool hasInt(const string s, int n)// does s have n?
{
return s.contains(tostring(n));
}
bool intIn(int n, const string s)// is n in s?
{
return s.contains(tostring(n));
}
It would be convenient if I could add
hasInt as a member function to std::string:bool string::hasInt(int n)
{
return ::hasInt(this, n);
}
Then I could use "member syntax" to call the function, like `text.hasInt(123)`.
Of course, that's not possible, because then I'd be changing the header files
in the standard libraries.
Here's an idea for a new language feature:
let's use the `override` keyword to allow us to "inject" member functions
into an existing class, without modifying the class definition. So the code:
override bool string::hasInt(int n)
{
return ::hasInt(this, n);
}
will (in effect) add
hasInt as a member function to string. Thus, this "override member function" feature has a syntax like:
ReturnType ClassName::function(args){...etc...}
HOWEVER..... what if ClassName doesn't necessarily need to be a class, and could be
other types? Then you open the door to override members like:
override bool int::intIn(const string s)
{
return ::intIn(this, s);
}
Which allows code like `(123).intIn(text)`.
This is halfway to UFCS!
Using some macro magic and helper templates, we could define a
MAKE_UFCS macro to convert a non-member function into a member function:
#define MAKE_UFCS(f) \
override \
retType(f) argType1(f)::f(argType2(f) x)\
{ \
return f(this, x); \
}
Thus the non-member functions
hasInt and intIn could be "opted in" to UFCSby the macro calls:
MAKEUFCS(hasInt);
MAKEUFCS(intIn);
Or maybe, if this override-to-UFCS is useful enough, the override feature can be
applied to a collection of functions at once, like:
override hasInt, intIn;
or
override {
#include <cstdlib>
}
To UFCS-ify an entire header file at the same time.
EDIT: this idea would be similar to Scala's "Extension Methods": https://docs.scala-lang.org/scala3/book/ca-extension-methods.html
or C#'s "Extension Members": https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/extension-methods
https://redd.it/1qyikcg
@r_cpp
MigrationManager::GetInstance();
manager.CreateMigrationHistory();
size_t applied = manager.ApplyPendingMigrations();
```
Supports rollbacks, dry-run preview, checksum verification, and distributed locking for safe concurrent deployments.
---
## Backup & Restore
Full database backup/restore with progress reporting:
```cpp
#include <Lightweight/SqlBackup.hpp>
// Backup to compressed archive (multi-threaded)
SqlBackup::Backup(
"backup.zip",
connectionString,
4, // concurrent workers
progressManager,
"", // schema
"*", // table filter (glob)
{}, // retry settings
{ .method = CompressionMethod::Zstd, .level = 6 }
);
// Restore
SqlBackup::Restore("backup.zip", connectionString, 4, progressManager);
```
Preserves indexes, foreign keys (including composite), and supports table filtering.
---
## Supported Databases
- Microsoft SQL Server
- PostgreSQL
- SQLite3
Works anywhere ODBC works (Windows, Linux, macOS).
---
## What's Next
We're actively developing and would love feedback. The library is production-ready for our use cases, but we're always looking to improve the API and add features.
We also consider abstracting away ODBC such that it could support non-ODBC databases like SQLite3 directly without the ODBC layer. That's a longer-term goal, but definitely a goal.
We currently focus on SQL tooling (migrations and backup/restore) as both are quite young additions that are still evolving.
Questions and PRs welcome!
https://redd.it/1r0co80
@r_cpp
manager.CreateMigrationHistory();
size_t applied = manager.ApplyPendingMigrations();
```
Supports rollbacks, dry-run preview, checksum verification, and distributed locking for safe concurrent deployments.
---
## Backup & Restore
Full database backup/restore with progress reporting:
```cpp
#include <Lightweight/SqlBackup.hpp>
// Backup to compressed archive (multi-threaded)
SqlBackup::Backup(
"backup.zip",
connectionString,
4, // concurrent workers
progressManager,
"", // schema
"*", // table filter (glob)
{}, // retry settings
{ .method = CompressionMethod::Zstd, .level = 6 }
);
// Restore
SqlBackup::Restore("backup.zip", connectionString, 4, progressManager);
```
Preserves indexes, foreign keys (including composite), and supports table filtering.
---
## Supported Databases
- Microsoft SQL Server
- PostgreSQL
- SQLite3
Works anywhere ODBC works (Windows, Linux, macOS).
---
## What's Next
We're actively developing and would love feedback. The library is production-ready for our use cases, but we're always looking to improve the API and add features.
We also consider abstracting away ODBC such that it could support non-ODBC databases like SQLite3 directly without the ODBC layer. That's a longer-term goal, but definitely a goal.
We currently focus on SQL tooling (migrations and backup/restore) as both are quite young additions that are still evolving.
Questions and PRs welcome!
https://redd.it/1r0co80
@r_cpp
Reddit
From the cpp community on Reddit: Lightweight: Almost-zero-overhead C++23 SQL library with DataMapper ORM, migrations, and backup/restore
Explore this post and more from the cpp community
Corosio Beta - coroutine-native networking for C++20
We are releasing the Corosio beta - a coroutine-native networking library for C++20 built by the C++ Alliance. It is the successor to Boost.Asio, designed from the ground up for coroutines.
**What is it?**
Corosio provides TCP sockets, acceptors, TLS streams, timers, and DNS resolution. Every operation is an awaitable. You write co\_await and the library handles executor affinity, cancellation, and frame allocation. No callbacks. No futures. No sender/receiver.
It is built on Capy, a coroutine I/O foundation that ships with Corosio. Capy provides the task types, buffer sequences, stream concepts, and execution model. The two libraries have no dependencies outside the standard library.
**An echo server in 45 lines:**
#include <boost/capy.hpp>
#include <boost/corosio.hpp>
namespace corosio = boost::corosio;
namespace capy = boost::capy;
capy::task<> echo_session(corosio::tcp_socket sock)
{
char buf[1024];
for (;;)
{
auto [ec, n] = co_await sock.read_some(
capy::mutable_buffer(buf, sizeof(buf)));
auto [wec, wn] = co_await capy::write(
sock, capy::const_buffer(buf, n));
if (ec)
break;
if (wec)
break;
}
sock.close();
}
capy::task<> accept_loop(
corosio::tcp_acceptor& acc,
corosio::io_context& ioc)
{
for (;;)
{
corosio::tcp_socket peer(ioc);
auto [ec] = co_await acc.accept(peer);
if (ec)
continue;
capy::run_async(ioc.get_executor())(echo_session(std::move(peer)));
}
}
int main()
{
corosio::io_context ioc;
corosio::tcp_acceptor acc(ioc, corosio::endpoint(8080));
capy::run_async(ioc.get_executor())(accept_loop(acc, ioc));
ioc.run();
}
**Features:**
* Coroutine-only - every I/O operation is an awaitable, no callbacks
* TCP sockets, acceptors, TLS streams, timers, DNS resolution
* Cross-platform: Windows (IOCP), Linux (epoll), macOS/FreeBSD (kqueue)
* Type-erased streams - write any\_stream& and accept any stream type. Compile once, link anywhere. No template explosion.
* Zero steady-state heap allocations after warmup
* Automatic executor affinity - your coroutine always resumes on the right thread
* Automatic stop token propagation - cancel at the top, everything below stops Buffer sequences with byte-level manipulation (slice, front, consuming\_buffers, circular buffers)
* Concurrency primitives: strand, thread\_pool, async\_mutex, async\_event, when\_all, when\_any Forward-flow allocator control for coroutine frames
* C++20: GCC 12+, Clang 17+, MSVC 14.34+
**Get it:**
git clone https://github.com/cppalliance/corosio.git
cd corosio
cmake -S . -B build -G Ninja
cmake --build build
No dependencies. Capy is fetched automatically.
Or use CMake FetchContent in your project:
include(FetchContent)
FetchContent_Declare(corosio
GIT_REPOSITORY https://github.com/cppalliance/corosio.git
GIT_TAG develop
GIT_SHALLOW TRUE)
FetchContent_MakeAvailable(corosio)
target_link_libraries(my_app Boost::corosio)
**Links:**
* Corosio: [https://github.com/cppalliance/corosio](https://github.com/cppalliance/corosio)
* Corosio docs: [https://develop.corosio.cpp.al/](https://develop.corosio.cpp.al/)
* Capy: [https://github.com/cppalliance/capy](https://github.com/cppalliance/capy)
* Capy docs: [https://develop.capy.cpp.al/](https://develop.capy.cpp.al/)
**What’s next:**
HTTP, WebSocket, and high-level server libraries are in development on the same foundation. Corosio is heading for Boost formal review. We want your feedback.
https://redd.it/1rqykml
@r_cpp
We are releasing the Corosio beta - a coroutine-native networking library for C++20 built by the C++ Alliance. It is the successor to Boost.Asio, designed from the ground up for coroutines.
**What is it?**
Corosio provides TCP sockets, acceptors, TLS streams, timers, and DNS resolution. Every operation is an awaitable. You write co\_await and the library handles executor affinity, cancellation, and frame allocation. No callbacks. No futures. No sender/receiver.
It is built on Capy, a coroutine I/O foundation that ships with Corosio. Capy provides the task types, buffer sequences, stream concepts, and execution model. The two libraries have no dependencies outside the standard library.
**An echo server in 45 lines:**
#include <boost/capy.hpp>
#include <boost/corosio.hpp>
namespace corosio = boost::corosio;
namespace capy = boost::capy;
capy::task<> echo_session(corosio::tcp_socket sock)
{
char buf[1024];
for (;;)
{
auto [ec, n] = co_await sock.read_some(
capy::mutable_buffer(buf, sizeof(buf)));
auto [wec, wn] = co_await capy::write(
sock, capy::const_buffer(buf, n));
if (ec)
break;
if (wec)
break;
}
sock.close();
}
capy::task<> accept_loop(
corosio::tcp_acceptor& acc,
corosio::io_context& ioc)
{
for (;;)
{
corosio::tcp_socket peer(ioc);
auto [ec] = co_await acc.accept(peer);
if (ec)
continue;
capy::run_async(ioc.get_executor())(echo_session(std::move(peer)));
}
}
int main()
{
corosio::io_context ioc;
corosio::tcp_acceptor acc(ioc, corosio::endpoint(8080));
capy::run_async(ioc.get_executor())(accept_loop(acc, ioc));
ioc.run();
}
**Features:**
* Coroutine-only - every I/O operation is an awaitable, no callbacks
* TCP sockets, acceptors, TLS streams, timers, DNS resolution
* Cross-platform: Windows (IOCP), Linux (epoll), macOS/FreeBSD (kqueue)
* Type-erased streams - write any\_stream& and accept any stream type. Compile once, link anywhere. No template explosion.
* Zero steady-state heap allocations after warmup
* Automatic executor affinity - your coroutine always resumes on the right thread
* Automatic stop token propagation - cancel at the top, everything below stops Buffer sequences with byte-level manipulation (slice, front, consuming\_buffers, circular buffers)
* Concurrency primitives: strand, thread\_pool, async\_mutex, async\_event, when\_all, when\_any Forward-flow allocator control for coroutine frames
* C++20: GCC 12+, Clang 17+, MSVC 14.34+
**Get it:**
git clone https://github.com/cppalliance/corosio.git
cd corosio
cmake -S . -B build -G Ninja
cmake --build build
No dependencies. Capy is fetched automatically.
Or use CMake FetchContent in your project:
include(FetchContent)
FetchContent_Declare(corosio
GIT_REPOSITORY https://github.com/cppalliance/corosio.git
GIT_TAG develop
GIT_SHALLOW TRUE)
FetchContent_MakeAvailable(corosio)
target_link_libraries(my_app Boost::corosio)
**Links:**
* Corosio: [https://github.com/cppalliance/corosio](https://github.com/cppalliance/corosio)
* Corosio docs: [https://develop.corosio.cpp.al/](https://develop.corosio.cpp.al/)
* Capy: [https://github.com/cppalliance/capy](https://github.com/cppalliance/capy)
* Capy docs: [https://develop.capy.cpp.al/](https://develop.capy.cpp.al/)
**What’s next:**
HTTP, WebSocket, and high-level server libraries are in development on the same foundation. Corosio is heading for Boost formal review. We want your feedback.
https://redd.it/1rqykml
@r_cpp
GitHub
GitHub - cppalliance/corosio
Contribute to cppalliance/corosio development by creating an account on GitHub.
Modern declarative EDSL for graphic user interface in C++? Not QML or XAML.
Hello everyone,
I have been investigating lately (after using NiceGUI for a real project) and learning Jetpack Compose (not writing any real code with it, would do it with Jetpack Multiplatform if ever).
I am really impressed by Jetpack Compose approach, especially UDF.
I thought there could possibly not be a better way than MVVM tbh, after using it for years.
But after seeing how declarative composition inside the language, not with XAML or QML can be done, I am sold in the amount of boilerplate that can be saved, plus the better integration when the dsl is in the host language.
So I wanted to mention I found this, which I think could be a good start for some experiments, on top of wxWidgets: https://github.com/rmpowell77/wxUI
I think it has a talk in CppCon2025 here: https://www.youtube.com/watch?v=xu4pI72zlO4
Kudos to the author for bringing something like this to C++! Definitely useful.
I would like to hear your opinions on this style of EDSL GUI embedding and pros/cons you find for those of you who do GUI programming all the time.
Also, wild idea: with the power of compile-time C++ programming and C++26 reflection, it would be possible to get existing xaml interfaces and convert it into regular C++ at compile-time via #embed or #include and not even changing the EDSL itself and making it directly embedded and reusable in C++? That would be plenty useful.
https://redd.it/1ry51fg
@r_cpp
Hello everyone,
I have been investigating lately (after using NiceGUI for a real project) and learning Jetpack Compose (not writing any real code with it, would do it with Jetpack Multiplatform if ever).
I am really impressed by Jetpack Compose approach, especially UDF.
I thought there could possibly not be a better way than MVVM tbh, after using it for years.
But after seeing how declarative composition inside the language, not with XAML or QML can be done, I am sold in the amount of boilerplate that can be saved, plus the better integration when the dsl is in the host language.
So I wanted to mention I found this, which I think could be a good start for some experiments, on top of wxWidgets: https://github.com/rmpowell77/wxUI
I think it has a talk in CppCon2025 here: https://www.youtube.com/watch?v=xu4pI72zlO4
Kudos to the author for bringing something like this to C++! Definitely useful.
I would like to hear your opinions on this style of EDSL GUI embedding and pros/cons you find for those of you who do GUI programming all the time.
Also, wild idea: with the power of compile-time C++ programming and C++26 reflection, it would be possible to get existing xaml interfaces and convert it into regular C++ at compile-time via #embed or #include and not even changing the EDSL itself and making it directly embedded and reusable in C++? That would be plenty useful.
https://redd.it/1ry51fg
@r_cpp
GitHub
GitHub - rmpowell77/wxUI: C++ header-only library to make declarative UIs for wxWidgets.
C++ header-only library to make declarative UIs for wxWidgets. - rmpowell77/wxUI
C++26: Structured bindings in conditions
https://www.sandordargo.com/blog/2026/04/15/cpp26-structured-bindings-condition
https://redd.it/1smrf3p
@r_cpp
https://www.sandordargo.com/blog/2026/04/15/cpp26-structured-bindings-condition
https://redd.it/1smrf3p
@r_cpp
Sandor Dargo’s Blog
C++26: Structured bindings in conditions
Structured bindings were introduced in C++17 as an alternative way of declaring variables. They allow you to decompose an object into a set of named variables, where the collection of those bindings conceptually represents the original object as a whole.…
I built a C++ integer-to-string library based on a new AVX-512 paper
I built a small C++ integer-to-string conversion library based on a new paper by Jael Champagne Gareau and Daniel Lemire, "Converting an Integer to a Decimal String in Under Two Nanoseconds":
- Project: https://github.com/simditoa/simditoa
- Paper: https://arxiv.org/abs/2604.26019
The paper looks at decimal formatting for integers, which shows up in logging, JSON/CSV/XML serialization, database output, and other places where numbers eventually become text. The interesting part, and the part I wanted to experiment with, is that it uses AVX-512 IFMA instructions to extract multiple decimal digits in parallel, avoiding the usual repeated division/modulo loop and avoiding large lookup tables.
The library exposes a small
Current project shape:
- C++17
-
- AVX-512 IFMA + VBMI path for supported x86-64 CPUs
- portable scalar fallback
- CMake package/install support
- tests for edge cases, digit lengths, and randomized values
- a simple benchmark against
The README benchmark currently shows
The core trick is neat: for 8-digit chunks, it uses AVX-512 IFMA with precomputed constants based on
https://redd.it/1t2ny62
@r_cpp
I built a small C++ integer-to-string conversion library based on a new paper by Jael Champagne Gareau and Daniel Lemire, "Converting an Integer to a Decimal String in Under Two Nanoseconds":
- Project: https://github.com/simditoa/simditoa
- Paper: https://arxiv.org/abs/2604.26019
The paper looks at decimal formatting for integers, which shows up in logging, JSON/CSV/XML serialization, database output, and other places where numbers eventually become text. The interesting part, and the part I wanted to experiment with, is that it uses AVX-512 IFMA instructions to extract multiple decimal digits in parallel, avoiding the usual repeated division/modulo loop and avoiding large lookup tables.
The library exposes a small
to_chars-style API:#include "simditoa.h"
char buf[simditoa::MAX_DIGITS + 1];
size_t len = simditoa::to_chars(12345, buf);
buf[len] = '\0';
Current project shape:
- C++17
-
int64_t and uint64_t support- AVX-512 IFMA + VBMI path for supported x86-64 CPUs
- portable scalar fallback
- CMake package/install support
- tests for edge cases, digit lengths, and randomized values
- a simple benchmark against
std::to_charsThe README benchmark currently shows
simditoa::to_chars at about 15.82 ns/int versus 36.35 ns/int for std::to_chars on the tested setup, roughly 2.3x faster in that run. The paper reports stronger results for its full algorithm and benchmark suite, including single-core performance ahead of other tested methods, but my repo should be treated as a compact implementation based on the paper rather than a full reproduction of every variant in it.The core trick is neat: for 8-digit chunks, it uses AVX-512 IFMA with precomputed constants based on
floor(2^52 / 10^k) to compute digit positions in parallel, then gathers the digit bytes with AVX-512 byte permutation. Larger values are split into chunks.https://redd.it/1t2ny62
@r_cpp
GitHub
GitHub - simditoa/simditoa: SIMD-accelerated integer-to-string conversion
SIMD-accelerated integer-to-string conversion. Contribute to simditoa/simditoa development by creating an account on GitHub.
Are you satisfied with the current state of C++ CLI parsers?
There are already many excellent C++ CLI parsers out there, but most of them still revolve around mutable runtime builder APIs.
Most existing parsers look something like this:
CLI::App app{"example"};
std::string output;
bool verbose = false;
std::vector<std::string> inputs;
app.add_option("-o,--output", output, "output file");
app.add_flag("-v,--verbose", verbose, "verbose mode");
app.add_option("inputs", inputs, "input files")->required();
CLI11_PARSE(app, argc, argv);
Why are duplicate option names still runtime errors in C++ CLI parsers? Where is the compile-time validation?
Why is the command schema still built through runtime mutation? Many CLI schemas are effectively static. Why not treat them as a static schema and let the compiler enforce it?
One of the things I love about C++ is its ability to express intent and constraints in the type system and let the compiler enforce them.
But many C++ CLI parsers still rely heavily on runtime mutation and stringly-typed APIs.
Rust has `clap`, which is a great typed CLI parser. So what about C++?
So I wanted to explore what that direction could look like in modern C++20.
**I built a new C++ CLI parser** that takes a different approach:
#include <cli/cli.hh>
struct Args {
cli::Flag<"verbose", 'v'> verbose;
cli::StringOption<"output", 'o'> output;
cli::Positional<std::string, cli::nargs::one_or_more> inputs;
};
using namespace std;
auto main(int argc, char** argv) -> int {
// Returns parsed Args or exits with an error message.
const auto args = cli::parseOrExit<Args>(argc, argv);
std::cout << "verbose: " << args.verbose.value() << '\n';
if (args.output.has_value()) {
std::cout << "output: " << *args.output.value() << '\n';
}
for (const auto& input : args.inputs.value()) {
std::cout << input << '\n';
}
}
**Try this out on Godbolt:** [https://godbolt.org/z/53d8rEMoP](https://godbolt.org/z/53d8rEMoP)
The goal is to explore a C++20-native parser API where the command line is represented as a typed schema rather than a mutable runtime builder.
The interesting part for me is that this works in **plain C++20**, without reflection, macros, code generation, or external tooling.
Repository: [https://github.com/CLI20-dev/cli20](https://github.com/CLI20-dev/cli20)
Still early-stage. I'm mainly looking for feedback on API ergonomics, diagnostics, and the compile-time/runtime tradeoff.
https://redd.it/1t5fovs
@r_cpp
There are already many excellent C++ CLI parsers out there, but most of them still revolve around mutable runtime builder APIs.
Most existing parsers look something like this:
CLI::App app{"example"};
std::string output;
bool verbose = false;
std::vector<std::string> inputs;
app.add_option("-o,--output", output, "output file");
app.add_flag("-v,--verbose", verbose, "verbose mode");
app.add_option("inputs", inputs, "input files")->required();
CLI11_PARSE(app, argc, argv);
Why are duplicate option names still runtime errors in C++ CLI parsers? Where is the compile-time validation?
Why is the command schema still built through runtime mutation? Many CLI schemas are effectively static. Why not treat them as a static schema and let the compiler enforce it?
One of the things I love about C++ is its ability to express intent and constraints in the type system and let the compiler enforce them.
But many C++ CLI parsers still rely heavily on runtime mutation and stringly-typed APIs.
Rust has `clap`, which is a great typed CLI parser. So what about C++?
So I wanted to explore what that direction could look like in modern C++20.
**I built a new C++ CLI parser** that takes a different approach:
#include <cli/cli.hh>
struct Args {
cli::Flag<"verbose", 'v'> verbose;
cli::StringOption<"output", 'o'> output;
cli::Positional<std::string, cli::nargs::one_or_more> inputs;
};
using namespace std;
auto main(int argc, char** argv) -> int {
// Returns parsed Args or exits with an error message.
const auto args = cli::parseOrExit<Args>(argc, argv);
std::cout << "verbose: " << args.verbose.value() << '\n';
if (args.output.has_value()) {
std::cout << "output: " << *args.output.value() << '\n';
}
for (const auto& input : args.inputs.value()) {
std::cout << input << '\n';
}
}
**Try this out on Godbolt:** [https://godbolt.org/z/53d8rEMoP](https://godbolt.org/z/53d8rEMoP)
The goal is to explore a C++20-native parser API where the command line is represented as a typed schema rather than a mutable runtime builder.
The interesting part for me is that this works in **plain C++20**, without reflection, macros, code generation, or external tooling.
Repository: [https://github.com/CLI20-dev/cli20](https://github.com/CLI20-dev/cli20)
Still early-stage. I'm mainly looking for feedback on API ergonomics, diagnostics, and the compile-time/runtime tradeoff.
https://redd.it/1t5fovs
@r_cpp
godbolt.org
Compiler Explorer - C++
struct BuildArgs {
cli::Flag<"release", 'r'> release{
{.help = "Build with optimizations"}};
cli::IntOption<"jobs", 'j'> jobs{
{.help = "Parallel jobs", .presence = cli::required}};
};
struct Args {
cli::Description description{"A tiny…
cli::Flag<"release", 'r'> release{
{.help = "Build with optimizations"}};
cli::IntOption<"jobs", 'j'> jobs{
{.help = "Parallel jobs", .presence = cli::required}};
};
struct Args {
cli::Description description{"A tiny…
Poor man's defineaggregate
TLDR: [Try it on Compiler Explorer](https://godbolt.org/z/sfczoP7hr).
----
While waiting for Clang to support `defineaggregate
template <auto name>
struct FieldByName
{
};
We calculate the layout of our fake aggregate (sizes, alignment, offsets, ...) at compile time, like so:
// Layout information
template <auto... fields>
struct MetaAggregateInfo
{
static consteval auto calcalign(std::sizet offset, std::sizet align)
{
return (offset + align - 1) & ~(align - 1);
}
static constexpr std::array names{ std::stringview(fields.name)... };
static constexpr std::array sizes{ sizeof(typename decltype(fields)::Type)... };
static constexpr std::array aligns{ alignof(typename decltype(fields)::Type)... };
static constexpr auto maxalign = std::ranges::max(aligns);
static constexpr auto offsets = {
std::removeconstt<decltype(sizes)> offsets;
std::sizet nextoffset = 0;
for (auto size, align, offset : std::views::zip(sizes, aligns, offsets))
{
offset = calcalign(nextoffset, align);
nextoffset = offset + size;
}
return offsets;
}();
static constexpr auto totalsize = calcalign(offsets.back() + sizes.back(), maxalign);
};
I found it simpler to just use a partial specialization for the case where the aggregate has no members:
template <>
struct MetaAggregateInfo<>
{
static constexpr std::array<std::stringview, 0> names{};
static constexpr std::array<std::sizet, 0> sizes{};
static constexpr std::array<std::sizet, 0> aligns{};
static constexpr auto maxalign = 1uz;
static constexpr std::array<std::sizet, 0> offsets{};
static constexpr auto totalsize = 1uz;
};
}
A few more helpers:
// Use to declare the type of a field. See example below.
template <typename T>
constexpr detail::FieldType<T> type;
// Type and name of a field
template <typename TheType, std::sizet size>
struct Field
{
using Type = TheType;
detail::FieldType<TheType> type;
std::array<char, size> name;
};
// Use to declare the name of a field
template <detail::ConstexprStringHelper helper>
consteval auto operator""name()
{
return helper.array;
}
// Use with operator to access a field by name
template <detail::ConstexprStringHelper helper>
consteval auto operator""field() -> detail::FieldByName<helper.array>
{
return {};
}
And now the meat of the code:
template <auto... fields>
class MetaAggregate
{
public:
static constexpr detail::MetaAggregateInfo<fields...> info{};
We define our constructors, copy/move operators and destructor. We use the offsets to get a pointer on which we can do a placement `new`. Other than that, this part is not very interesting.
MetaAggregate()
requires(std::defaultinitializable<typename decltype(fields)::Type> && ...)
{
std::apply(
& { (new (storage.data() + offset) decltype(fields)::Type(), ...); },
info.offsets
);
}
MetaAggregate(const MetaAggregate& other)
requires(std::copyconstructible<typename decltype(fields)::Type> && ...)
: MetaAggregate(other.refs())
{
}
MetaAggregate(MetaAggregate&& other)
requires(std::moveconstructible<typename
TLDR: [Try it on Compiler Explorer](https://godbolt.org/z/sfczoP7hr).
----
While waiting for Clang to support `defineaggregate
, I got curious about whether it's possible to do something similar in C++23. Turns out it *kinda* is.
### Rules:
- Only C++23 features;
- No external programs;
- No macros;
- Generated code should be similar to just using a struct.
----
We start with some helper types:
#include <algorithm>
#include <array>
#include <concepts>
#include <functional>
#include <print>
#include <ranges>
#include <string_view>
#include <tuple>
#include <type_traits>
namespace detail
{
// See type
template <typename T>
struct FieldType
{
using Type = T;
};
// Helper for using a string as a template parameter
template <std::size_t size>
struct ConstexprStringHelper
{
std::array<char, size - 1> array;
constexpr ConstexprStringHelper(const char (&c_array)[size])
{
std::copy_n(c_array, size - 1, std::begin(array));
}
};
// See operator""field`template <auto name>
struct FieldByName
{
};
We calculate the layout of our fake aggregate (sizes, alignment, offsets, ...) at compile time, like so:
// Layout information
template <auto... fields>
struct MetaAggregateInfo
{
static consteval auto calcalign(std::sizet offset, std::sizet align)
{
return (offset + align - 1) & ~(align - 1);
}
static constexpr std::array names{ std::stringview(fields.name)... };
static constexpr std::array sizes{ sizeof(typename decltype(fields)::Type)... };
static constexpr std::array aligns{ alignof(typename decltype(fields)::Type)... };
static constexpr auto maxalign = std::ranges::max(aligns);
static constexpr auto offsets = {
std::removeconstt<decltype(sizes)> offsets;
std::sizet nextoffset = 0;
for (auto size, align, offset : std::views::zip(sizes, aligns, offsets))
{
offset = calcalign(nextoffset, align);
nextoffset = offset + size;
}
return offsets;
}();
static constexpr auto totalsize = calcalign(offsets.back() + sizes.back(), maxalign);
};
I found it simpler to just use a partial specialization for the case where the aggregate has no members:
template <>
struct MetaAggregateInfo<>
{
static constexpr std::array<std::stringview, 0> names{};
static constexpr std::array<std::sizet, 0> sizes{};
static constexpr std::array<std::sizet, 0> aligns{};
static constexpr auto maxalign = 1uz;
static constexpr std::array<std::sizet, 0> offsets{};
static constexpr auto totalsize = 1uz;
};
}
A few more helpers:
// Use to declare the type of a field. See example below.
template <typename T>
constexpr detail::FieldType<T> type;
// Type and name of a field
template <typename TheType, std::sizet size>
struct Field
{
using Type = TheType;
detail::FieldType<TheType> type;
std::array<char, size> name;
};
// Use to declare the name of a field
template <detail::ConstexprStringHelper helper>
consteval auto operator""name()
{
return helper.array;
}
// Use with operator to access a field by name
template <detail::ConstexprStringHelper helper>
consteval auto operator""field() -> detail::FieldByName<helper.array>
{
return {};
}
And now the meat of the code:
template <auto... fields>
class MetaAggregate
{
public:
static constexpr detail::MetaAggregateInfo<fields...> info{};
We define our constructors, copy/move operators and destructor. We use the offsets to get a pointer on which we can do a placement `new`. Other than that, this part is not very interesting.
MetaAggregate()
requires(std::defaultinitializable<typename decltype(fields)::Type> && ...)
{
std::apply(
& { (new (storage.data() + offset) decltype(fields)::Type(), ...); },
info.offsets
);
}
MetaAggregate(const MetaAggregate& other)
requires(std::copyconstructible<typename decltype(fields)::Type> && ...)
: MetaAggregate(other.refs())
{
}
MetaAggregate(MetaAggregate&& other)
requires(std::moveconstructible<typename
godbolt.org
Compiler Explorer - C++
namespace detail
{
// See `type`
template <typename T>
struct FieldType
{
using Type = T;
};
// Helper for using a string as a template parameter
template <std::size_t size>
struct ConstexprStringHelper
{
std::array<char, size - 1> array;
constexpr…
{
// See `type`
template <typename T>
struct FieldType
{
using Type = T;
};
// Helper for using a string as a template parameter
template <std::size_t size>
struct ConstexprStringHelper
{
std::array<char, size - 1> array;
constexpr…
Poor man's define_aggregate
TLDR: [Try it on Compiler Explorer](https://godbolt.org/z/sfczoP7hr).
----
While waiting for Clang to support `define_aggregate`, I got curious about whether it's possible to do something similar in C++23. Turns out it *kinda* is.
### Rules:
- Only C++23 features;
- No external programs;
- No macros;
- Generated code should be similar to just using a `struct`.
----
We start with some helper types:
#include <algorithm>
#include <array>
#include <concepts>
#include <functional>
#include <print>
#include <ranges>
#include <string_view>
#include <tuple>
#include <type_traits>
namespace detail
{
// See `type`
template <typename T>
struct FieldType
{
using Type = T;
};
// Helper for using a string as a template parameter
template <std::size_t size>
struct ConstexprStringHelper
{
std::array<char, size - 1> array;
constexpr ConstexprStringHelper(const char (&c_array)[size])
{
std::copy_n(c_array, size - 1, std::begin(array));
}
};
// See `operator""_field`
template <auto name>
struct FieldByName
{
};
We calculate the layout of our fake aggregate (sizes, alignment, offsets, ...) at compile time, like so:
// Layout information
template <auto... fields>
struct MetaAggregateInfo
{
static consteval auto calc_align(std::size_t offset, std::size_t align)
{
return (offset + align - 1) & ~(align - 1);
}
static constexpr std::array names{ std::string_view(fields.name)... };
static constexpr std::array sizes{ sizeof(typename decltype(fields)::Type)... };
static constexpr std::array aligns{ alignof(typename decltype(fields)::Type)... };
static constexpr auto max_align = std::ranges::max(aligns);
static constexpr auto offsets = [] {
std::remove_const_t<decltype(sizes)> offsets;
std::size_t next_offset = 0;
for (auto [size, align, offset] : std::views::zip(sizes, aligns, offsets))
{
offset = calc_align(next_offset, align);
next_offset = offset + size;
}
return offsets;
}();
static constexpr auto total_size = calc_align(offsets.back() + sizes.back(), max_align);
};
I found it simpler to just use a partial specialization for the case where the aggregate has no members:
template <>
struct MetaAggregateInfo<>
{
static constexpr std::array<std::string_view, 0> names{};
static constexpr std::array<std::size_t, 0> sizes{};
static constexpr std::array<std::size_t, 0> aligns{};
static constexpr auto max_align = 1uz;
static constexpr std::array<std::size_t, 0> offsets{};
static constexpr auto total_size = 1uz;
};
}
A few more helpers:
// Use to declare the type of a field. See example below.
template <typename T>
constexpr detail::FieldType<T> type;
// Type and name of a field
template <typename TheType, std::size_t size>
struct Field
{
using Type = TheType;
detail::FieldType<TheType> type;
std::array<char, size> name;
};
// Use to declare the name of a field
template <detail::ConstexprStringHelper helper>
consteval auto operator""_name()
{
return helper.array;
}
// Use with operator[] to access a field by name
template <detail::ConstexprStringHelper helper>
consteval auto operator""_field() -> detail::FieldByName<helper.array>
{
return {};
}
And now the meat of the code:
template <auto... fields>
class MetaAggregate
{
public:
static constexpr detail::MetaAggregateInfo<fields...> info{};
We define our constructors, copy/move operators and destructor. We use the offsets to get a pointer on which we can do a placement `new`. Other than that, this part is not very interesting.
MetaAggregate()
requires(std::default_initializable<typename decltype(fields)::Type> && ...)
{
std::apply(
[&](auto... offset) { (new (storage.data() + offset) decltype(fields)::Type(), ...); },
info.offsets
);
}
MetaAggregate(const MetaAggregate& other)
requires(std::copy_constructible<typename decltype(fields)::Type> && ...)
: MetaAggregate(other.refs())
{
}
MetaAggregate(MetaAggregate&& other)
requires(std::move_constructible<typename
TLDR: [Try it on Compiler Explorer](https://godbolt.org/z/sfczoP7hr).
----
While waiting for Clang to support `define_aggregate`, I got curious about whether it's possible to do something similar in C++23. Turns out it *kinda* is.
### Rules:
- Only C++23 features;
- No external programs;
- No macros;
- Generated code should be similar to just using a `struct`.
----
We start with some helper types:
#include <algorithm>
#include <array>
#include <concepts>
#include <functional>
#include <print>
#include <ranges>
#include <string_view>
#include <tuple>
#include <type_traits>
namespace detail
{
// See `type`
template <typename T>
struct FieldType
{
using Type = T;
};
// Helper for using a string as a template parameter
template <std::size_t size>
struct ConstexprStringHelper
{
std::array<char, size - 1> array;
constexpr ConstexprStringHelper(const char (&c_array)[size])
{
std::copy_n(c_array, size - 1, std::begin(array));
}
};
// See `operator""_field`
template <auto name>
struct FieldByName
{
};
We calculate the layout of our fake aggregate (sizes, alignment, offsets, ...) at compile time, like so:
// Layout information
template <auto... fields>
struct MetaAggregateInfo
{
static consteval auto calc_align(std::size_t offset, std::size_t align)
{
return (offset + align - 1) & ~(align - 1);
}
static constexpr std::array names{ std::string_view(fields.name)... };
static constexpr std::array sizes{ sizeof(typename decltype(fields)::Type)... };
static constexpr std::array aligns{ alignof(typename decltype(fields)::Type)... };
static constexpr auto max_align = std::ranges::max(aligns);
static constexpr auto offsets = [] {
std::remove_const_t<decltype(sizes)> offsets;
std::size_t next_offset = 0;
for (auto [size, align, offset] : std::views::zip(sizes, aligns, offsets))
{
offset = calc_align(next_offset, align);
next_offset = offset + size;
}
return offsets;
}();
static constexpr auto total_size = calc_align(offsets.back() + sizes.back(), max_align);
};
I found it simpler to just use a partial specialization for the case where the aggregate has no members:
template <>
struct MetaAggregateInfo<>
{
static constexpr std::array<std::string_view, 0> names{};
static constexpr std::array<std::size_t, 0> sizes{};
static constexpr std::array<std::size_t, 0> aligns{};
static constexpr auto max_align = 1uz;
static constexpr std::array<std::size_t, 0> offsets{};
static constexpr auto total_size = 1uz;
};
}
A few more helpers:
// Use to declare the type of a field. See example below.
template <typename T>
constexpr detail::FieldType<T> type;
// Type and name of a field
template <typename TheType, std::size_t size>
struct Field
{
using Type = TheType;
detail::FieldType<TheType> type;
std::array<char, size> name;
};
// Use to declare the name of a field
template <detail::ConstexprStringHelper helper>
consteval auto operator""_name()
{
return helper.array;
}
// Use with operator[] to access a field by name
template <detail::ConstexprStringHelper helper>
consteval auto operator""_field() -> detail::FieldByName<helper.array>
{
return {};
}
And now the meat of the code:
template <auto... fields>
class MetaAggregate
{
public:
static constexpr detail::MetaAggregateInfo<fields...> info{};
We define our constructors, copy/move operators and destructor. We use the offsets to get a pointer on which we can do a placement `new`. Other than that, this part is not very interesting.
MetaAggregate()
requires(std::default_initializable<typename decltype(fields)::Type> && ...)
{
std::apply(
[&](auto... offset) { (new (storage.data() + offset) decltype(fields)::Type(), ...); },
info.offsets
);
}
MetaAggregate(const MetaAggregate& other)
requires(std::copy_constructible<typename decltype(fields)::Type> && ...)
: MetaAggregate(other.refs())
{
}
MetaAggregate(MetaAggregate&& other)
requires(std::move_constructible<typename
godbolt.org
Compiler Explorer - C++
namespace detail
{
// See `type`
template <typename T>
struct FieldType
{
using Type = T;
};
// Helper for using a string as a template parameter
template <std::size_t size>
struct ConstexprStringHelper
{
std::array<char, size - 1> array;
constexpr…
{
// See `type`
template <typename T>
struct FieldType
{
using Type = T;
};
// Helper for using a string as a template parameter
template <std::size_t size>
struct ConstexprStringHelper
{
std::array<char, size - 1> array;
constexpr…
fluxen: a single-header key-value store for C++20
I built fluxen to solve a problem I kept running into in my side projects. I wanted persistent key-value storage without pulling in a full database or dealing with CMake/linking.
To use fluxen, you just drop the header into your project and you have a persistent key-value database that you can learn to use in an afternoon, with no CMake, no dependencies, and no linking.
#include "fluxen.hpp"
fluxen::DB db("myapp.db");
db.put("username", "jim");
if (auto name = db.get("username")) {
std::cout << *name << "\n"; // jim
}
It supports strings, numbers, and any trivially copyable type without the need for manual serialization. Transactions batch writes into a single syscall and guarantee durability via fsync.
GitHub: [https://github.com/dvuvud/fluxen](https://github.com/dvuvud/fluxen)
Docs: [https://dvuvud.github.io/fluxen](https://dvuvud.github.io/fluxen)
https://redd.it/1t928n5
@r_cpp
I built fluxen to solve a problem I kept running into in my side projects. I wanted persistent key-value storage without pulling in a full database or dealing with CMake/linking.
To use fluxen, you just drop the header into your project and you have a persistent key-value database that you can learn to use in an afternoon, with no CMake, no dependencies, and no linking.
#include "fluxen.hpp"
fluxen::DB db("myapp.db");
db.put("username", "jim");
if (auto name = db.get("username")) {
std::cout << *name << "\n"; // jim
}
It supports strings, numbers, and any trivially copyable type without the need for manual serialization. Transactions batch writes into a single syscall and guarantee durability via fsync.
GitHub: [https://github.com/dvuvud/fluxen](https://github.com/dvuvud/fluxen)
Docs: [https://dvuvud.github.io/fluxen](https://dvuvud.github.io/fluxen)
https://redd.it/1t928n5
@r_cpp
GitHub
GitHub - dvuvud/fluxen: Single-header embedded key-value store
Single-header embedded key-value store. Contribute to dvuvud/fluxen development by creating an account on GitHub.