[C++] Custom assert

Started by
11 comments, last by RmbRT 3 months, 2 weeks ago

Hi everybody,
What is your thinking about having custom assert? Why is it important? What is the best way?
Thanks to enter the discussion!

Advertisement

Why do you want to know?

It's a whole discussion since lot of many books, article, game companies.
There are some arguments about Unit Tests for example.
I'm interested to know the thinking of the community.
And next to the thinking what people recommend from experience.

Not sure what asserts that to do with Unit Test. asserts should mainly be used to ensure invariants, that are not naturally enforcable (ie. two containers that are to be filled together, always having the same size). Unit Tests are external tests that are written to ensure code is behaving the same way regardless of input - which is something that should not be enforced via asserts. Maybe if you want some poor mans unit-tests, you just write methods with asserts as tests? But I wouldn't recommend that generally.

As for custom asserts themselves, they have benefits. You might want to be able to customize when asserts are executed (maybe you have a very nasty release-only bug, that you want to double-check your assertions, w/o turning of NDEBUG itself which may be used elsewhere). You may want to leave asserts in certain user-tested builds, but have them log instead; etc… But you would mostly be looking into your own assert-macro (which is necessary to ensure no code inside assert is generated/executed when excluded.

On thing I can add is that it might be benifical to have an assert that does leave the checked code in release-builds. Say you want to ensure that a map-insert actually happened:

const auto [itr, wasAdded] = map.try_emplace(key, value);
assert(wasAdded);

With assert, you must make a separate variable, because if you put the line inside assert, it would be removed. But if we made something like “ensure”, we could write:

ensure(map.try_emplace(key, value).second);

With lot less annoying overhead. (ensure would behave like assert in debug-builds, and otherwise it would just execute the code normally).

So there is definately room for customization, but how much you that benefits you depends on what you want to do with it. If all you do is “assert”, with the same behaviour as baseline, there is no point in making your own macro (except maybe to be easier able to change later on).

Alundra said:

It's a whole discussion since lot of many books, article, game companies.
There are some arguments about Unit Tests for example.
I'm interested to know the thinking of the community.
And next to the thinking what people recommend from experience.

As you discovered it's a broad subject. You have very good chances that “the community” thinks and recommend in similar ways. So we can have the same discussion here as well, but very likely you won't hear much new or useful things as long as you stick at that global level.

Every major game engine and library, and most of the minor ones, include a variety of custom assertion systems.

Unit testing is also common and is almost entirely a cost/benefit decision. Most gameplay code is on the “cheaper to hire an army of QA” side of the balance, but long-running infrastructure in larger companies like server infrastructure or large shared libraries the balance favors automation.

The posts feel like a bad attempt at a school assignment.

It's to improve about the topic and discuss about it.
The most popular is to base custom assert from Pow2.
The main things I can think of:
- Have the message in the assert function
- Make the assert possible for release build too
- There is a debate about assert to not break unit test
The main source of information is now: https://gamesfromwithin.com/asserting-oneself

That's one, but far from the "best" base for things. An assert like that is a single tool, and not even a good one. There's an entire Home Depot full of tools, and more stores besides, not just that small screwdriver.

Go look up xunit test patterns, a nearly 900 page book of many, many, many ways to have automated unit tests. The content is freely available on the website as well. That would be a good starting point into what is an enormous field inside software development.

The article you linked to is very outdated, with a lot of old viewpoints.

You obviously have to do unit tests. And you obviously need a unit test framework for that. Personally I use Doctest because I like including the unit tests in the same source files as the code being tested.

However, unit tests are a completely separate issue from assertions, and have a completely different purpose. Assertions are there to document the assumptions of your code; unit tests are there to verify the correctness of your code.

Personally I use the standard assert macro from the C++ standard library for assertions. I don't have any concrete needs that aren't met by the standard assert, and I don't see the sake of customization for the sake of customization.

I never unit test unless something is a complex part of a whole and I expect the larger thing to fail, and it seems infeasible to track down a bug once the larger thing starts misbehaving. I effectively use a series of global variables that register a test function on initialisation and then a main function that runs all those tests. A test framework is not required. A simple TEST(name, codeblock) macro is entirely sufficient.

I use assertions as a way to validate untrusted inputs, such as user input or third party code. Assertions never get disabled in my style; instead, I annotate values as trusted or untrusted in constexpr / inlined constructors whenever possible. For example, I have a

class Index {
	unsigned value;
	bool ok;
public:
	inline Index(): ok(false) {}
	constexpr Index(Ok<unsigned> trusted): value(*trusted), ok(true) {}
	constexpr Index(unsigned untrusted): value(untrusted), ok(false) {}
	inline unsigned operator() (unsigned size) const
		{ if(!ok) { assert(value < size); } return value; }
};

class, which I can either construct via i or ok(i) (which returns a type-annotated reference/copy of i, which then overloads Index's constructor). I use that calls to pass indices to array subscript functions and others. Inside my array's subscript operator, I then do index(array.size()), which reveals the index to me after asserting that the index.value is smaller than array.size(). However, this check is only performed if the ok flag of the index is not set. For non-inlineable instances of Index, this is less efficient than straight-out asserts, but for inlineable trusted indices, it has 0 overhead. It lets me reuse the same code for trusted and untrusted values and does not require me to mess around with NDEBUG-style macros, and completely hides the manual assertion behind a nice semantic annotation, as well as gating each access to the index with a syntax that explicitly makes the index relate to a given collection size. The assertion is only executed once, though, as repeated identical inlined assertions get eliminated.

P.S.: I really wonder why someone with 12.5 years of gamedev.net membership would create a thread about opinions on making your own asserts. This sounds like a <1 year programmer question.

Walk with God.

This topic is closed to new replies.

Advertisement