Using Reflection in C++26 and a Toaster to Learn Reflection

How hard is it to put a Boeing 737 safely on the ground? I asked my toaster, and it said: extremely difficult.

Not something you would like to hear when the runway is already visible through the front windshield. Not much time left to learn the basics of avionics, cockpit instruments, procedures, and the rest.

Luckily, I used to write software for full-flight simulators — sophisticated machines used so future pilots don’t need to crash real planes while learning how to operate them.

All we need is reflection.

Okay, probably more. But from the whole codebase I worked with, reflection was the part I remember as the most difficult. The code was written by real C++ wizards and contained hundreds of lines of templates and macros, with the word META appearing almost every second line. All this magic just so we could access all the variables from the simulation engine.

If we can get reflection right, the rest of the task should be child’s play.

“Toaster, let’s talk about C++ and reflection.”

A quick crash course, and I was able to write down three lessons learned. What you see below is the version after multiple iterations, until I could finally prove to myself that my mind understood what was going on.

First lesson learned: reflection is just a mechanism for accessing the compiler’s knowledge about your code.

Second lesson learned: there is a cat ears* operator ^^ in C++. It returns a std::meta::info value that acts as our handle to the reflected stuff, and we can use a set of std::meta:: functions to get useful information from it. All of this happens at compile time.

Third lesson learned: the opposite direction of the cat ears operator is [: :]. I don’t have a name for it yet, but it looks quite toasterish.

“Toaster, am I missing anything on my list?”

Fourth lesson learned: reflection itself happens at compile time, but the code produced using it can operate on runtime objects. I thought I already understood this one, but the toaster insisted that I put it on the list anyway.

Closely related to the fourth lesson are annotations. Those are actually interesting because they are explicitly designed to attach information to declarations that reflection can later observe.

[[=cli_hidden{}]]
float internal_state;

Of course there was more, but the ground was getting closer, so there was no time for this. It was time to practice.

ctrl+T
tmux
claude

create a simple project (cmake) showing usage of reflections in cpp (^^operator). Class aircraft containing pitch, roll, yaw – float and landed – bool. All 4 should be exposed via CLI using reflection mechanism

Claude was “gesticulating” for more than 10 minutes. Luckily, our plane was a very slow one, so I could go make a coffee, take my dog out, and come back before it was done.

The generated code compiled. It worked. And it was perfect as a starting point for exploring the topic.

And that was enough — a small playground to mess with the code. What happens if I add this, remove that, or hit the whole thing with a hammer a few times? Just code-monkey playtime mode.

After the monkey finished playing, we were ready.


  template <typename T> constexpr auto members = std::define_static_array(std::meta::nonstatic_data_members_of(^^T, std::meta::access_context::unchecked()));

  // ...
    
  Aircraft ac;
  template for (constexpr auto m : members<Aircraft>) {
      if constexpr (std::meta::identifier_of(m) == "landed") {
          if constexpr (std::meta::type_of(m) == ^^bool) {
              ac.[:m:] = true;
          }
      }
  }

Touchdown.

* Finally! After all those years, we have something cool in our language. I was always jealous that Kotlin had its Elvis operator ?:

Author’s note: there was a time when I asked the Toaster for illustrations in the “Thinking Toasters” style and got nice, warm, happy drawings. Now I get neon pink fuchsia. I think the ghost of Mean Girl is here for good. See previous post.

14 Ways to Reverse a 32-Bit Integer. (And Why They Don’t Matter Anymore)

It was 2017. Pre-AI, pre-COVID. A time when people worked in places called offices, and finding a place in one meant writing cover letters, sending CVs, and having an interview with a real person.

And there I was, sitting in a small room with a department manager and two senior engineers—the three people who would decide whether I was a fit for their team.

They were well prepared. Intro. Work description. Ten technical questions—all written on a single sheet of paper. Real professionals.

Among the questions was the greatest one I have ever received:

Reverse the bit order in a 32-bit integer.

I got a pen, and my job was to write a solution. While coding on paper, I was loudly explaining what I was doing and why. I went with a simple while loop and pointers—the simpler it is, the bigger the chance the thing will compile — or would have, if writing Ctrl+F9 with my pen had any effect.

Luckily, I did not get the job. Sometimes rejection is the best thing that can happen in your life.

But the question stayed in my head. Later, I found multiple solutions—including a single ARM instruction that does exactly that. It became a fun little obsession: benchmarking different implementations and comparing them.

Now fast forward and it’s 2026. Post-COVID. Mid-AI. I am sitting on the other side, interviewing a potential candidate.

Should we ask them to do an online assessment?

The task I received ten years ago can now be solved in 30 seconds. Including all solutions I was so proud to find myself, even more. Including solid reasoning for each version. So how do you see whether the person on the other side really understands what is going on?

And if they do understand—does it even make them a good candidate? Do they need to know how to reason about code at all?

Times have changed a lot, and building teams is no longer just about whether someone is “a good fit” today. It is about predicting whether a person will still be a good fit in an environment that is going to change dramatically over the next five years. And in that context, reversing bits may not be the most valuable skill at all.

