C++ - Reddit
228 subscribers
48 photos
8 videos
25.3K links
Stay up-to-date with everything C++!
Content directly fetched from the subreddit just for you.

Join our group for discussions : @programminginc

Powered by : @r_channels
Download Telegram
libcwd (C++ debugging library) released under MIT license!

Hi all,

I am happy to announce that after 333 commits spanning two months of continuous work, I released version 2 of libcwd, now under a new license: the MIT license!

The website has been re-done (as well as a lot of other things);
see https://carlowood.github.io/libcwd/index.html?libcwd-theme=dark

There you can also find how to get it (basically, from the git repository; there is no tar ball (yet)).

Let me know what you think or if you need help,
my email address is at the bottom of the INSTALL file.

Carlo Wood

---

Background

For those unfamiliar with libcwd. Version 0.99 was the first public release in 2000 under the QPL; I've used and tuned it for more than two decades, being a very active C++ developer myself (on linux).

Version 1.x had memory allocation support; I removed this in version 2 because it made things very very complicated, and I never needed that myself anymore since a decade anyway.

Version 2 still does, as did version 1, ELF and DWARF decoding of the executable and linked shared libraries. For this a POSIX system with ELF is necessary. But libcwd can be configured without Location support too; you should be able to use it for just (multi-threaded) debug output on, for example, Windows.

https://redd.it/1uq2fql
@r_cpp
C++ Primer 6th edition by Stanley Lippman et al

Wondering if anyone has news on whether the 6th edition will be released? It has been slated for march 2025 but it's more than a year since then.

Stanley Lippman has passed in 2022, rip, so is the 6th edition never going to be released?

Have seen listings for the 6th edition on online shop pages but they are stated as unavailable yet.

https://redd.it/1uq3vl5
@r_cpp
Stream compaction on NEON: vectorizing copy_if by hand (30x)

## Problem

Given two arrays `a` and `out`, write into `out`, with no gaps, only those elements of `a` that satisfy a given
condition.
Here, the condition is `a[i] > threshold`, with `a[i] ∈ (0, 1)` and `threshold ∈ {0, 0.5, 1}`.

## Why the compiler gives up
A single `if` in a copy loop drops throughput from 112 to as low as 2.6 GB/s:
the compiler can't vectorize it, because NEON has no compress instruction. Here's how to build it.
```cpp
auto copy_if(const float* a, float* out, size_t n) {
size_t j = 0;
for (size_t i = 0; i < n; ++i) {
if (a[i] > 0) out[j++] = a[i];
}
return j;
}
```

In copy_if, the output cursor `j` depends on the data. To vectorize the loop, the compiler needs a compress instruction
(one that collects selected elements at the front of the register, with no gaps). NEON has no such instruction, so the
compiler
gives up:

```text
clang++ -O3 -Rpass-analysis=loop-vectorize -std=c++23 main.cpp -o main

main.cpp:5:5: remark: loop not vectorized: value that could not be identified as reduction is used outside the loop [-Rpass-analysis=loop-vectorize]
5 | for (size_t i = 0; i < n; ++i) {
| ^
main.cpp:6:23: remark: loop not vectorized: cannot identify array bounds [-Rpass-analysis=loop-vectorize]
6 | if (a[i] > 0) out[j++] = a[i];
```

The clang vectorizer can only classify `j` as either an induction (fixed step) or a reduction,
but `j` is neither of those. It's a data-dependent cursor.
The compiler cannot vectorize this type of cursor.
The second remark has the same cause: it cannot compute the range of accesses to `out`.

## Benchmark: two scalar problems

*All benchmarks: Apple M5; clang++ -O3 -std=c++23 -march=native; GB/s = (2n * 4 bytes) / time, min of 3e9 / n runs;
cache: n=1e5, DRAM: n=1e7*

