今日已更新 166 条资讯 | 累计 40611 条内容
关于我们

标签:#cpp

找到 36 篇相关文章

AI 资讯

BVH for Collision Detection: From AABB to Optimal Hierarchies

Table of Contents Why Broad-Phase Exists (and why naive O(N²) dies at 10k objects) Bounding Volume Hierarchy: The Data Structure That Scales Topology Choices: Binary vs. Multi-Branch, Pointer vs. Array Layout Construction Algorithms: From Naive to SAH-Optimal Traversal Strategies for Collision Queries The Static/Dynamic Dichotomy: Why One Tree Cannot Serve Two Masters The Dual-BVH Architecture Preview 1. Why Broad-Phase Exists The Pairwise Problem Every collision detection system faces the same fundamental challenge: given N objects, determine which pairs might be colliding so the expensive narrow-phase (SAT, GJK, EPA) only runs on plausible candidates. The naive approach tests every pair: // Naive O(N²) broad-phase — dies at ~10k objects std :: vector < CollisionPair > broadPhaseNaive ( const std :: vector < Object *>& objects ) { std :: vector < CollisionPair > pairs ; for ( size_t i = 0 ; i < objects . size (); ++ i ) { for ( size_t j = i + 1 ; j < objects . size (); ++ j ) { if ( aabbOverlap ( objects [ i ] -> aabb , objects [ j ] -> aabb )) { pairs . emplace_back ( objects [ i ], objects [ j ]); } } } return pairs ; } Complexity: O ( N ² ) AABB tests. At 60 Hz you have 16.67 ms/frame. At 120 Hz: 8.33 ms. Objects (N) Pairwise Tests @ 3 ns/test Frame Budget (60 Hz) 100 4,950 0.015 ms Trivial 1,000 499,500 1.5 ms Comfortable 10,000 49,995,000 150 ms 10x over budget 100,000 ~5x10^9 15,000 ms Impossible Cache Miss Catastrophe The pairwise loop doesn't just do too much work, it does it poorly . Each iteration accesses two random objects in memory. With 10k objects, you're thrashing L3 cache every frame. The BVH approach exploits spatial coherence: nearby objects in space are nearby in the tree, turning random access into sequential scans. The Real Job: Proving Separation KEY INSIGHT: Broad-phase is a rejection machine. Broad-phase is not about finding collisions. It's about proving separation as cheaply as possible. Every AABB overlap test that returns false is a vic

2026-09-07 原文 →
AI 资讯

Embedding a web UI into a native desktop application comes with a price

I thought embedding a web UI into a native desktop application would be the easy part. After all... macOS has WebKit. Linux has GTK WebKit. Windows has WebView2. One API per platform, smaller installers, native look & feel. Sounds perfect. Then reality arrived. macOS 🍎 Honestly, this was the easiest platform. System WebKit is there. It behaves consistently. No additional runtime. No installer surprises. Exactly what you'd expect from a platform component. 10/10 Linux 🐧 Things became... more interesting. GTK WebKit works, but suddenly packaging starts to matter. An AppImage built on one distribution may refuse to start on another because some required WebKitGTK library isn't available. Your application itself is perfectly fine. The user's system just doesn't happen to provide exactly the version your build expects. You quickly discover that "works on my machine" has many regional dialects. 7/10 Windows 🪟 This one surprised me the most. Unlike macOS, the web view isn't really just "there." Using WebView2 means depending on the Edge WebView runtime. If the runtime isn't installed, congratulations—you now need another installer. So your installer may install something whose purpose is to allow your application to display HTML. Not exactly the dependency story I was hoping for. 2/10 Meeting in the middle At some point I asked myself: Why am I spending time debugging operating-system packaging instead of building my application? So I tried CEF (Chromium Embedded Framework). Yes... The application becomes larger. Quite a bit larger. But in exchange: • Same rendering engine everywhere. • Same JavaScript engine everywhere. • Same debugging experience. • Same HTML/CSS behavior. • No Linux WebKit dependency lottery. • No separate WebView runtime installation on Windows. • One code path across all desktop platforms. Ironically, shipping your own browser turned out to be simpler than relying on the browser already "provided" by the operating system. It's one of those engineering

2026-09-07 原文 →
AI 资讯

Chip8 in C++

