Articles & Books

Reflecting JSON into C++ Objects -- Barry Revzin

C++26 marks a transformative milestone with the adoption of full compile-time reflection, enabling powerful new metaprogramming capabilities. In this post, we’ll explore how reflection lets you turn a JSON file directly into a fully-typed C++ object — all at compile time.

Reflecting JSON into C++ Objects

by Barry Revzin

From the article:

Last week, C++26 was finalized in Sofia, Bulgaria — and C++26 will include all of the reflection papers that we were pushing for:

  1. P2996R13: Reflection for C++26
  2. P3394R4: Annotations for Reflection
  3. P3293R3: Splicing a Base Class Subobject
  4. P3491R3define_static_{string,object,array}
  5. P1306R5: Expansion Statements
  6. P3096R12: Function Parameter Reflection in Reflection for C++26
  7. P3560R2: Error Handling in Reflection

Those are in the order in which they were adopted, not in the order of their impact (otherwise splicing base classes would go last). This is a pretty incredible achievement that couldn’t have happened without lots of people’s work, but no one person is more responsible for Reflection in C++26 than Dan Katz.

So today I wanted to talk about a very cool example that Dan put together on the flight home from Sofia, while I was unconscious a few seats over: the ability to, at compile time, ingest a JSON file and turn it into a C++ object. That is, given a file test.json that looks like this:

{ "outer": "text", 
"inner": { "field": "yes", "number": 2996 } 
} 

We can write this:

constexpr const char data[] = { 
     #embed "test.json" 
     , 0 

}; constexpr auto v = json_to_object<data>;

Discover C++26’s Compile-Time Reflection -- Daniel Lemire

Capture-decran-le-2025-06-21-a-21.55.10-825x510.pngC++26 is bringing a long-awaited feature to the language: compile-time reflection, enabling programs to introspect and manipulate their own structure during compilation. This powerful capability opens the door to eliminating boilerplate, improving performance, and writing more expressive, reusable code with ease.

Discover C++26’s Compile-Time Reflection

by Daniel Lemire

From the article:

Herb Sutter just announced that the verdict is in: C++26, the next version of C++, will include compile-time reflection.

Reflection in programming languages means that you have access the code’s own structure. For example, you can take a class, and enumerate its methods. For example, you could receive a class, check whether it contains a method that returns a string, call this method and get the string. Most programming languages have some form of reflection. For example, the good old Java does have complete reflection support.

However, C++ is getting compile-time reflection. It is an important development.

I announced a few months ago that thanks to joint work with Francisco Geiman Thiesen, the performance-oriented JSON library simdjson would support compile-time reflection as soon as mainstream compilers support it.

Variadic Class Template Arguments -- Sandor Dargo

SANDOR_DARGO_ROUND.JPGVariadic templates are a powerful C++ feature that allow template classes or functions to accept an arbitrary number of parameters. In this article, we’ll explore how to combine them with class templates and examine the various ways the parameter pack can be expanded.

Variadic Class Template Arguments

by Sandor Dargo

From the article:

Let’s talk about class templates and variadic parameters. How to use them in combination?

But first of all, what are variadic templates?

Variadic template don’t accept a fixed size of parameters, but anything from one to more. The set of parameters is called the parameter pack.

In the template parameter list, we can see the three dots (...) signaling the pack after the typename or class keywords, or after the constraints. Then later in the function / constructor parameter list, you can observe it right after the actual type name, and finally once the pack is expanded, it comes after the packed variable name.

Variadic arguments can be used in so many different ways both with function and class templates. Today, we are focusing on class templates.

When you are using variadic templates with class templates, the expansion can happen at different places. You might expand the parameter pack

  • within the constructor or any other member function
  • when declaring a member variable
  • at the inheritance list
  • in a using declaration
  • in friend declarations

 

C++26: Disallow Binding a Returned Reference to a Temporary -- Sandor Dargo

SANDOR_DARGO_ROUND.JPGThis change in C++26 tightens the rules around returning references to temporaries — and that’s a good thing. It turns dangerous, bug-prone code into immediate compilation errors, making code safer and easier to reason about. It also reflects a broader trend in modern C++: giving programmers stronger guarantees and better diagnostics, even at the cost of breaking some old patterns.

C++26: Disallow Binding a Returned Reference to a Temporary

by Sandor Dargo