Maybe the best question nowadays is much simpler:

“Tell me about a technical question that stayed with you long after the interview was over.”

How NOT to use mocks in googletest framework and why singletons are bad

Lets make a game*. It is about marmot – a great, wild beast that can do one thing: it can sleep. Marmot’s state depends on the superposition of his universe: in the winter he sleeps. And during the night he sleeps. And if he not sleeps, he is awake. Our Universe interface will look like this:

class Universe
{
public:
    virtual ~Universe() = default;

    virtual bool IsDay() const = 0;
    virtual bool IsWinter() const = 0;
};

Now comes a brilliant idea: let’s model The Universe as a singleton class – in the end there should be only one and its state should be same for all the clients (marmots). We add this factory method.

Universe& UniverseFactory::getInstance()
{
	static RandomUniverse singleton;
	return singleton;
}

Marmot can now use it to get a Universe and figure out if he supposed to sleep or not.

MarmotState Marmot::getState() const
{
    if(UniverseFactory::getInstance().IsWinter()) {
        return MarmotState::MARMOT_IS_SLEEPING;
    }

    if(UniverseFactory::getInstance().IsDay()) {
        return MarmotState::MARMOT_IS_AWAKE;
    }

    // Oh oh - we made a mistake here! Marmot should not be awake
    return MarmotState::MARMOT_IS_AWAKE;
}

Of course everything is unit tested and all the tests are green so lets “ship it!“.

Next day the support phone line gets red hot as hundreds of people are calling, asking why their marmots are not sleeping in the middle of the night. What happened here? Why our tests did not catch this? Let’s look at them.

static UniverseMock universe;

Universe& UniverseFactory::getInstance()
{
	return universe;
}

class SomeClassTest: public ::testing::Test {
public:

	void SetUp() {}

	void TearDown() {}
};

TEST_F(MarmotTest, When_Not_Winter_And_Day_Should_Be_Awake) {
    Marmot marmot;

    EXPECT_CALL(universe, IsWinter()).WillOnce(Return(false));
    EXPECT_CALL(universe, IsDay()).WillOnce(Return(true));
    ASSERT_EQ(MarmotState::MARMOT_IS_AWAKE, marmot.getState());
}

TEST_F(MarmotTest, When_Winter_Should_Sleep) {
    Marmot marmot;

    EXPECT_CALL(universe, IsWinter()).WillRepeatedly(Return(true)); // oh!

    EXPECT_CALL(universe, IsDay()).WillOnce(Return(true));
    ASSERT_EQ(MarmotState::MARMOT_IS_SLEEPING, marmot.getState());

    EXPECT_CALL(universe, IsDay()).WillOnce(Return(false));
    ASSERT_EQ(MarmotState::MARMOT_IS_SLEEPING, marmot.getState());
}

TEST_F(MarmotTest, When_Night_Should_Sleep) {
    Marmot marmot;

    EXPECT_CALL(universe, IsDay()).WillOnce(Return(false));
    ASSERT_EQ(MarmotState::MARMOT_IS_SLEEPING, marmot.getState());
}

The last test should fail but it does not because we’ve made two terrible things: the universe mock is a static variable and there is no expectation on IsWinter in the last test. The first issue is the real problem here. The mock object does hold its state until destroyed and in the second test we asked it to always return true when ‘IsWinter’ gets called. Fine. Let’s just create and destroy the mock for every test run so the expectations don’t propagate between tests.

static UniverseMock* universe;

Universe& UniverseFactory::getInstance()
{
    return *universe;
}

class MarmotTest: public ::testing::Test {
public:

    void SetUp() {
        universe = new UniverseMock;
    }

    void TearDown() {
        delete universe;
    }
};

And this will work until one day somebody does this:

auto& universe = UniverseFactory::getInstance();

MarmotState Marmot::getState() const
{
if(universe.IsWinter()) {
return MarmotState::MARMOT_IS_SLEEPING;
}

if(universe.IsDay()) {
return MarmotState::MARMOT_IS_AWAKE;
}

...

Now we get a segfault during testing — because our singleton is recreated for each test, while the marmot updates its reference only once. Which brings us to the main conclusion of this post: singletons are bad, and we should think carefully before deciding to use them. And if we do choose to use a singleton, we need to think even harder to ensure our code remains testable.

Does that mean we have to redesign our marmot game?
Not necessarily. We can leave the bad code untouched — with the help of a magical function that verifies and clears expectations between tests. It’s called (surprise, surprise): VerifyAndClearExpectations. And we can just call it after every test run, like this:

class MarmotTest: public ::testing::Test {
public:

    void SetUp() {}

    void TearDown() {
        Mock::VerifyAndClearExpectations(&universe);
    }
};

Now tests go red and we can clearly see the root-causes – one because of wrong expectations and second because of the bug. Fix it, ship it, and go for a… not yet! First, add a “Refactor Marmot Universe” ticket to the technical backlog. Now you can go for a beer — or to sleep, if you’re a marmot and it’s winter. Or night.

*All source code can be found here