| function | ms (cache) | GB/s (cache) | ms (DRAM) | GB/s (DRAM) |
|-------------------------|-----------:|-------------:|----------:|------------:|
| copy a[i] | 0.004 | 195 | 0.71 | 112 |
| copy a[i] if a[i] > 0 | 0.022 | 37 | 2.41 | 33 |
| copy a[i] if a[i] > 0.5 | 0.258 | 3.1 | 30.61 | 2.6 |
| copy a[i] if a[i] > 1 | 0.021 | 37 | 2.39 | 33 |

"copy a[i]" is the same loop, but with no condition. The compiler vectorizes it. The only difference is a single `if`.
The same data, only the branch predictability changes:

1. \> 0 (always true) and > 1 (always false): branch predictor never misses → 33 GB/s. The lack of vectorization costs
3x.
2. \> 0.5 (50/50): the branch predictor misses on every second element → 3 GB/s

The trick fixes both problems.

## Trick 1: compress emulation

Let `n` be a multiple of the register width; the tail is a separate topic and has nothing to do with this trick.
Also:

1. The size of `out` must be >= `n`.
2. Suppose the algorithm selected `cnt` elements. Then all elements in `out[cnt, n)` are left undefined (garbage).
An algorithm that keeps the tail clean adds nothing new to the idea, so it will not be considered.

NEON - the SIMD instruction set used in Apple M-series chips and almost every mobile core - has no instruction for compressing a
register, so we have to emulate it.