From the article:

In short, thanks to P2748R5 by Brian Bi, it’s no longer possible to return a reference that binds to a temporary expression, and that’s just lovely.

What exactly is changing?

Often, a proposal modifies a lot of wording, and it’s not practical to start by reading through it all. But in this case, the changes are small and easy to digest, so let’s dive in.

This line is being removed:

“The lifetime of a temporary bound to the returned value in a function return statement (8.7.4) is not extended; the temporary is destroyed at the end of the full-expression in the return statement.”

And this line is being added (excluding the examples we’ll discuss in a moment):

“In a function whose return type is a reference, other than an invented function for std::is_convertible ([meta.rel]), a return statement that binds the returned reference to a temporary expression ([class.temporary]) is ill-formed.”

There are two interesting shifts here:

  • The language becomes more specific. It doesn’t merely mention “temporaries” but refers directly to references binding to temporary expressions, with certain exceptions.
  • The effect isn’t just that lifetime extension doesn’t happen, it’s now a compilation error. The code is ill-formed.

constexpr Functions: Optimization vs Guarantee -- Andreas Fertig

me.pngConstexpr has been around for a while now, but many don’t fully understand its subtleties. Andreas Fertig explores its use and when a constexpr expression might not be evaluated at compile time.

constexpr Functions: Optimization vs Guarantee

by Andreas Fertig

From the article:

The feature of constant evaluation is nothing new in 2023. You have constexpr available since C++11. Yet, in many of my classes, I see that people still struggle with constexpr functions. Let me shed some light on them.

What you get is not what you see

One thing, which is a feature, is that constexpr functions can be evaluated at compile-time, but they can run at run-time as well. That evaluation at compile-time requires all values known at compile-time is reasonable. But I often see that the assumption is once all values for a constexpr function are known at compile-time, the function will be evaluated at compile-time.

I can say that I find this assumption reasonable, and discovering the truth isn’t easy. Let’s consider an example (Listing 1).

constexpr auto Fun(int v)
{
  return 42 / v; ①
}

int main()
{
  const auto f = Fun(6); ②
  return f;              ③
}
The constexpr function Fun divides 42 by a value provided by the parameter v ①. In ②, I call Fun with the value 6 and assign the result to the variable f.

Three types of name lookups in C++ -- Sandor Dargo

SANDOR_DARGO_ROUND.JPGLet's revisit a core concept in C++: how the compiler finds the names you use in your code. From qualified and unqualified name lookups to the special case of Argument-Dependent Lookup (ADL), understanding these mechanisms is essential for writing clear and correct C++ programs.

Three types of name lookups in C++

by Sandor Dargo

From the article:

Let’s get back to some basics this week and talk about name lookups in C++. In other words: when you refer to a symbol in your code, how does the compiler find it?

Essentially, we can differentiate between three kinds of lookups:

  • Qualified name lookup
  • Unqualified name lookup
  • Argument-Dependent Lookup (ADL)

Let’s explore them in that order.

Qualified Name Lookup

The term qualified refers to symbols that are explicitly scoped using the :: operator. In other words, these are names that appear to the right of a ::, such as x in a::b::x.

Before the compiler can perform a qualified name lookup, it must first resolve the left-hand side of the :: operator. This identifies the namespace or class being referenced.

Qualified name lookup is relatively simple: it only searches the explicitly named scope. It does not search enclosing or outer scopes.

Why does C++ think my class is copy-constructible when it can’t be copy-constructed? -- Raymond Chen

RaymondChen_5in-150x150.jpgIn C++, the presence of a user-declared (but not explicitly deleted) copy constructor is enough for the type to be considered copy-constructible by traits like std::is_copy_constructible. However, whether that constructor is instantiable is a separate matter—if it attempts to call a deleted base copy constructor, you'll still get a compile-time error when you actually try to use it.

Why does C++ think my class is copy-constructible when it can’t be copy-constructed?

by Raymond Chen

From the article:

Consider the following scenario:

template<typename T>
struct Base
{
    // Default-constructible
    Base() = default;

    // Not copy-constructible
    Base(Base const &) = delete;
};

template<typename T>
struct Derived : Base<T>
{
    Derived() = default;
    Derived(Derived const& d) : Base<T>(d) {}
};

// This assertion passes?
static_assert(
    std::is_copy_constructible_v<Derived<int>>);

