#include <iostream>
#include <arm_sve.h> // The magical SVE library for modern ARM chips
// A function to add two arrays together using SVE
void add_arrays_sve(float* A, float* B, float* C, int n) {
int i = 0;
// Keep looping until we have processed all 'n' elements
while (i < n) {
// 1. THE MAGIC TAPE (Predicate)
// This generates a true/false mask based on how many numbers are left.
// It tells the CPU to "turn off" slots we don't need so we don't crash.
svbool_t mask = svwhilelt_b32(i, n);
// 2. Load data from A and B into our stretchy vectors, using the mask
svfloat32_t vecA = svld1_f32(mask, &A[i]);
svfloat32_t vecB = svld1_f32(mask, &B[i]);
// 3. Add the vectors together, safely ignoring masked-off slots
svfloat32_t vecC = svadd_f32_z(mask, vecA, vecB);
// 4. Store the results back into standard memory
svst1_f32(mask, &C[i], vecC);
// 5. THE STRETCHY PART
// svcntw() asks the CPU: "How many 32-bit words fit in your vector?"
// We move forward by that amount, whether it's 4, 8, 16, or 64!
i += svcntw();
}
}
int main() {
int n = 10; // 10 numbers total
float A[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
float B[10] = {10, 10, 10, 10, 10, 10, 10, 10, 10, 10};
float C[10] = {0}; // Where the answers go
add_arrays_sve(A, B, C, n);
std::cout << "Results of SVE math: ";
for(int i = 0; i < n; ++i) {
std::cout << C[i] << " ";
}
std::cout << "\n";
return 0;
}
Apr 3, 2026
[SIMD][SVE] example
Nov 25, 2025
[ACCU 2005] Learning To Stop Writing C++ Code minute
Learning To Stop Writing C++ Code (and Why You Won’t Miss It) - Daisy Hollman - ACCU 2025
https://www.youtube.com/watch?v=mpGx-_uLPDM&t=870s
Best Practices for coding with LLMs
- Use smaller files
- Over-test everything.
- LLMs are pretty good at generating tests for existing code
- But they also pretty decent at helping you with Test-Driven Development
- Well-contained unit tests are much easier for LLMs to reason about
- Encapsulation is critical!
LLMs currently struggle with "long-term learning"
Whereas a human working on the same project for weeks or months can abstract away the details of a complicated workflow and "learn" which poorly encapsulated sharp edges are ignorable, LLMs currently struggle with this kind of thing. (early 2025)
In other words, code coupling is bad—don't connect dissimilar things from different units of encapsulation in unintuitive ways.
- Naming is more important than ever
- Intuitive abstraction design goes a long ways
- Agents often don't know to "check" for unintuitive behavior
...or they might "check" sometimes and not other times - Writing abstractions that are easy to correctly "guess" how they work is important
- Write better (but still concise!) comments and documentation
QUOTE
The compiler does not read comments and neither do I — Bjarne Stroustrup
Maybe it's time to revise this? LLMs do read comments
- This is the opposite of code coupling—similar things within a given unit of encapsulation should be grouped together.
- "Don't Repeat Yourself" (DRY) coding helps make efficient use of the LLM's context window
- Don't do unexpected things
- Especially if those things often don't have syntax (e.g., copy constructors in C++, auto-dereferencing in Rust, non-idiomatic __getattribute__ in Python, etc.)
- In C++, use regular types whenever possible. (read my note about regular type: https://vsdmars.blogspot.com/2018/06/c-regular-type.html , basically, design by contract, precondition)
- Don't mix owning and non-owning semantics in the same type or template
- Don't mix value and reference semantics in the same type or template
- Both contracts and effects systems are ways of encapsulating information and reducing code coupling.
- Encapsulation is key to effectively working with LLMs because of the context window size constraints.
- But also, it's a lot easier to train LLMs on small, well-contained problems.
- Contracts promote Liskov Substitutability, allowing LLMs to infer behavior of a broader category of types.
The Concept: Labeling the "Black Box"
Nov 9, 2025
[C++] Use string_view _sv over raw char string
Reference:
https://youtu.be/jXQ6WtYmfZw?si=B_C-UXBVCFpAODVh&t=4428
std:: string s("the foo and the bar");
std:: println("{}", std::ranges::contains_subrange(s, "foo" ));
This won't work due to C-style string literal "foo" is actually a range of four characters: #include <iostream>
#include <string>
#include <string_view>
#include <ranges>
#include <print> // C++23 for std::println
int main() {
using namespace std::literals; // Enables the "sv" suffix
std::string s("the foo and the bar");
// "foo"sv creates a std::string_view of length 3.
// This will now print "true".
std::println("{}", std::ranges::contains_subrange(s, "foo"sv));
}std::string s("the foo and the bar");
// This is the simplest way and does what you expect.
// It will print "true".
std::println("{}", s.contains("foo"));
Oct 17, 2025
[C++][template] Double checked Stop technique
godbolt:
https://godbolt.org/z/Yenv16xzj
#include <functional>
#include <iostream>
#include <optional>
template <size_t I, typename F>
constexpr std::optional<int> ApplyIndexForOverflow(F f) {
return ApplyIndexForOverflow<I - 1>(f);
}
template <size_t I, typename F>
constexpr std::optional<int> ApplyIndexFor(F f) {
if (I == 0) {
return std::nullopt;
}
// double checked stop; otherwise introduced stack overflow
// from the compiler runtime due to has to instantiate
// unbounded template instance, like above `ApplyIndexForOverflow`
return ApplyIndexFor<(I == 0 ? 0 : I - 1 )>(f);
}
template <size_t I, typename F>
constexpr std::optional<int> ApplyIndexForConstexpr(F f) {
if constexpr(sizeof(F) == 1){
return I;
}
if constexpr(I - 1 == 0){
return std::nullopt;
} else {
return ApplyIndexForConstexpr<I-1>(f);
}
}
int main() {
auto run = []{};
ApplyIndexForOverflow<100>(run);
ApplyIndexFor<100>(run);
ApplyIndexForConstexpr<100>(run);
}
Oct 4, 2025
[alrotighm] trampoline pattern
#include <iostream>
#include <vector>
#include <numeric>
#include <variant>
#include <functional>
#include <utility>
// --- (Paste the Bounce, Step, and trampoline definitions from above here) ---
template<typename T>
struct Bounce;
template<typename T>
using Step = std::variant<T, Bounce<T>>;
template<typename T>
struct Bounce {
std::function<Step<T>()> thunk;
};
template<typename T>
T trampoline(Step<T> first_step) {
Step<T> current_step = std::move(first_step);
while (std::holds_alternative<Bounce<T>>(current_step)) {
current_step = std::get<Bounce<T>>(current_step).thunk();
}
return std::get<T>(current_step);
}
// --- (Paste the sum_trampolined function from above here) ---
Step<long> sum_trampolined(const std::vector<long>& data, size_t index, long current_sum) {
if (index == data.size()) {
return current_sum;
}
return Bounce<long>{
[=]() {
return sum_trampolined(data, index + 1, current_sum + data[index]);
}
};
}
int main() {
// This will now work without crashing!
std::vector<long> large_vec(200000, 1);
// To start the process, we create the very first step.
Step<long> first_step = sum_trampolined(large_vec, 0, 0);
// The trampoline function runs the computation to completion.
long total = trampoline(first_step);
std::cout << "Trampolined sum of large vector: " << total << std::endl;
std::cout << "The program finished successfully." << std::endl;
return 0;
}
Jul 29, 2025
[C++] Coroutine examples
- C++ coroutine has two scopes: caller scope, coroutine scope (if you familiar with Python yield/send, same concept but more subtle with object lifetime and more flexible. Or Golang goroutine/channel [stackful], or C++ fiber[stackful])
- Suspend suspends the coroutine and back to the caller, caller uses handler to resume the coroutine
- Underneath using JMP instead of CALL since it's stackless coroutine.
- Beware that even `void coroutine(const int& arg)` that has co_await/co_yield/co_return, when it suspends, the binding arg has been stack rewinded thus destroyed.
i.e.
If a coroutine has a parameter passed by reference, resuming the coroutine after the lifetime of the entity referred to by that parameter has ended is likely to result in undefined behavior.
The C++ core guidelines say not to use references at all. - Just avoid references" isn't comprehensive, e.g. we have span, string_view, even though they are not reference, can still facing the same dangling issue after co_await/co_yield/co_return.
Thus, a better contract is:
Pass by Value with Owning Types. - The way we ensure caller doesn't compile is by making Co non-moveable, and accepting it by value in the co_await implementation in the promise type.
This means the only way to await it is if you do so immediately in the same full expression as the function call, so that guaranteed copy elision can kick in.
Because temporaries aren't destroyed until that full expression has been evaluated, the lifetimes work out perfectly using the usual language rules around lifetimes.
Design TIP:
Eliminated the problem with references by just declaring an entire pattern of code illegal.Co<void> UseInt_Async(const int& x) { printf("%d\n", x); co_await DoSomething(); printf("%d\n", x); } k3::Co<void> UseInt_Async(const int&); // Works fine co_await UseInt_Async(17); // Compiler error due to k3::Co<void> is not moveable. k3::Co<void> co = UseInt_Async(17); co_await std::move(c); - What if we want to use above pattern?
Indirection, just like std::bindk3::Co<void> UseInt_Async(const int&); // Totally safe k3::Future<void> future{UseInt_Async, 17}; printf("I created the future!\n"); co_await std::move(future).Run(); - Co<void> has the interface concept of awaitable, as below.
- Fan out or run in sequence; the implementation is imaginable.
// Run all concurrently, finishing once all finish. k3::Co<void> FanOut(std::vector<k3::Future<void>> futures); // Run all concurrently, finishing when the first finishes. template <typename T> k3::Co<T> Race(std::vector<k3::Future<T>> futures);
co_await awaitable;
co_yieldstruct awaitable { bool await_ready() { return false; // false: suspended, true: not suspended} void await_suspend(std::coroutine_handle<> h) { // what to do when suspended} void await_resume() { // in await_suspend's coroutine_handle calling .resume() comes here. and after this resume to coroutine. } int await_resume() {return 42; // co_await returns value from here.} };
co_return
Stack:
So in coroutine, how do we avoid stack overflow?
c++ coroutine guarantees a tail call into the other coroutine, and then you can do it again on the way back out. There's no need to have a stack frame to return to; everything is a JUMP instruction.
This is the only guaranteed[as of c++20] tail call in the standard; it's actually kind of a unique mixing of abstraction levels.
Co<void> Foo();
Co<void> Bar() {
// Tail call into the body of Foo.
co_await Foo();
// Tail call back from the body of Foo.
[...]
}
This indicates that with coroutine embedded inside a coroutine all use the same stack; thusif there are multiple suspension, the stack can be overflown.
c9 solution:
class Event {
Co<void> Wait();
// If there is a waiter, it will start running concurrently
// on another thread. The calling thread continues on.
// Look at the co_wait example below.
void Notify();
};
We could resolve this by breaking the assumption that there is only one thread available. Instead we could resume the coroutine on a different thread, letting it run concurrently at the same time as the notifier.
Avoid executors in the coroutine library design:
Of course individual things you wait for might have an executor internally. Like if you wait for an RPC to finish, you'll probably be resumed on one of a team of threads reading RPC replies from the network.
// Hop to a specific executor. We need to run there because…
// look the co_await example below~
co_await Reschedule(my_executor);Cancellation in library design
- RPCs to stuck machines
- Request hedging (idempotent operations)
- Avoiding wasted work
- Timeouts
The callee exists only to serve the caller. It must stop promptly if the caller loses interest.
- the caller of a coroutine is always in control.
- If the caller no longer wants the callee to run, the callee should stop running.
- And it should do so promptly. Not after getting a response to its RPC. Not after a timeout expires. Immediately.
All it should get to do is the kind of thing you want to do before you release a lock or unwind after an exception. Ensure internal invariants are restored; that kind of thing. - coroutine's local automatic variable won't be destructed until it is resumed till the end of the coroutine. (while is is on the heap)
- If cancellation happens (which is to resume after coroutine suspended, and pass the cancellation bit to the coroutine_handler which be consumed by the awaitable, and the awaitable resume checks the bit to cancel the original code path.), the coroutine continues till the end.
Children should not out-live parent.
Prefer structured concurrency wherever possible.
Handler:
// task, aka handler, will be created through promise_type instance's get_return_object()
// and destructed once the coroutine function is returned to the caller.
// the destruct of task does not mean the promise_type instance is destructed, it still resides on the heap where
// coroutine is located.
struct task
{
struct promise_type
{
task get_return_object() { return {}; // Always called first when coroutine being called. }
std::suspend_never initial_suspend() { return {}; // Always called second when coroutine being called. }
std::suspend_never final_suspend() noexcept { return {}; / Always called last when coroutine is finished. (i.e. done) }
void return_void() { // called if return from coroutine with void.}
std::suspend_always yield_value(T value) noexcept
{
// used for co_yeild, once co_yeild is called, the yield value is installed here.
// and suspend afterwards due to return `std::suspend_always`
return {};
}
std::suspend_always return_value(T value) {
// used for co_return, once co_return is called with expr, the expr value is installed here.
// and suspend afterwards due to return `std::suspend_always`
return {};
}
void unhandled_exception() {}
};
task(std::coroutin_handler<promise_type> h) {
// store the coroutin_handler inside the task.
}
};
#include <iostream>
#include <coroutine>
// 1. The Promise Type
struct MyTaskPromise {
// The compiler calls this to get the return object.
// We return a 'MyTask' object, and in its constructor, we pass it the handle.
struct MyTask get_return_object();
std::suspend_never initial_suspend() noexcept { return {}; }
std::suspend_always final_suspend() noexcept { return {}; }
void return_void() noexcept {}
void unhandled_exception() noexcept {}
};
// 2. The Task Type
// This is the wrapper around the coroutine_handle.
struct MyTask {
using promise_type = MyTaskPromise;
std::coroutine_handle<MyTaskPromise> handle;
// The constructor takes the handle from the promise's get_return_object() call.
MyTask(std::coroutine_handle<MyTaskPromise> h) : handle(h) {}
};
// Now we can define get_return_object() because MyTask is defined.
MyTask MyTaskPromise::get_return_object() {
// This is the key line: we construct the MyTask object with the handle
// to the coroutine that owns this promise.
return MyTask{std::coroutine_handle<MyTaskPromise>::from_promise(*this)};
}
// 3. A simple awaitable to demonstrate suspension.
struct Awaitable {
bool await_ready() { return false; }
void await_resume() {}
// This await_suspend returns `true`, which suspends the coroutine
// and returns control to the caller (main).
bool await_suspend(std::coroutine_handle<>) noexcept {
std::cout << "-> Awaitable: Coroutine is suspending." << std::endl;
return true;
}
};
// 4. The Coroutine Function
MyTask MyCoroutine() {
std::cout << "Coroutine: Starting." << std::endl;
co_await Awaitable{}; // Coroutine suspends here.
std::cout << "Coroutine: Resumed and finishing." << std::endl;
co_return;
}
// 5. The Caller (main function)
int main() {
// 1. The call to MyCoroutine() returns a 'MyTask' object.
// This object contains the handle to the suspended coroutine.
MyTask task = MyCoroutine();
std::cout << "\nMain: Coroutine is suspended. I am the caller.\n" << std::endl;
// 2. We can now use the handle stored inside the 'task' object
// to resume the coroutine.
if (task.handle) {
std::cout << "Main: Resuming the suspended coroutine." << std::endl;
task.handle.resume();
}
std::cout << "\nMain: Coroutine has finished its execution." << std::endl;
// 3. Clean up the coroutine's memory.
if (task.handle) {
task.handle.destroy();
}
return 0;
}
co_yield Example:
#include <coroutine>
#include <iostream>
#include <optional>
template<std::movable T>
class Generator
{
public:
struct promise_type
{
Generator<T> get_return_object()
{
std::cout << "get_return_object()\n"; // -2; Generator created with pointer to the heap
return Generator{Handle::from_promise(*this)};
}
static std::suspend_always initial_suspend() noexcept
{
std::cout << "suspend_always initial_suspend()\n"; // -4 init. suspend. Go to the caller.
return {};
}
static std::suspend_always final_suspend() noexcept
{
std::cout << "suspend_always final_suspend()\n"; // -15 coroutine ends, call this and suspend.
// Back to the caller. Go to (13)
return {};
}
std::suspend_always yield_value(T value) noexcept
{
std::cout << "yield_value() : " << value << "\n"; // -9, suspend. Go to the caller.
current_value = std::move(value);
return {};
}
// Disallow co_await in generator coroutines.
void await_transform() = delete;
[[noreturn]]
static void unhandled_exception() { throw; }
std::optional<T> current_value;
};
using Handle = std::coroutine_handle<promise_type>;
explicit Generator(const Handle coroutine) :
m_coroutine{coroutine}
{
std::cout << "Generator constructor\n"; // -3
}
Generator() = default;
~Generator()
{
// make sure no double free through handler.destroy() while
// caller could have a copy of the handler.
if (m_coroutine && !m_coroutine.done())
m_coroutine.destroy();
std::cout << "Generator destructor\n";
}
Generator(const Generator&) = delete;
Generator& operator=(const Generator&) = delete;
Generator(Generator&& other) noexcept :
m_coroutine{other.m_coroutine}
{
std::cout << "Generator move constructor\n";
other.m_coroutine = {};
}
Generator& operator=(Generator&& other) noexcept
{
std::cout << "Generator assign operator=\n";
if (this != &other)
{
if (m_coroutine)
m_coroutine.destroy();
m_coroutine = other.m_coroutine;
other.m_coroutine = {};
}
return *this;
}
// Range-based for loop support.
class Iter
{
public:
void operator++()
{
std::cout << "Iter ++ currnet value: " << *m_coroutine.promise().current_value << "\n";
m_coroutine.resume(); // -11, resume from (9) yeild's suspend; JUMP to COROUTIN RIGHT AWAY!
// Following cout is not run until coroutin suspend again.
// -13, followed by (12) and after (9) yeild's suspend.
std::cout << "Iter ++ resumed currnet value: " << *m_coroutine.promise().current_value << "\n";
}
const T& operator*() const
{
// -10, caller print out the value.
std::cout << " Iter* return value: " << *m_coroutine.promise().current_value << "\n";
return *m_coroutine.promise().current_value;
}
bool operator==(std::default_sentinel_t) const
{
// -16, caller is calling this from coroutin's suspend.
std::cout << "Iter == called: !m_coroutine: " << (!m_coroutine) << " m_coroutine.done(): " << m_coroutine.done() << "\n";
return !m_coroutine || m_coroutine.done();
}
explicit Iter(const Handle coroutine) :
m_coroutine{coroutine}
{}
private:
Handle m_coroutine;
};
Iter begin()
{
std::cout << "Iter begin\n"; // -5, caller range for calls begin()
if (m_coroutine)
m_coroutine.resume(); // -6 (4) suspended resumed. Go to coroutine.
return Iter{m_coroutine};
}
std::default_sentinel_t end() { return {}; }
private:
Handle m_coroutine;
};
template<std::integral T>
Generator<T> range(T first, const T last)
{
// suspended right away since initial_suspend() returns std::suspend_always
// returns to the caller with Generator instance
std::cout << "Range\n"; // -7 from (6)
while (first < last){
std::cout << "Range first: " << first << "\n";
co_yield first++; // -8, go to yield_value(), install `first` value, first is + 1, and suspend.
// // -12, resume from (11) operator++()
std::cout << "Range after first++: " << first << "\n";
}
// -14, coroutine end, suspend_always final_suspend() called.
}
int main()
{
std::cout << "Start for loop\n"; // -1
// Generator is destructed only once due to range loop extends lifetime.
for (const char i : range(65, 67))
std::cout << i << "\n";
// -17, out of range for loop scope,
// range(65, 67) returned `Generator` destructs.
std::cout << "End for loop\n";
std::cout << '\n';
}
stdout:Start for loop
get_return_object()
Generator constructor
suspend_always initial_suspend()
Iter begin
Range
Range first: 65
yield_value() : 65
Iter == called: !m_coroutine: 0 m_coroutine.done(): 0
Iter* return value: 65
A
Iter ++ currnet value: 65
Range after first++: 66
Range first: 66
yield_value() : 66
Iter ++ resumed currnet value: 66
Iter == called: !m_coroutine: 0 m_coroutine.done(): 0
Iter* return value: 66
B
Iter ++ currnet value: 66
Range after first++: 67
suspend_always final_suspend()
Iter ++ resumed currnet value: 66
Iter == called: !m_coroutine: 0 m_coroutine.done(): 1
Generator destructor
End for loop
co_await Example:
#include <coroutine>
#include <iostream>
#include <stdexcept>
#include <thread>
auto switch_to_new_thread(std::jthread& out) {
std::cout << "switch_to_new_thread start\n";
struct awaitable {
std::jthread* p_out;
bool await_ready() {
std::cout << "await ready\n";
return false;
};
void await_suspend(std::coroutine_handle<> h) {
std::cout << "await_suspend\n";
std::jthread& out = *p_out;
if (out.joinable())
throw std::runtime_error("Output jthread parameter not empty");
out = std::jthread([h] {
std::cout << "calling handler.resume()\n";
h.resume();
});
// Potential undefined behavior: accessing potentially destroyed *this
// std::cout << "New thread ID: " << p_out->get_id() << '\n';
std::cout << "New thread ID: " << out.get_id() << '\n'; // this is OK
}
void await_resume() {
std::cout << "await_resume\n";
}
};
std::cout << "switch_to_new_thread about to return\n";
return awaitable{&out};
}
struct task {
struct promise_type {
task get_return_object() {
std::cout << "get_return_object()\n";
return {};
}
std::suspend_never initial_suspend() {
std::cout << "inital_suspend()\n";
return {};
}
std::suspend_never final_suspend() noexcept {
std::cout << "final_suspend()\n";
return {};
}
void return_void() {
std::cout << "return_void\n";
}
void unhandled_exception() {}
};
~task() {
std::cout << "task destruct\n";
}
};
task resuming_on_new_thread(std::jthread& out) {
std::cout << "Coroutine started on thread: " << std::this_thread::get_id() << '\n';
co_await switch_to_new_thread(out);
// awaiter destroyed here
std::cout << "Coroutine resumed on thread: " << std::this_thread::get_id() << '\n';
}
int main() {
std::jthread out;
std::cout << "start\n";
resuming_on_new_thread(out);
std::cout << "ending main()\n";
}
stdout:start
get_return_object()
inital_suspend()
Coroutine started on thread: 140248882116480
switch_to_new_thread start
switch_to_new_thread about to return
await ready
await_suspend
New thread ID: 140248877143744
task destruct
ending main()
calling handler.resume()
await_resume
Coroutine resumed on thread: 140248877143744
return_void
final_suspend()
Nov 6, 2024
[C++] include from /dev/stdin
When I was publishing Concurrent LRUCache which is used by my previous job at Linkedin, I used this feature provided by godbolt for demo, which is it could include headers through https protocol.
e.g: https://godbolt.org/z/Y6he8z9Gf
Turned out this feature can be done through #include "/dev/stdin"
#include "/dev/stdin"
int main(){
foo f{};
(void)(f);
}Aug 29, 2024
[C++] ExplicitArgumentBarrier trick to force argument type being deduced during the call.
template <int&... ExplicitArgumentBarrier, typename C>
constexpr auto deref_second_view(C&& map ABSL_ATTRIBUTE_LIFETIME_BOUND) {
return deref_second_view_t<std::remove_reference_t<C>>(map);
}Aug 16, 2024
[C++] friend function definition inside a type can be found only through ADL if without forward declaration.
void foo(){
auto f = &foo;
}
struct Test{
void foo(){
auto f = &Test::foo;
}
friend void bar(){
// auto f = &bar; // Use of undeclared identifier 'baz'
void bar();
auto f = &bar;
}
friend void baz(Test t){
// Find through ADL
baz(Test{});
// auto f = &baz; // Use of undeclared identifier 'baz'
}
};
int main()
{
}
Jul 28, 2024
[C++] tag pointer
Reference:
Storing data in pointers
#include <cstddef>
#include <cstdint>
#include <iostream>
enum ListType {
kNone,
kReady,
kDeleting,
};
static constexpr uintptr_t kListTypeMask = 0b11;
static constexpr uintptr_t kCleanPtrMask = ~kListTypeMask;
struct Tree;
struct Node{
void set_list_type(ListType list_type) {
uintptr_t t = static_cast<uintptr_t>(list_type);
uintptr_t v = ptr_and_list_type_ & kCleanPtrMask;
ptr_and_list_type_ = (v | t);
}
ListType list_type() const {
return static_cast<ListType>(ptr_and_list_type_ & kListTypeMask);
}
void set_prev_next_ptr(Node** p) {
uintptr_t t = ptr_and_list_type_ & kListTypeMask;
uintptr_t v = reinterpret_cast<uintptr_t>(p);
ptr_and_list_type_ = (v | t);
}
Node** prev_next_ptr() const {
return reinterpret_cast<Node**>(ptr_and_list_type_ & kCleanPtrMask);
}
Tree* tree_ = nullptr;
Node* parent_ = nullptr;
Node* next_ = nullptr;
uintptr_t ptr_and_list_type_ = 0;
};
struct Tree{
void MarkReady(Node* p) {
p->next_ = ready_;
p->set_prev_next_ptr(&ready_);
if (ready_ != nullptr) {
ready_->set_prev_next_ptr(&p->next_);
}
ready_ = p;
p->set_list_type(kReady);
}
int padding;
Node* ready_ = nullptr;
};
int main() {
// Print 8 since padding is type of int occupies 8 bytes.
std::cout << "offset of ready_: " <<
reinterpret_cast<void*>(&((Tree*)0)->ready_) << "\n";
Tree* tree = new Tree{};
std::cout << "tree: " << reinterpret_cast<void *>(&tree) << "\n";
std::cout << "tree offset of ready: " <<
reinterpret_cast<void *>(&tree->ready_) << "\n";
Node p;
p.tree_ = tree;
tree->MarkReady(&p);
std::cout << reinterpret_cast<void *>(p.prev_next_ptr()) << "\n";
}
Dec 31, 2023
[Cppcon 2023] Expressing Implementation Sameness and Similarity - Polymorphism in Modern C++, Daisy Hollman
Reference:
https://youtu.be/Fhw43xofyfo?si=PcMFEsqb7NRiCmyX
Two different kinds of sameness
Interface sameness
- Enables users to treat things the same way in their code(create their own sameness)
- Cannot be removed later
- You can't change your user's code(at least not easily)
- Once you allow users to treat things as the same, it's very hard to change that later
- When done well: low code coupling(the degree of interdependence between software modules)
Implementation sameness
- Enables readers to understand and use existing sameness of similarity
- Can be changed or removed at any point in the future when it stops being helpful
- When done well: high code cohesion(the degree to which the elements inside a module belong together)
Reusing customization points.
Mixin mixins.
template<class> struct CRTPMixin;
template<template <class> class Mixin, class Derived>
struct CRTPMixin<Mixin<Derived>> {
consteval auto& self() { return static_cast<Derived&>(*this); }
consteval auto const& self() const { return static_cast<Derived const&>(*this); }
};
template<typename Derived>
struct PrintableElementwise : CRTPMixin<PrintableElementwise<Derived>> {
void print() const {
auto const& e = self().elements();
std::apply([](auto const&... el) {
([&]{ cout << el << "\n"; }(), ...);
}, e);
}
};As stated by Sy Brand's C++23’s Deducing this can help with the above
https://devblogs.microsoft.com/cppblog/cpp23-deducing-this/static_cast<Derived const&>(*this);
struct PrintableElementwise {
void print(this auto const& self) {
auto const& e = self.elements();
std::apply([](auto const&... el) {
([&]{ count << el << "\n"; }(), ...);
}, e);
}
};
struct Foo : PrintableElementwise {
int a;
double b;
std::string s;
auto elements() const {
return std::forward_as_tuple(x, y, z);
}
};
Above has mixed interface sameness with implementation sameness; which causes error:auto items = std::vector<PrintableElementwise>{/*...*/};
for (auto* i : items) {
i->print(); // error if derived type has no memfn elements().
}
Qualifier forwarding
template<class T>
void foo(T&& t) {
bar(std::forward<T>(t));
}
equivalent to:
template<class T>
void foo(T& t) {
bar(t);
}
template<class T>
using not_forwarding_ref = T;
template<class T>
void foo(not_forwarding_ref<T>&& t) {
bar(std::move(t));
}Don't Repeat Yourself(DRY)
'Every piece of knowledge must have a single, unambiguous, authoritative representation within a system'Separating the ownership mechanism (typical pattern for a class template customization point)
From:
template<class Thing>
class OwingCollection {
private:
vector<unique_ptr<Thing>> things_;
protected:
void for_each(/* concept */ std::invocable<Thing const&> auto&& f) const { /*...*/};
public:
void insert(unique_ptr<Thing>) {};
unique_ptr<Thing> remove(unique_ptr<Thing>) {};
bool contains(unique_ptr<Thing>) const {};
void remove_if(invocable<Thing const&> auto&&) {};
};
To:template<class Thing, template<class...> class Owner = unique_ptr>
class OwingCollection {
private:
vector<Owner<Thing>> things_;
protected:
void for_each(/* concept */ std::invocable<Thing const&> auto&& f) const { /*...*/};
public:
void insert(Owner <Thing>) {};
Owner <Thing> remove(Owner <Thing>) {};
bool contains(Owner <Thing>) const {};
void remove_if(invocable<Thing const&> auto&&) {};
};STD's example of separable pattern
C++20 Concepts
template<class T>
requires requires(T&& t) { { t.clear() } -> convertible_to<bool>; }
void do_the_stuff(T&& t) { /*...*/ }
Those two types fit for above API. struct Container {
// returns true if the container was
// non-empty before the clear
bool clear();
};
struct Color {
// returns true if opacity == 0
bool clear();
};
Concepts does not differentiate namespace.More places having the concept of 'sameness'
- 'Normal' functions (C like)
- Macros and Code generation
- 'Gross' but sometimes better than repeating things.
- Customization Point Objects(CPOs)
- Type erasure
- constexpr functions
- 'Sameness' of compile-time and runtime implementations.
- Dependency injection (needs reflection)
- Aspect-oriented programming(needs reflection)
- Decoration(needs reflection)
Dec 12, 2023
[C++] std::unstable_unique implement
std::unstable_unique implement by Andrei Alexandrescu
#include <cassert>
#include <vector>
template <class iterator, class BinaryPredicate>
iterator unstable_unique(iterator first, iterator last, BinaryPredicate p) {
if (first == last || std::next(first) == last)
return last; // 0 or 1-element range
++first; // position first on the first unknown element (first element will stay by definition)
bool first_is_known_duplicate = false; // see below for description
for (; first < last; ++first) {
if (!first_is_known_duplicate) {
if (!p(*first, *std::prev(first))) {
continue;
}
}
// Here we know that `*first` is a dupe and should be replaced. Also the range is not empty.
assert(first < last);
for (--last;; --last) {
if (first == last)
return first; // just past the last unique element
assert(first < last);
if (!p(*last, *std::prev(last)))
break;
}
assert(!p(*first, *last));
// Here we know we're good to replace *first with *last.
// Complicating matter: if we do so, we "forget" whether *std::next(first) is a duplicate of *first.
// Maintain `first_is_known_duplicate` to keep track of that.
first_is_known_duplicate = p(*first, *std::next(first));
*first = std::move(*last);
}
return first;
}
template <class iterator>
iterator unstable_unique(iterator first, iterator last) {
return unstable_unique(first, last, [](auto& a, auto& b) { return a == b; });
}
int main() {
std::vector<int> v1;
auto new_end1 = unstable_unique(v1.begin(), v1.end());
assert(v1.end() == new_end1);
std::vector<int> v2 = { 1, 2, 3, 4, 5 };
auto new_end2 = unstable_unique(v2.begin(), v2.end());
// std::cout << new_end2 - v2.begin() << '\n';
assert(v2.end() == new_end2);
// std::copy(v2.begin(), new_end2, std::ostream_iterator<int>(std::cout, " "));
assert(std::vector<int>({ 1, 2, 3, 4, 5 }) == v2);
std::vector<int> v3 = { 1, 1, 2, 3, 4, 5 };
auto new_end3 = unstable_unique(v3.begin(), v3.end());
assert((v3.begin() + 5 == new_end3));
assert(std::vector<int>({ 1, 5, 2, 3, 4, 5 }) == v3);
std::vector<int> v4 = { 1, 1, 2, 2, 3, 3, 4, 4, 5, 5 };
auto new_end4 = unstable_unique(v4.begin(), v4.end());
assert((v4.begin() + 5 == new_end4));
assert(std::vector<int>({ 1, 5, 2, 4, 3, 3, 4, 4, 5, 5 }) == v4);
std::vector<int> v5 = { 1, 1, 1, 1, 1 };
auto new_end5 = unstable_unique(v5.begin(), v5.end());
assert((v5.begin() + 1 == new_end5));
assert(std::vector<int>({ 1, 1, 1, 1, 1 }) == v5);
std::vector<int> v6 = { 1, 1, 1, 1, 1, 2 };
auto new_end6 = unstable_unique(v6.begin(), v6.end());
assert((v6.begin() + 2 == new_end6));
assert(std::vector<int>({ 1, 2, 1, 1, 1, 2 }) == v6);
std::vector<int> v7 = { 1, 2, 2, 3, 3, 3, 4, 4, 4, 4 };
auto new_end7 = unstable_unique(v7.begin(), v7.end(), [](int a, int b) { return a == b; });
assert((v7.begin() + 4 == new_end7));
assert(std::vector<int>({ 1, 2, 4, 3, 3, 3, 4, 4, 4, 4 }) == v7);
};
Dec 7, 2023
[C++] ValueOr(type) type check.
#include <type_traits>
#include <utility>
template <typename T>
struct Foo {
template <typename U>
T ValueOr(U&&) {
static_assert(sizeof(U) > 1);
return {};
}
};
template <typename T>
struct ForwardedDeclval {
T operator()();
};
template <typename... Ts, typename F>
consteval void InstantiateForStaticAssertions(F f) {
static_assert(std::is_same_v<decltype(f(ForwardedDeclval<Ts>()...)), void>);
}
template <typename T>
struct FooWrapper {
template <typename U>
T ValueOr(U&& u) {
InstantiateForStaticAssertions<Foo<T>, decltype(u)>(
[](auto t, auto u) { t().ValueOr(u()); });
return {};
}
};
int main() {
FooWrapper<int> f;
f.ValueOr(1);
//f.ValueOr('5');
}
Oct 12, 2022
[C++23] std::unreachable / gcc::__builtin_unreachable
Jul 18, 2022
[C++23] std::unreachable
Reference:
(msvc) __assume
(gcc/clang) __builtin_unreachable()
(c++23) std::unreachable
[[noreturn]] inline void unreachable()
{
// Uses compiler specific extensions if possible.
// Even if no extension is used, undefined behavior is still raised by
// an empty function body and the noreturn attribute.
#ifdef __GNUC__ // GCC, Clang, ICC
__builtin_unreachable();
#elifdef _MSC_VER // MSVC
__assume(false);
#endif
}
Jul 16, 2022
[C++] std::index_sequence example
Reference:
std::apply
std::index_sequence
<utility>
index_sequence is quite handy with argument deduction.
#include <utility>
#include <array>
template <typename... Args>
struct CustomType { };
template <typename... Args>
CustomType<Args...> method(const Args&... args) { return {}; }
template <typename T, std::size_t N, typename VariadicFunc, std::size_t... I>
auto methodApply(
const std::array<T, N>& arr, VariadicFunc func, std::index_sequence<I...>) {
return func(arr[I]...);
}
template <typename T,
std::size_t N,
typename VariadicFunc,
typename Indices = std::make_index_sequence<N>>
auto methodArr(const std::array<T, N>& arr, VariadicFunc func) {
return methodApply(arr, func, Indices());
}
template <typename T, std::size_t N>
auto method(const std::array<T, N>& arr) {
return methodArr(arr, [](const auto&... args) { return method(args...); });
}
int main() {
method(1.0f, 2.0f, 3.0f);
std::array<float, 4> arr{1.0f, 2.0f, 3.0f};
method(arr);
}
Jul 8, 2022
[C++] inline std::forward impl
// An inline std::forward replacement.
#define FMT_FORWARD(...) static_cast<decltype(__VA_ARGS__)&&>(__VA_ARGS__)
Jul 6, 2022
[C++] count bool 'true' snippet
template <bool B = false> constexpr auto count() -> size_t { return B ? 1 : 0; }
template <bool B1, bool B2, bool... Tail> constexpr auto count() -> size_t {
return (B1 ? 1 : 0) + count<B2, Tail...>();
}
Jun 13, 2022
[C++][C++20] compile time heap allocate
For this reason, you can now use strings or vectors at compile time.
#include <vector>
#include <ranges>
#include <algorithm>
#include <numeric>
template<std::ranges::input_range T>
constexpr auto modifiedAvg(const T& rg) {
using elemType = std::ranges::range_value_t<T>;
// initialize compile-time vector with passed elements:
std::vector<elemType> v{std::ranges::begin(rg),
std::ranges::end(rg)};
// perform several modifications:
v.push_back(elemType{});
std::ranges::sort(v);
auto newEnd = std::unique(v.begin(), v.end());
// return average of modified vector:
auto sum = std::accumulate(v.begin(), newEnd, elemType{});
return sum / static_cast<double>(v.size());
}
// 注意,要用constexpr不然modifiedAvg為runtime.
constexpr auto avg = modifiedAvg(orig);
// use concept
// initialize compile-time vector with passed elements
template<std::ranges::input_range T>
consteval auto modifiedAvg(T rg) {
using elemType = std::ranges::range_value_t<T>;
std::vector<elemType> v{std::ranges::begin(rg), std::ranges::end(rg)};
}
However, note that we still cannot declare and initialize a vector at compile time that is usable at runtime:
int main() {
constexpr std::vector orig{0, 8, 15, 132, 4, 77}; // ERROR
}#include <vector>
constexpr auto returnVector() {
std::vector<int> v{0, 8, 15};
v.push_back(42);
return v;
}
constexpr auto returnVectorSize() {
constexpr auto coll = returnVector();
return coll.size();
}
int main() {
// constexpr auto coll = returnVector(); // ERROR
constexpr auto tmp = returnVectorSize();
}
#include <vector>
#include <ranges>
#include <algorithm>
#include <array>
template<std::ranges::input_range T>
consteval auto mergeValuesSz(T rg, auto... vals) {
// create compile-time vector:
std::vector<std::ranges::range_value_t<T>> v{
std::ranges::begin(rg), std::ranges::end(rg)};
(... , v.push_back(vals)); // and merge passed values
std::ranges::sort(v);
constexpr auto maxSz = rg.size() + sizeof...(vals);
std::array<std::ranges::range_value_t<T>, maxSz> arr{};
auto res = std::ranges::unique_copy(v, arr.begin());
return std::pair{arr, res.out - arr.begin()};
}constexpr Language Extensions
- You can now use heap memory at compile time.
- Runtime polymorphism is supported:
- You can now use virtual functions.
- You can now use dynamic_cast.
- You can now use typeid.
- You can have try-catch blocks now (but you are still not allowed to throw).
- You can now change the active member of a union.
- Note that you are still not allowed to use static in constexpr or consteval functions.
lamdba
template<typename... Args>
void foo(Args... args) {
// OK since C++20
auto l4 = [...args = std::move(args)] {
bar(args...); // OK
};
}
template<typename... Args>
void foo(Args... args) {
auto l4 = [&...fooArgs = args] {
bar(fooArgs...); // OK
};
}
new type:
char8_t
std::u8string
std::u8string_view
char8_t c = u8'@'; // character with UTF-8 encoding for character @
const char8_t* s = u8"K\u00F6ln"; // character sequence with UTF-8 encoding for Köln
Synchronized Output Streams:
https://en.cppreference.com/w/cpp/io/basic_osyncstream
#include <iostream>
#include <cmath>
#include <thread>
#include <syncstream>
void squareRoots(int num) {
for (int i = 0; i < num ; ++i) {
std::osyncstream coutSync{std::cout};
coutSync << "squareroot of " << i << " is "
<< std::sqrt(i) << '\n';
}
}
int main() {
std::jthread t1(squareRoots, 5);
std::jthread t2(squareRoots, 5);
std::jthread t3(squareRoots, 5);
}
For writing to file:
#include <fstream>
#include <cmath>
#include <thread>
#include <syncstream>
void squareRoots(std::ostream& strm, int num) {
std::osyncstream syncStrm{strm};
for (int i = 0; i < num ; ++i) {
syncStrm << "squareroot of " << i << " is "
<< std::sqrt(i) << '\n' << std::flush_emit;
}
}
int main() {
std::ofstream fs{"tmp.out"};
std::jthread t1(squareRoots, std::ref(fs), 5);
std::jthread t2(squareRoots, std::ref(fs), 5);
std::jthread t3(squareRoots, std::ref(fs), 5);
}
or:
#include <fstream>
#include <cmath>
#include <thread>
#include <syncstream>
void squareRoots(std::ostream& strm, int num) {
for (int i = 0; i < num ; ++i) {
strm << "squareroot of " << i << " is "
<< std::sqrt(i) << '\n' << std::flush_emit;
}
}
int main() {
std::ofstream fs{"tmp.out"};
std::osyncstream syncStrm1{fs};
std::jthread t1(squareRoots, std::ref(syncStrm1), 5);
std::osyncstream syncStrm2{fs};
std::jthread t2(squareRoots, std::ref(syncStrm2), 5);
std::osyncstream syncStrm3{fs};
std::jthread t3(squareRoots, std::ref(syncStrm3), 5);
}
Mar 24, 2012
[C++11][NOTE] Variadic Templates , Parameter Packs
template<typename… Types> // declare liststruct
Count; // walking template
template<typename T, typename… Rest> // walk list
struct Count<T, Rest…>
{
const static int value = Count<Rest…>::value +1;
};
template<> struct Count<> // recognize end of
{ // list
const static int value = 0;
};
auto count1 = Count<int, double, char>::value; // count1 = 3
auto count2 = Count<>::value; // count2 = 0