(To be fair, the trick itself is not new. Lemire
[used it on SSE](https://lemire.me/blog/2017/01/20/how-quickly-can-you-remove-spaces-from-a-string/) back in 2017.
But NEON has no movemask and no cheap popcnt.)
Here's how to build it from what we do have.

What our compress analog needs to be able to do:

1. Accept a register from `a` and a mask register that says which elements to keep.
2. Return the number of elements we selected (to move the `out` pointer).
3. Store the selected elements in `out`.

### tbl: arbitrary byte selection

NEON has the table-lookup (`tbl`) instruction family. Its purpose is arbitrary byte permutation/selection.
The instruction accepts two registers:

1. `table` - the bytes to select from.
2. `index` - the positions of the bytes to take.

In other
words, this is a SIMD analog of `out[i] = table[index[i]]`.

We will use the `vqtbl1q_u8` instruction:

| part | meaning |
|:----:|------------------------------------------|
| v | vector intrinsic |
| q | table consists of 128-bit registers |
| tbl | table lookup |
| 1 | number of registers in table |
| q | result and indices are 128-bit registers |
| u8 | elements of table are `uint8_t` |

`tbl` permutes bytes, but we need to select floats (4 bytes). So, we will create `index` in blocks of 4 bytes:
to select the second (0-based) float of the register, `index` will contain its bytes [8, 9, 10, 11] (the second element
starts at an offset of `2 * sizeof(float) = 8`).

Computing `index` every time is slow. There are 16 variants in total (4 elements to take/drop), so we will precompute all the `index` variants.
But to select the `index` using the mask, we need to convert the mask to a number (call it `idx`):

### mask → idx

The mask consists of 4 elements, each either 0x00000000 (false) or 0xFFFFFFFF (true).
If the i-th element is true, we want to set the i-th bit in idx.

Trick: `mask & [1, 2, 4, 8]`. Because `0xFFFFFFFF & x = x`, the true elements keep their weight (1/2/4/8), while the false ones become 0.
We add all elements together and get a number between 0 and 15.

```cpp
std::array<uint32_t, 4> weights{1, 2, 4, 8};
size_t idx = vaddvq_u32(vandq_u32(mask, vld1q_u32(weights.data())));
```

- `vld1q_u32(weights.data())` - load 4 values from memory at address `weights.data()` into a register (ld - load)
- `vandq_u32` - elementwise & (and)
- `vaddvq_u32` - sum of all the elements in the register (addv - add across vector)

### Precompute the `index` table

There is no way to compute registers at compile time, so instead of `uint8x16_t` (register of 16 `uint8_t`) we will store `std::array<uint8_t, 16>`.
For each `idx` we will go through the 4 elements of `mask`. If the element is selected,
we append the indices of its 4 bytes into `index` at the cursor position and advance the cursor by 4.

```cpp
consteval auto make_index_table() {
std::array<std::array<uint8_t, 16>, 16> index{};
for (size_t idx = 0; idx < 16; ++idx) { // iterate over all masks
size_t j = 0; // j is the cursor
for (size_t i = 0; i < 4; ++i) // iterate over the mask's elements
if (idx & (1 << i)) // if the i-th element is selected
for (size_t k = 0; k < 4; ++k) // iterate over its bytes
index[idx][j++] = i * 4 + k; // store the indices of its bytes
}
return index;
}
```

The `j` cursor advances only on selected elements, so their bytes are placed in `index` consecutively. `tbl` with that `index`
collects floats into a register. Unused positions in `index` are zeros, so in the tail, after `count` elements, there will be garbage.

### The `count` table

Next we need to compute the number of elements we select. Similarly we can precompute a table for this:

```cpp
consteval auto make_count_table() {
std::array<uint8_t, 16> count{};
for (size_t idx = 0; idx < 16; ++idx)
for (size_t i = 0; i < 4; ++i)
if (idx & 1 << i)
++count[idx];
return count;
}
```

### The full `compress`

```cpp
auto compress(uint32x4_t mask, float32x4_t a) {
static constexpr std::array<uint32_t, 4> weights{1, 2, 4, 8};
const size_t idx = vaddvq_u32(vandq_u32(mask, vld1q_u32(weights.data())));

static constexpr auto count = make_count_table();
static constexpr auto index_table = make_index_table();

const auto index = vld1q_u8(index_table[idx].data()); // at runtime, loads only one row of the table into a register
return std::pair{vreinterpretq_f32_u8(vqtbl1q_u8(vreinterpretq_u8_f32(a), index)), count[idx]};

}
```
Because `tbl` works only with u8, we need to cast `a` to u8 and then cast the result back to f32.

We write full
registers of 4 floats to memory, but advance the cursor only by `cnt`.
`compress` stores valid elements at the front of the register, at `[j, j + cnt)`, and garbage at `[j + cnt, j + 4)`.
The next iteration will start at `j + cnt` and overwrite the garbage from the previous step.
Garbage will remain only in `out[cnt, n)` after the last store.
We don't go out of bounds because the cursor never overtakes the elements that have been read.

## The `copy_if` loop

```cpp
auto copy_if_neon(const float* __restrict a,
float* __restrict out,
float threshold,
size_t n) {
auto thd = vdupq_n_f32(threshold); // load threshold into a register
size_t j = 0;
for (size_t i = 0; i < n; i += 4) {
auto v = vld1q_f32(a + i); // load the current 4 elements of a into a register
auto mask = vcgtq_f32(v, thd); // compute the mask

auto [packed, cnt] = compress(mask, v);
vst1q_f32(out + j, packed); // store packed into out[j, j + 4). [j + cnt, j + 4) will hold garbage
j += cnt;
}
return j;
}
```

- `vcgtq_f32(v, thd)` - calculate elementwise `v[i] > thd[i]`. `cgt` - compare greater
- `vst1q_f32` - store 4 floats from a register into memory. `st` - store

## Result

| function | ms (cache) | GB/s (cache) | ms (DRAM) | GB/s (DRAM) |
|-------------------------|-----------:|-------------:|----------:|------------:|
| copy a[i] if a[i] > 0 | 0.0104 | 77 | 1.11 | 72 |
| copy a[i] if a[i] > 0.5 | 0.01063 | 75 | 1.13 | 71 |
| copy a[i] if a[i] > 1 | 0.01 | 80 | 1.06 | 76 |

\> 0.5 was the worst case for the scalar version, 3 GB/s. Now 71 GB/s. A more than 20x speedup.
Now there are no branches, so speed doesn't depend on data.

## Trick 2: calculating `idx` and `count` in a single `addv`

`idx` is always less than 16, so let weights = {1 + 16, 2 + 16, 4 + 16, 8 + 16}
and s = sum across mask & weights. Then s / 16 is the element count and s % 16 is `idx`.
So, we don't need to compute the `count` table.
`compress` now:
```cpp
auto compress(uint32x4_t mask, float32x4_t a) {
static constexpr std::array<uint32_t, 4> weights{1 + 16, 2 + 16, 4 + 16, 8 + 16};
const size_t s = vaddvq_u32(vandq_u32(mask, vld1q_u32(weights.data())));
const size_t count = s >> 4; // same as s / 16
const size_t idx = s & 15; // same as s % 16

static constexpr auto index_table = make_index_table();

const auto index = vld1q_u8(index_table[idx].data());
return std::pair{vreinterpretq_f32_u8(vqtbl1q_u8(vreinterpretq_u8_f32(a), index)), count};
}
```

And now the speed climbs again:

| function | ms (cache) | GB/s (cache) | ms (DRAM) | GB/s (DRAM) |
|-------------------------|-----------:|-------------:|----------:|------------:|
| copy a[i] if a[i] > 0 | 0.0095 | 84 | 1.015 | 79 |
| copy a[i] if a[i] > 0.5 | 0.0095 | 84 | 1.007 | 79 |
| copy a[i] if a[i] > 1 | 0.0096 | 83 | 1.008 | 79 |

## Unroll

We can squeeze out more speed by unrolling the loop 4x (16 elements per iteration):

| function | ms (cache) | GB/s (cache) | ms (DRAM) | GB/s (DRAM) |
|-------------------------|-----------:|-------------:|----------:|------------:|
| copy a[i] if a[i] > 0 | 0.0081 | 98 | 0.882 | 91 |
| copy a[i] if a[i] > 0.5 | 0.0083 | 97 | 0.892 | 90 |
| copy a[i] if a[i] > 1 | 0.0082 | 97 | 0.869 | 92 |

Final code ([godbolt](https://godbolt.org/z/n8E6ocKoj)):

```cpp
consteval auto make_index_table() {
std::array<std::array<uint8_t, 16>, 16> index{};
for (size_t idx = 0; idx < 16; ++idx) {
size_t j = 0;
for (size_t i = 0; i < 4; ++i)
if (idx & (1 << i))
for (size_t k = 0; k < 4; ++k)
index[idx][j++] = i * 4 + k;
}
return
index;
}
auto compress(uint32x4_t mask, float32x4_t a) {
static constexpr std::array<uint32_t, 4> weights{1 + 16, 2 + 16, 4 + 16, 8 + 16};
const size_t s = vaddvq_u32(vandq_u32(mask, vld1q_u32(weights.data())));
const size_t count = s >> 4;
const size_t idx = s & 15;

static constexpr auto index_table = make_index_table();

const auto index = vld1q_u8(index_table[idx].data());
return std::pair{vreinterpretq_f32_u8(vqtbl1q_u8(vreinterpretq_u8_f32(a), index)), count};
}
auto copy_if_neon_unroll(const float* __restrict a,
float* __restrict out,
float threshold,
size_t n) {
auto thd = vdupq_n_f32(threshold);
size_t j = 0;
size_t i = 0;
for (; i + 16 <= n; i += 16) {
#pragma unroll
for (size_t i0 = 0; i0 < 16; i0 += 4) {
auto v = vld1q_f32(a + i + i0);
auto mask = vcgtq_f32(v, thd);
auto [packed, cnt] = compress(mask, v);
vst1q_f32(out + j, packed);
j += cnt;
}
}
for (; i + 4 <= n; i += 4) {
auto v = vld1q_f32(a + i);
auto mask = vcgtq_f32(v, thd);

auto [packed, cnt] = compress(mask, v);
vst1q_f32(out + j, packed);
j += cnt;
}
return j;
}
```
`tbl` and the index table provide `compress`, something that NEON doesn't have out of the box.
This isn't just about `> threshold`. Filter, remove and other data-dependent functions are built the same way.


https://redd.it/1uqscnr
@r_cpp
Interesting behavior from C++20 to C++23

Consider the following snippet

int& get()
{
    int x;
    return x;
}


int main()
{
}

on GCC it compiles for C++20 but not for C++23

It returns with the error:

test.cpp:4:12: error: cannot bind non-const lvalue reference of type 'int&' to an rvalue of type 'int'

C++ is now suddenly treating the variable x as an rvalue?


Edit: Im not talking about dangling reference, thats just for the sake of the example

https://redd.it/1usgvun
@r_cpp
Trying to create a paper piano

Trying to create a paper piano

I came across many paper piano projects where they detect finger and paper touch using opencv and some even use neural network but the problem still remains that it's not accessible for everyone. Some people still don't have computers or even laptops. So I thought about a paper piano project that uses only the mobile phone and itscamera and using a cheap phone stand to set the correct angle and a printed paper. So a mobile phone which most people have, an app to download for free and a mobile stand with a printed paper which altogether costs approx $ 2.5. The reason for this post is that I may not work on this project anytime sooner so if anyone wants to work on this project help yourself and if you come across some references or a project similar to this then add it in the comment.

https://redd.it/1uslas1
@r_cpp
Libraries of, installing, and depending on C++20 modules

Until now, much of the discourse around C++20 modules has been around the tooling, and actually getting modules to work at all. I believe that now, in mid-2026, the tooling is mostly mature: the three largest compilers support most use-cases of modules. IDEs like CLion, and lint tools like ReSharper C++ and clangd support modules, with some caveats. CMake, xmake, Ninja, and other fledgling build systems have full support for modules. Many prevalent C++ libraries and projects have been recently modularised or are in the process of modularising.

I hope I'm not being too presumptive in saying the community is more or less ready (albeit horribly late...) to move to the next step, and start discussing how C++20 modules can and should tie in to inter-project work, rather than simply using modules within a project.

To begin with, I don't think the standard says anything about 'libraries'; these are existing paradigms grandfathered in from C or earlier. There are many axes we have to discuss here:

- Static archives
- Dynamically-linked libraries
- Symbol visibility defaults with `__declspec( dllexport )`
- Primary module interface-only (hereafter, PMI) libraries such as `module vulkan`
- Configuring such modules with macros
- Built module interface (hereafter, BMI) and binary interface (hereafter, ABI) compatibility; currently, BMIs are simply not portable, not even within a compiler toolchain across versions
- How shared objects, static archives, BMIs, and PMIs interact
- How build systems, toolchains, and package managers like conan and vcpkg interact with everything

For instance, consider I'm writing a 3D game engine. I want the following modules:

- vulkan which export imports std
- argparse
- glm
- glaze
- quill, which imports fmt
- fmt itself
- winrt, if running on Windows

I want to provide my own PMI that has export class Engine, and maybe some other functionality like abstractions over the 3D graphics APIs, an object and entity manager, a mini shader graph generator, and more. I also have export imported some symbols from my dependencies, especially std. I want to choose to configure my engine to render on D3D or Vulkan. Consumers can then load the library, add assets like textures, meshes, skeletons, shaders; they can plug the engine into a bigger project which might include a script interpreter in C++, real-time spatial audio and physics packages, some networking, and an XML-based UI system, and produce a complete game or visualisation executable.

Now, I mention these details just to flesh out the example to give a sense of a reasonably complicated library-esque project.

How does one even think about delivering this 'engine library' to the consumer? The traditional three configs are headers + precompiled DLL, headers + source, or headers only. Each have their established workflows. Source-available can be compiled into the entire binary with whole-program optimisation; headers-only libraries are exceptionally easy to vendor (just copy-paste). Header-only libraries can also be easily customised with consumer macros. PMIs, however, being translation units, cannot; we hit this when installing `module vulkan`. We need some module-compatible way to describe 'library configuration' beyond simply co-opting macros, something like Rust's `cfg`.

There is talk of the Common Package Specification (CPS), P1689R5, and P3286, but nothing concrete yet, especially since there has been no massive (commercial) push for
modules (at least, not until recently). This talk at NDC looks at a possible cargo-esque future for C++.

I'm writing this to spur some discussion here in the C++ community, and ask what some veterans of the build system/toolchain/package manager community think.

https://redd.it/1ustebp
@r_cpp
What Made You Fall in Love With C++—and What Almost Made You Quit?

This is a great opportunity to share your life story as a C++ programmer: how you started, why you chose C++, what your first impressive project made purely for fun was, and what kind of job you do.

You can share all of that here!

I would also love to hear the criticism—the days when you wanted to escape from boring CRUD and API projects, as well as the happiest days of your life when you finally got the chance to work on your dream job.

https://redd.it/1uto7bm
@r_cpp
Building a tool for offline coding practice on flights/trips — would you actually use this?

Hey everyone,

I'm a developer working on a side project/startup idea and wanted to get honest feedback before I sink more time into it.

The idea: a website where you pick a programming language and a specific track (e.g., Go basics, or backend development with Go), and it generates a downloadable offline package — documentation, tutorials, and exercises — packed into a zip file. You download it before you lose internet access (long flight, train, remote area, wherever), and once offline you just open it in your browser like a normal site. No connection needed.

I know there are already tools like DevDocs, Dash, and Zeal that let you save documentation offline. What I'm trying to do differently is focus on structured learning tracks with exercises, not just a raw doc viewer — more like a self-contained mini-course you can work through with zero connectivity.

I'm trying to figure out monetization without doing a subscription (that already feels like the wrong model for something people might use a handful of times a year). Current thinking is one-time purchase per track, or eventually partnering with airlines.

Genuine questions for you all:

1. Would you pay a small one-time fee for something like this? (like 5 dollars)
2. Would you use it even if it were free — or does existing offline documentation (Dash, DevDocs, etc.) already solve this well enough for you?

Trying not to build something nobody wants, so brutally honest feedback is welcome. Thanks!

https://redd.it/1utrasf
@r_cpp
Looking for C++ / Systems / HFT Internship or New Grad Opportunities

Title: Looking for C++ / Systems / HFT Internship or New Grad Opportunities

Hi everyone,

I'm currently looking for Software Engineering Intern, C++ Developer, Systems Programming, or HFT/Low-Latency Engineering opportunities.

Over the past year, I've focused heavily on C++, Linux, competitive programming, and building projects that pushed me beyond my comfort zone.

A few highlights about my profile:

Codeforces Specialist
CodeChef 4★
Strong foundation in DSA and Modern C++
Comfortable with Linux, Git, and CMake

I've built two major C++ projects:

A systems programming project focused on low-level software engineering and performance.
A low-latency order book/trading systems project that has received 50+ GitHub stars and valuable feedback from engineers with HFT backgrounds, including people associated with firms like IMC Trading and Jane Street. Their suggestions helped me improve both the project and my understanding of high-performance systems.

I'm particularly interested in roles involving:

High-Performance C++
Systems Programming
Backend Engineering
Low-Latency Infrastructure
Distributed Systems
Quant/HFT Technology

I'm always eager to learn, take on challenging engineering problems, and contribute to real-world systems.

If your company is hiring interns or new graduates, or if you know of any opportunities that match my profile, I'd greatly appreciate any referrals, advice, or connections.

Feel free to DM me if you'd like to connect.



https://redd.it/1utud1j
@r_cpp