Why does this assertion pass? It is plainly evident that you cannot copy a Derived<int> because doing so will try to copy the Base<int>, which is not copyable.

Returning several values from a function in C++ (C++23 edition) -- Daniel Lemire

RkWx2Fr8_400x400.jpgWhile C++ doesn’t have native syntax for returning multiple values like some other languages, modern C++ offers powerful tools to accomplish the same goal. With features like std::tuple, structured bindings, std::expected, and std::optional, handling multiple return values—and even error codes—has become both clean and expressive.

Returning several values from a function in C++ (C++23 edition)

by Daniel Lemire

From the article:

Many programming languages such as the Go programming language are designed to make it easy to return several values at once from a function. In Go, it is often used to return an optional error code. The C++ programming language does not have a built-in support for returning several values. However, several standard types can serve the same purpose. If you need to return two values, you can use an std::pair instance. If you need to return two or more values, an std::tuple instance will do. With recent C++ standards, it works really well!

Suppose we want to compute a division with a string error message when one is trying to divided by zero:

std::tuple<int,std::string> divide(int a, int b) {
 if (b == 0) {
 return {0, "Error: Division by zero"};
 }
 return {a / b, "Success"};
}

This approach works nicely. The code is clear and readable.

You might be concerned that we are fixing the type (int). If you want to write one function for all integer types, you can do so with concepts, like so:

Constructing Containers from Ranges in C++23 -- Sandor Dargo

SANDOR_DARGO_ROUND.JPGStarting from C++23, standard containers support a new set of constructor overloads. These constructors take a std::from_range tag, a range and an optional allocator. These from_range constructors make it easier to construct containers from ranges, helping make C++ code more concise, more expressive, and less error-prone.

Constructing Containers from Ranges in C++23

by Sandor Dargo

From the article:

I’ve written plenty on this blog about standard algorithms, but far less about ranges. That’s mostly because, although I’ve had production-ready compilers with C++20 ranges since late 2021, the original ranges library lacked a few key capabilities.

The biggest gap was at the end of a pipeline: you could transform data lazily, but you couldn’t drop the result straight into a brand-new container. What you got back was a view; turning that view into, say, a std::vector still required the old iterator-pair constructor.

C++23 fixes that in two complementary ways:

  • std::to (an adaptor that finishes a pipeline by converting to a container), and
  • from_range constructors on every standard container.

Today we’ll focus on the second improvement, because it’s the one you can implement in your own types, too.

The from_range constructor

Every standard container now supports a new set of constructors that make integration with ranges smoother — the so-called from_range constructors.

These constructors allow you to build a container directly from a range, rather than from a pair of iterators.

C++ Insights now uses Clang 20 -- Andreas Fertig

me.pngTime flies—C++ Insights just turned 7! To celebrate, I’ve upgraded the tool to Clang 20, unlocking even more C++23 and C++26 features for you to explore.

C++ Insights now uses Clang 20

by Andreas Fertig

From the article:

size_t

For a long time now, when you used size_t or std::size_t the resulting transformation kept the name. It did not expand to the underlying machine-specific date type. To be honest, that was more like a happy accident. Clang 20 came with two changes to libc++

The first https://github.com/llvm/llvm-project/commit/d6832a611a7c4ec36f08b1cfe9af850dad32da2e modularized <cstddef> for better internal structuring, avoiding too much content to be included. This patch was followed by a second one: https://github.com/llvm/llvm-project/commit/5acc4a3dc0e2145d2bfef47f1543bb291c2b866a. This one now made an interesting change.

Previously, libc++ defined std::size_t as

1
using ::size_t _LIBCPP_USING_IF_EXISTS; 

As the second patch highlighted, this required including the operating systems <stddef.h>. In the spirit of reducing unnecessary includes the line was changed to:

1
using size_t = decltype(sizeof(int)); 

This is an easy C++ solution to get the proper data type for size_t. Which is great. Yet, the AST nodes of the two versions look different. Previously, the operating system (macOS in this case) defined in its header:

1
typedef unsigned long size_t; 

Well, with the new version, the transformation no longer stops at size_t but expands it all down to unsigned long. This probably should have been the case from the beginning, but I liked that tests and transformations did not change across platforms in this specific case. However, there are other instances where the transformation did yield different output on different platforms, so I accept this one.