The reason I started this project is to learn more about C++, as we all know the best way of learning a programming language is to do projects, DO PROJECTS!! I used Austin Morlan's website to learn how to build it, it's quite good ( https://austinmorlan.com/posts/chip8_emulator/ ). I made some tweaks which I found to be better for me. I will not be posting the whole codebase here, it's too long. What I will be sharing are snippets of code, what I learned from it, and what I found amazing or funny (projects can have their own jokes). What is an Emulator ? An emulator is just hardware or software that lets the host system replicate conditions like the CPU, memory systems, clock cycles, etc., of the guest system whose functions/behaviour they want to simulate. It helps to bridge the architectural gap by making sure that each instruction code can be executed. In the case of Chip8, we have to simulate the hardware restrictions of the 1970s: a 64x32 screen, a 16-key keypad, timers, and a buzz sound. If you google Chip8, you will see that it is not actually a real physical device. It is a virtual machine/interpreter where you can interpret games (that was the intended purpose), like Pong or Space Invaders. It was a virtual language created in 1977 AD for a computer called COSMAC VIP. Building in C++ I wanted to get familiar with C++, that's why I am here. Building a Chip8 emulator in C++. Well, I learned you need headers, classes to define objects, the standard library, built-in objects like std::ifstream, std::streampos, and so on. I will explain some parts that left a mark in my memory. Header Files Well, before C++, I had only used a header file for an FPGA (Tang Nano 9K) project which I did. It made the LED blink in intervals. But now I understand more, such as how we create a blueprint of the class which we will be using to create objects in the future. Two modes: Public: The attributes and methods of the said class can be accessed by other functions or parts of the p

2026-09-07 原文 →
AI 资讯

How I built my own set of audio plugins with JUCE

A build log on ESP, six VST3 plugins written in C++ with JUCE 8 and shipped through a store I built myself. What the framework does for you, where it stops, and the one measurement that changed how I work. The line Six plugins, all JUCE 8, all VST3 plus standalone, all GPL v3, all downloadable from esp-plugin-store.vercel.app : Plugin What it is Basic Oscilator three oscillators on juce::dsp , the first thing I ever built, kept honestly VERTEX dynamic range compressor with a live transfer curve ESP-L1 brick-wall limiter with pre and post spectrum overlay MEGACRUSHER distortion, saturation and bit-crusher, three algorithms SPECTRUM real-time analyser, 2048-point FFT, spectrogram and 3D waterfall SYNTH/1 16-voice wavetable synth, unison, step sequencer, FX rack, interactive EQ That table is in the order I wrote them, and the order matters more than any single plugin. Each one starts roughly where the previous one ran out of framework. What juce::dsp actually hands you Basic Oscilator is three oscillators, three LFOs, a bit-crusher and a master gain. Almost all of it is the juce::dsp module doing the work: juce :: dsp :: ProcessSpec spec ; spec . maximumBlockSize = ( juce :: uint32 ) samplesPerBlock ; spec . sampleRate = sampleRate ; spec . numChannels = ( juce :: uint32 ) getTotalNumOutputChannels (); for ( int i = 0 ; i < 3 ; ++ i ) { oscillators [ i ]. prepare ( spec ); lfos [ i ]. prepare ( spec ); lfos [ i ]. initialise ([]( float x ) { return std :: sin ( x ); }); } masterGain . prepare ( spec ); That is the whole contract of the module. Prepare everything with one ProcessSpec , wrap your buffer in an AudioBlock , hand it to a processor as a context: juce :: dsp :: AudioBlock < float > block { tempBuffer }; oscillators [ i ]. process ( juce :: dsp :: ProcessContextReplacing < float > ( block )); juce::dsp::Oscillator takes its waveform as a lambda, so the three waves are three one-liners: case 0 : osc . initialise ([]( float x ) { return std :: sin ( x ); }); //

2026-09-05 原文 →
AI 资讯

From Arduino to ESP-IDF: The architecture behind my digital "Swiss Army Knife"

1. Why build another multi-tool? How many of you have often found yourselves wanting to buy a Flipper Zero? I thought about it many times, but there were always problems holding me back: stock is often limited, the price tag is quite high, and above all, you miss out on the thrill of building such a powerful tool literally from scratch. From these observations, my project was born: designing and developing a low-level "Swiss Army Knife". It all started a few months ago. I was thinking about buying an M5Stick S3 after watching some videos online where people spoke very highly of it, especially for one major detail: unlike the Flipper, it has Wi-Fi and Bluetooth modules already built-in. Digging deeper, I quickly realized the advantages of the ESP32-S3 over the classic Arduino. The key features that convinced me were: Dual-core processor: It opens the door to serious features, like managing firmware tasks separately. More RAM: It allows integrating very complex external libraries (like heavy graphical interfaces) without killing performance. Native USB HID: It allows emulating peripherals like keyboards or mice natively and quickly. So, the hardware was decided. But why build a multi-tool? The main reason is to explore and understand the technical background of as many tools as possible. Lately, I feel there is a tendency to overlook the ingenuity of the mechanisms operating right in front of our eyes. We prefer having a ready-made tool, usable perhaps without even knowing the basics of computer science. I wanted to go in the opposite direction and understand exactly how these things work at the code level. 2. Fluid Graphics and Multitasking: How not to blow up an ESP32 A major problem when rendering a graphical interface on a microcontroller is that the CPU has to calculate and send every single pixel. Since this is a time-consuming operation, the entire device gets blocked until the whole interface is completely redrawn. In a multi-tool, if the ESP32 is stuck drawin

2026-09-01 原文 →
AI 资讯

Building a High-Performance Robot Communication System with DDS

Building a High-Performance Robot Communication System with DDS Modern robots may have dozens of processes distributed across CPUs, edge computers, and embedded devices. ROS 2 uses DDS (Data Distribution Service) as its underlying communication technology. Understanding DDS helps you design robot systems that remain responsive as message traffic grows. The Communication Model Instead of connecting every process directly: Camera ---> Perception LiDAR ---> Perception IMU ---> Localization | v Planning | v Control ROS 2 nodes communicate through DDS topics and discovery. A simplified model is: Publisher | v DDS DataWriter | v Topic | v DDS DataReader | v Subscriber Why DDS Is Useful for Robotics DDS provides mechanisms for: Discovery Reliability Durability Deadline management History Resource limits Data delivery policies These features are important because different robot data has different requirements. A camera stream may prioritize low latency. A configuration message may prioritize reliability. High-Performance Design Avoid treating every topic identically. For example: Data Typical Priority Camera frames Low latency LiDAR scans High throughput IMU Low latency Robot commands Reliability Configuration Reliability + durability Diagnostics Reliability Reduce Copying Large sensor messages can consume substantial CPU and memory bandwidth. Good practices include: Avoid unnecessary serialization/deserialization. Reuse buffers where possible. Keep image resolution appropriate for the workload. Compress only when bandwidth savings justify CPU cost. Separate high-rate sensor topics from low-rate metadata. Separate Data Paths A useful architecture is: +--> Vision Camera ----------+ | LiDAR -----------+--> Perception --> Planning --> Control | IMU -------------+ Diagnostics ---------------------> Monitoring Configuration ------------------> Lifecycle Manager Not all traffic needs the same QoS or processing path. Measuring Performance Do not optimize based on intuition alone.

2026-09-01 原文 →
AI 资讯

Mutation Testing as a Merge Gate for Agent-Written Tests

An agent patch that passes its own tests is a baseline, not a verdict. The same model wrote the code and the tests, so both share the same blind spots. Mutation testing scores the tests themselves: inject a fault, run the suite, and see whether it notices. In practice, the first mutant often survives. Previous rounds on this account established three gates before merge: property checks, fixtures, and a freeze on flaky tests. This round adds a fourth gate that runs after the suite is green. It answers a different question — not "does the patch work?" but "would the tests catch it if it didn't?" Why green tests from an agent are weak evidence Code coverage measures execution, not detection. A test can execute a line and still miss the bug on it. A suite that only checks is_even(2) and is_even(4) runs both lines, passes both assertions, and stays blind to a mutation that flips == to != . Agents produce this shape of test by default. They follow the happy path, mirror the implementation, and rarely probe boundaries. The result is a suite that is green, fast, and weak for regression. Mutation testing converts that intuition into a number. For each small fault, rebuild and rerun. If the tests fail, the mutant is killed. If they pass, it survived — and you found a hole in the suite, not in the code. A minimal harness The harness below applies one mutation at a time to the implementation file, compiles it together with an unchanged test file, runs the resulting binary, and records the outcome. It is deliberately small: regex-based, two files, no dependencies beyond a compiler. #!/usr/bin/env python3 # mutate.py — score a test binary against source mutations. import re import subprocess import sys import tempfile from pathlib import Path MUTATIONS = [ ( " eq_to_neq " , r " == " , " != " ), ( " lt_to_le " , r " < " , " <= " ), ( " add_to_sub " , r " \+ " , " - " ), ( " zero_to_one " , r " return 0; " , " return 1; " ), ] def mutate_once ( src : str , pattern : str , replaceme

2026-08-27 原文 →
AI 资讯

The Agent's Tests Passed. Mutation Testing Showed 2 of 4 Faults Survived.

The agent patch passed the gates I ran on it. Its unit tests were green, fixtures matched, nothing was flaky. Then I seeded four faults into the implementation, one at a time. Two survived. That gap is what this article is about. A green suite is a claim, not a measurement. Mutation testing turns it into a measurement: introduce a fault, run the suite, and see whether the suite notices. I now run this loop before merging any agent-written patch, and the whole thing costs a few rebuilds. Why green tests lie A passing test proves one thing only: the test and the implementation agree on the inputs the test exercised. When an agent writes both the patch and the tests, the tests inherit the patch's assumptions. If the implementation encodes a wrong assumption, the test encodes the same one. The suite is green because it is blind, not because the code is right. The patch in this article came from a free model on MonkeyCode's free model access. Disclosure: This article was prepared as part of MonkeyCode's product outreach. The model wrote a bounded queue and a test file. The test file was not wrong. It was blind in exactly the place the implementation was wrong. The method: five steps Mutation testing is easy to describe and awkward to skip: Freeze flaky tests first. A flaky test fails at random, so it makes every mutation look like a kill. The signal is garbage. This is the flaky freeze from the gates post; without it, the numbers mean nothing. Select the functions the patch touched. Mutating untouched code measures someone else's tests. Generate mutations. Each mutation is one small fault: drop a modulo, flip a comparison, change an increment. Run the suite against each mutation. Rebuild, run, record. Gate on the kill rate. A surviving mutation means the suite cannot detect that fault class. Send the patch back with the survivor list as evidence. The artifact A minimal bounded queue, the agent's test, and a small Python driver. The queue: // bounded_queue.h #pragma once

2026-08-26 原文 →
AI 资讯

Building a Modular C++ Static Library: Clean Architecture, Encapsulation, and Safe Input Handling

As C++ codebases scale, housing utility routines, state management, and primary execution logic inside a single main.cpp file inevitably leads to technical debt. Code duplication increases, compilation times degrade, and testing isolated features becomes virtually impossible. Modular architecture solves this problem by enforcing a strict separation of concerns. By decoupling function declarations from their definitions and compiling utility modules into reusable static libraries, developers can achieve clean abstraction boundaries, simplify unit testing, and eliminate memory corruption vulnerabilities associated with unvalidated inputs. In this tutorial, you will learn how to build a production-grade C++ utility module from scratch, complete with boundary guards and static compilation. Prerequisites Before diving in, ensure you have: A modern C++ compiler supporting C++17 or higher (GCC, Clang, or MSVC). Basic familiarity with header files ( .h ) and translation units ( .cpp ). A Code Editor or IDE such as Visual Studio Code or Visual Studio . Project Structure To keep boundaries clean, we structure our workspace by isolating public headers from implementation units: text ModularCppLib/ ├── include/ │ ├── ArrayUtils.h │ └── ValidationUtils.h ├── src/ │ ├── ArrayUtils.cpp │ └── ValidationUtils.cpp ├── main.cpp └── README.md Phase 1: Structural Abstraction and Memory-Safe API Design Separating Interfaces from Translation Units In production C++ engineering, headers ( .h ) serve as explicit architectural contracts. They declare what operations are available without leaking how those operations are executed. All utility routines are scoped inside the explicit CoreUtils namespace to prevent global namespace pollution: namespace CoreUtils { // Contract: Accepts array pointer and length, // returns calculated mean safely double CalculateAverage ( const int * arr , std :: size_t size ); // Formats and prints array content void PrintArray ( const int * arr , std :: size_t si

2026-08-24 原文 →
AI 资讯

Shipping Stock CLIs as Subprocess Instead of Static-Linking SDKs

I'm building yyzTools, which bundles 9 third-party engines (OpenSSL, FFmpeg, ImageMagick, pdfcpu, Aria2, 7-Zip, RapidOCR, Everything...). I chose to spawn them as subprocesses rather than static-link their SDKs. Here's why—and the cost. The conventional approach When your app needs OpenSSL crypto, FFmpeg video processing, ImageMagick image ops—you reach for the SDK. Link libssl, link libav*, link libMagick. One binary, no external deps, fast function calls. It's the textbook answer. I did the opposite. yyzTools ships the stock CLI binaries (openssl.exe, ffmpeg.exe, magick.exe, pdfcpu, aria2c, 7z) and spawns them as subprocesses. The C++ layer is a thin loop: build args → CreateProcess → read stdout → wrap as JSON → return. It doesn't know what -gravity southeast or sm4-cbc means. It just passes the algorithm name through. Why I went this way Upgrades without recompiling This is the big one for a desktop app. OpenSSL ships a CVE, or adds sm2/sm3/sm4 support in 3.x. If you've static-linked, you recompile the whole app, run full regression, re-release, and every user reinstalls. With the subprocess model, I drop in a new openssl.exe. Zero C++ changes. The update is a few-MB delta, not a full reinstall. For a product where users won't tolerate reinstalling for a library bump, this is the deciding factor. No symbol conflicts OpenSSL, zlib, libpng—multiple libraries want to own these symbols. Static linking them all into one binary is a recipe for "which inflate did I just call?" With subprocess CLIs, each tool brings its own dependencies in its own process. No conflict. Transparent supply chain openssl version, ffmpeg -version—auditing which version of each tool is live is trivial. It's an independent binary. Far easier than digging symbols out of a statically-linked blob. Free crash isolation If ffmpeg.exe misbehaves, it exits non-zero and my host wraps that as an error. My main process keeps running. A static-linked bug can take down the whole app. The process boundary

2026-08-23 原文 →
开发者

Fixing a Snapcraft Build that had been Broken for Two Years

As I stated in my introduction , the first piece of work I did on Packet Sender was fix the Snapcraft build. What is Snapcraft? It has been a while since I've worked with Ubuntu, so I wasn't up to speed with how Ubuntu was now doing things like package management. As I understand it, apt and/or apt-get is still a thing, but Snapcraft is the new shiny. Snapcraft is more than just an app repository, though. It's a build system, dependency manager and a packager. Ergo, the vertical integration means you don’t have to keep multiple tools in sync. The Problem When building Packet Sender with Snapcraft, the product would build, link and run. But as soon as you ran it, it would crash with the following error message: /snap/packetsender/49/usr/local/bin/packetsender: error while loading shared libraries: libpxbackend-1.0.so: cannot open shared object file: No such file or directory .dll(s) and .so(s) I grew up in the '90s. This meant that, unfortunately, I grew up in the age of Microsoft, meaning I started on PCs. While I remember Windows 3.1's UI, I didn't really start using computers until Windows 95. My first machine was a Windows 98 box. I tell you this so you know that I grew up knowing what a .dll is because I grew up using Windows. Simply put, a .dll is a shared library . The idea was that instead of having to compile common libraries into executables and thus bloat the size of executables and the amount of memory they needed, vendors could deliver a shared library that would be loaded into memory once and could be called at run time by any running process that needed them. Of course, Microsoft being Microsoft, this was poorly engineered . .so s 1 are the same idea but on *nix. Thankfully, they had time to learn from Microsoft's mistakes. What does the error message mean? Simply put, what the error message was trying to tell us was that when the code was compiled, we assumed the .so files would be in a given place, but they weren't. One common way to fix this is with

2026-08-22 原文 →
AI 资讯

Case Study: A Free Model Wrote a C++ Tree Hasher. The Reference Oracle Found Three Bugs.

Conclusion first: a free model drafted a working C++17 directory hasher in one pass. The draft compiled, ran, and was still wrong. A differential test against standard system tools found three real bugs before the tool ever touched a production cache. Generation was the cheap part. Verification was the deliverable. Background I needed a deterministic hash of a directory tree. The use case was cache invalidation for a small build pipeline: if any file content, name, or symlink target changes, the cache key must change. If nothing changes, the key must stay identical across machines and across checkouts. Hand-writing the tool is maybe 200 lines of std::filesystem code. The happy path is easy. The risk lives in ordering, symlinks, and metadata leaking into the hash. I turned the task into an experiment. MonkeyCode's free model access and free server option meant the model ran on a remote server while I kept verification on my laptop. Disclosure: This article was prepared as part of MonkeyCode's product outreach. The plan: let the model write the first version, then prove or disprove it against a reference oracle. The Contract The goal was not "a tool that compiles." The goal was a tool that matches a reference implementation on every input I could generate. I wrote the contract in three sentences: Same tree → same hash, on any machine. Different content, name, or symlink target → different hash. File metadata (mtime, inode) must not affect the hash. Implementation Step 1: the prompt. I gave the model the contract, the C++17 standard, and one constraint: a single file with no dependencies beyond the standard library. Step 2: the draft. The model returned one .cpp file in a single response. It compiled on the first try. That is the exact moment where most workflows stop. This one did not. Step 3: the reference oracle. Instead of reviewing the code line by line, I built a harness that compares the tool against a shell pipeline: find " $tree " -printf '%P\0' | sort -z | wh

2026-08-20 原文 →
AI 资讯

Why Your Generated Tone Clicks, and How an Envelope Fixes It

If you have generated a pure tone in code and played it back, you may have noticed a small click at the start, the end, or both. The tone itself is clean, but the edges are not. That click is not a bug in your sine wave. It is a real and well understood artifact, and the fix is a technique you will reuse in every sound you ever synthesize: an envelope. This piece builds directly on generating a basic tone from scratch . We take a tone that clicks, look at the actual sample values to see why, and apply an envelope to smooth it. Everything is plain C++ with no libraries, and every number here is captured from a real run of the code. Where the click comes from A tone is a list of samples tracing a sine wave. A speaker turns those samples into sound by physically moving: the sample value sets the position of the speaker cone at each instant, where 0 is its resting position and larger values push it further forward or pull it back. Playing the tone moves the cone in and out 44,100 times a second to recreate the wave. When playback starts, the cone is at rest, at position 0. But the first sample of the tone is usually not 0. It is wherever the wave happens to be at that instant, and if that value is far from zero, the cone has to move from rest to that position in a single sample step, about 22 microseconds at this sample rate. That near instant movement is the click. A cone moving gradually pushes the air smoothly and produces a smooth sound. A cone forced to a distant position in one sample makes a sharp, abrupt movement of the air, which your ear hears as a click or pop. You can see it directly in the numbers. Here are the first six samples of a plain 440 Hz tone at half amplitude: n=0 raw=0 n=1 raw=1026 n=2 raw=2048 n=3 raw=3063 n=4 raw=4065 n=5 raw=5051 The wave leaves zero and climbs fast. Between the silence before playback and sample 1, the signal jumps by 1026 in one step. The same thing happens at the end: if the tone stops while the wave is partway through a cy

2026-08-17 原文 →
AI 资讯

What Building a C++ Benchmarking Suite Taught Me About "Simple" Data Structures

We all know the Big-O complexity of basic data structures. Arrays are O(n) for search. Hash maps are O(1). Linked lists are... well, complicated. But when I set out to build hashbrowns — a C++17 benchmarking suite comparing arrays, linked lists, and hash maps — I discovered that theory and practice are very different beasts. Here's what I learned building this project from scratch, and why you should probably benchmark before you optimize. 🎯 The Goal Was Simple (Ha!) I wanted a clean, educational project that would: Implement dynamic arrays, linked lists, and hash maps from scratch Benchmark insert, search, and remove operations Find the "crossover points" where one structure beats another Export everything to CSV for analysis Sounds straightforward, right? Four months later, I had written a custom memory tracker, implemented multiple hash map strategies, added statistical bootstrapping for confidence intervals, and learned more about CPU caches than I ever wanted to know. 📚 Lesson 1: Polymorphism Has a Price (But It's Worth It) My first architectural decision was creating a common DataStructure interface: class DataStructure { public: virtual void insert ( int key , const std :: string & value ) = 0 ; virtual bool search ( int key , std :: string & value ) const = 0 ; virtual bool remove ( int key ) = 0 ; virtual size_t memory_usage () const = 0 ; virtual std :: string type_name () const = 0 ; // ... }; This made benchmarking elegant — I could write generic code that tested any data structure: for ( auto & structure : structures ) { timer . start (); structure -> insert ( key , value ); timer . stop (); } But virtual function calls have overhead. In tight loops, that vtable lookup adds up. I spent a whole weekend convinced my hash map was slower than expected... until I realized I was measuring the cost of polymorphism, not the data structure itself. The fix? I kept the clean interface for the benchmarking harness but used templates internally where performance-cri

2026-08-14 原文 →
AI 资讯

Design Notes for a Deterministic C++ Simulation Framework

“Same inputs, same result” sounds like a simple requirement. In a multithreaded simulation, it is an architectural constraint that touches data layout, scheduling, physics, randomness, floating-point behavior, serialization, and debugging. Determinism is valuable for replays, lockstep networking, regression tests, and reproducing hard failures. It does not happen automatically. Define the determinism boundary Start by stating what must match. Do two runs on the same executable and machine need identical results? Across different compilers? Across CPU architectures? Across operating systems? Those are increasingly difficult guarantees. A framework should document the supported boundary rather than using “deterministic” as a universal adjective. Control time Do not feed variable wall-clock deltas directly into a deterministic simulation. Use a fixed simulation step and decide how the renderer catches up or interpolates. Record inputs by simulation tick. If the system pauses or falls behind, handle that condition explicitly instead of silently changing the rules. Make randomness replayable Every pseudorandom decision needs a known generator, seed, and consumption order. A global generator shared by many systems is fragile because adding one random call in an unrelated feature shifts the sequence everywhere. Prefer scoped streams or deterministic derivation by system, entity, and tick where appropriate. Record seeds in test and replay artifacts. Schedule parallel work deliberately Multithreading introduces nondeterministic execution order. If two jobs write shared state, results may depend on timing even when data races are technically avoided. A robust job graph should make read and write sets visible, separate independent phases, and define deterministic merge or reduction rules. Avoid relying on thread completion order. Parallelize work whose outputs can be combined predictably. Keep entity iteration stable Entity-component systems often use dense arrays and swap-rem

2026-08-14 原文 →
AI 资讯

# Why I’m Rewriting a PHP Extension in C23, Not C++

I forked the DataStax Cassandra driver when it stopped compiling on PHP 8 and most of its maintainers had already moved on. My first instinct was to write the new parts in C++. I built a Zend wrapper class, used RAII throughout, and put smart pointers around zval s—the whole modern setup. It introduced memory bugs that took me days to track down, and I did not get a meaningful benefit in return. So the driver is being rewritten in C23. I want to explain why, because “just use C++; it’s safer” is the reflexive answer. For a PHP extension, I no longer think it is the right one. This is not an argument that C++ is a bad language. In an application where I own the allocator, error model, and object lifetimes, std::vector and std::unique_ptr earn their keep. A PHP extension is different: the Zend Engine owns those rules, and its rules are written in C. The problem is not that C++ cannot call the Zend API. Plenty of extensions do. The problem is impedance: each abstraction has to be taught PHP’s lifetime rules, and the teaching code can become more complicated than the work it was meant to simplify. These are the four places where that cost me real debugging time. PHP owns the allocator PHP has its own memory manager. Request-scoped memory is allocated with functions such as emalloc , ecalloc , and safe_emalloc , then released with efree . Zend tracks that memory and normally reclaims what remains at request shutdown. Persistent allocations use a separate API because they have a different lifetime. Plain malloc and free —and therefore ordinary new and delete —sit outside that request-memory model. The moment I put a std::vector<zval> in an extension, its backing storage uses the C++ allocator unless I replace it. The obvious fix is a custom allocator: template < class T > struct PhpAllocator { using value_type = T ; template < class U > PhpAllocator ( const PhpAllocator < U >& ) noexcept {} PhpAllocator () noexcept = default ; [[ nodiscard ]] T * allocate ( std :: size_t

2026-08-08 原文 →
AI 资讯

ESP32 HTTP Client Sem Dores de Cabeça: Consuma REST APIs com Zero Alocação de Memória

Consumindo REST APIs no ESP32 sem Estourar a Memória: Conheça o ESP32-HTTP-Client Se você já desenvolveu projetos IoT no ESP32 que se comunicam com APIs REST (seja para enviar dados de sensores para a nuvem, consultar status de serviços ou integrar com Firebase e AWS), provavelmente já enfrentou um destes problemas clássicos: Fragmentação e estouro de heap: O combo padrão HTTPClient + ArduinoJson precisa carregar todo o payload HTTP na RAM como String antes de desserializar o JSON. Em payloads médios ou grandes, isso gera Out of Memory ou travamentos intermitentes. Lentidão em requisições consecutivas: O HTTPClient padrão refaz o handshake TLS/TCP repetidamente, adicionando centenas de milissegundos a cada chamada. Código verboso e boilerplate excessivo: Mais de 15 a 20 linhas de código para instanciar clientes, extrair buffers, checar erros e navegar em nós JSON. Para resolver esses gargalos de forma elegante e moderna, foi criada a biblioteca ESP32-HTTP-Client . O que é o ESP32-HTTP-Client? O ESP32-HTTP-Client é um cliente HTTP/REST moderno, fluente e orientado a objetos para ESP32, projetado especificamente para sistemas embarcados de alta eficiência. Em vez de "fazer download da resposta, guardar na memória e depois processar", ele utiliza Direct Memory Binding (injeção direta) e Stream Parsing : os dados do JSON são lidos diretamente do stream da rede e injetados direto nas suas variáveis ou struct s em C++, sem armazenar o payload inteiro na RAM . // Uma linha. Zero strings intermediárias. Injeção direta em memória. client . get ( "/sensor" ). getBody ( "temperature" , & myFloatVariable ); Benchmark: ESP32-HTTP-Client vs Abordagem Tradicional Em testes controlados com 100 requisições HTTP consecutivas contendo payloads JSON (usando o endpoint /users do JSONPlaceholder), os resultados comprovam a economia de recursos: Métrica / Recurso HTTPClient + ArduinoJson (Padrão) ESP32-HTTP-Client Diferencial Heap alocado por requisição ~58.2 KB ~0.0 KB (15 bytes) ~99.9%

2026-08-07 原文 →
开发者

Fast & Lightweight Online CRC Calculator

Hi everyone, I built a simple, fast, and lightweight online CRC calculator tool for embedded systems and developers. URL: https://crc-calc.com Features: Supports standard CRC polynomials (CRC-8, CRC-16, CRC-32, etc.) Custom polynomial & bit reflection settings No signup required I'd love to hear your feedback or suggestions!

2026-07-29 原文 →
AI 资讯

Ctrl+S said "Saved." The file was 0 bytes.

This is a submission for DEV's Summer Bug Smash: Smash Stories powered by Sentry . Written with the help of AI (Claude). The bug, the fix, the validation setup, and every claim below are mine, and were verified against the real codebase and a real full disk. The report Someone lost a Magic: The Gathering decklist. They were playing on Cockatrice — the open-source MTG client — with their decks on a drive that had quietly filled up while Oracle pushed an update in the background. They added a card, hit Ctrl+S, and Cockatrice said it saved. The debug log agreed: [2026-05-28 22:31:42.031 I] Saved deck to "G:/cockatrice300/data/decks/edh-b2-gitrog-reanimate.cod" with format 1 - true - true . Success. The file was 0 bytes. The deck was gone. That was issue #6952 , filed by Mekkiss. The steps to reproduce are four lines long and completely damning: Have a full disk. Open a deck on the full disk Add one card to it Save the deck (ctrl+s) Observe that the deck is now a 0 byte file. Three ways to be wrong at once The save path lived in DeckLoader::saveToFile() . Stripped down, it looked like this: QFile file ( fileName ); if ( ! file . open ( QIODevice :: WriteOnly | QIODevice :: Text )) { qCWarning ( DeckLoaderLog ) << "Could not create or open file:" << fileName ; return std :: nullopt ; } bool success = false ; switch ( fmt ) { /* ... saveToFile_Native / saveToFile_Plain ... */ } file . flush (); file . close (); qCInfo ( DeckLoaderLog ) << "Saved deck to " << fileName << "with format" << fmt << "-" << success ; There are three independent failures stacked on top of each other here, and you need all three to lose data: 1. WriteOnly truncates on open. The instant open() succeeds, the existing deck is 0 bytes. Not after a successful write — at open time . The old deck is already destroyed before a single byte of the new one is written. On a full disk, open() still succeeds: truncating a file doesn't need free space. It frees space. 2. The serializers always returned true . sa

2026-07-26 原文 →
AI 资讯

🚀 Mastering OOP for Interviews : Understanding Abstraction from First Principles (C++)

Series: Master OOP for Software Engineering Interviews Introduction Ask ten beginner developers: "What is abstraction?" Most answers sound like this: "Abstraction is the process of hiding implementation details and showing only essential information." Technically, that's correct. But if I ask the next question: "Why was abstraction invented?" or "Can you explain abstraction using an Inventory Management System?" or "How is abstraction different from encapsulation?" many candidates struggle. That's because they memorized the definition instead of understanding the idea behind it. In this article, we'll learn abstraction the way experienced software engineers think about it—not by memorizing definitions, but by understanding why it exists, what problem it solves, and how it appears in every modern software system. 🎯 Learning Goals After reading this article, you should be able to: Explain abstraction without memorizing a textbook definition. Understand why abstraction exists. Identify abstraction in everyday life. Recognize abstraction in software systems. Confidently answer beginner interview questions. Build a strong mental model that makes future OOP concepts easier. Before We Learn Abstraction... Let's ask an important question. Why do programming languages even provide OOP? Imagine writing software for an e-commerce company. The system contains: Products Customers Orders Warehouses Payments Delivery Partners Notifications Discounts Reviews Thousands of features. If every developer had to understand every implementation detail before writing code, software development would become impossible. We need a way to reduce complexity. That solution is called abstraction. The Problem Abstraction Solves Imagine buying a new car. You sit inside. You: Press the accelerator. Turn the steering wheel. Shift gears. Press the brake. Simple. But underneath the hood, hundreds of complex operations happen every second. The engine burns fuel. The pistons move. The gearbox changes tor

2026-07-15 原文 →