Assertions Parameterized by Location
Let’s say you are implementing a class to represent a Phone. You might want to have the precondition that you cannot hangUp() a phone if it was already hung up.
The typical way to do this is with assert() statements, so in the file phone.cpp you might write:
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 | class Phone { private: enum Status { Dialing, Connected, Disconnected }; Status _status; public: void hangUp() { assert(_status != Disconnected); /*... some code here...*/ } /*... more functions here ...*/ }; |
Then let’s say in caller.cpp you are modeling someone who wants to have a “typical” phone conversation:
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | void Alice::callBob() { _phone.dial("1-617-542-5942"); friendlyGreetings(); tellBruceSchneierJoke(); haveSpecialDiscussion(); _phone.HangUp(); } void Alice::haveSpecialDiscussion() { std::string password = askForThePassword(); if (password == "hostilefork") { describeWorldTakeover(); laughManiacally(); } else { talkAboutTheWeather(); } _phone.HangUp(); } |
When callBob() executes and hits line 48, the Phone’s assertion will fire because it was already hung up during haveSpecialDiscussion() (on line 60). Most implementations of assert will capture the filename and line number where the actual assertion occurs, so you’d probably get a message like:
assertion failure on line 90 of phone.cpp
There’s no flexibility to let you indicate another “place” in the source. For instance, what if you wanted the assert to identify the precise offending call to hangUp()?
To address these kinds of needs I created codeplace. One of its many uses is the location-parameterized assert…which lets you pass in a “place” to be identified when the assert triggers. So in phone.cpp you would write:
98 99 100 101 102 103 | public: void hangUp(const codeplace& cp) { assert(_status != Disconnected, cp); /*... some code here ...*/ } |
Then at the call sites, you would use a special macro called HERE to create a codeplace object which you pass as a parameter. For instance, the assertion-triggering call to hangUp() in caller.cpp would look like this:
48 | _phone.hangUp(HERE(false)); |
(Note: the forthcoming documentation for codeplace will explain what false, true, and string parameters mean for “HERE” mean…)
Now when the assertion is reported by the program, it will identify the call site. With this change to our example above, you would get something more like:
assertion failure on line 48 of caller.cpp
The location-parameterized assert is only one application of a codeplace. They are helpful abstractions for any time we need to speak about a place in our source. For instance, if we wanted to track the previous disconnection call we could have a local variable in the Phone class to save it… and the tracked<T> template even does this for you automatically. There are some other nuances of the codeplace implementation that are beyond the scope of this particular issue.
You might theorize that run-time access to the stack is the ultimate API for dealing with this sort of thing. Imagine if Phone::hangUp() could somehow obtain an object representing the call stack, and then extract whatever information it wanted about the callers. That’s a little heavy-handed, and I believe that the odds of the API being abused are so high that a “narrower” protocol agreement between callers and subroutines would need to be established for the common scenarios.
Tags: cplusplus

January 7th, 2009 at 5:32 pm
It should be noted that most sane OS’s (read: those not designed in Redmond) provide a handy backtrace_symbols() function that can be used to obtain the current stack trace in human readable form. An assert() function could use that to print out the errant caller’s full location, log it to a file, etc.
Jeremy
January 8th, 2009 at 4:39 pm
Stack traces are useful, although those APIs are generally platform-dependent (and require that you’re shipping a version of your program with debug symbols). I imagine that those APIs generally trigger some sort of security concerns as well.
This allows some fairly useful information without going quite that far. Also, you can use them in a comparative way. You can save a codeplace in a value and then test against specific ones. It’s a little harder to pick over a stack data structure to extract the information you’re interested in…
January 8th, 2009 at 6:38 pm
Hmm, it’s hard to see what security concern there could be, since they don’t actually change the state of anything. You’re right about requiring debug symbols in your executable if you want human-readable function names.
As for testing for specific codeplaces, that would be pretty fragile, since whenever you inserted/deleted a line of code in the file above where the codeplace was declared, the codeplace’s identity would change and any test for that codeplace would break.
January 8th, 2009 at 7:31 pm
Ah, although off the top of my head it seemed you could put a codeplace into a variable and then compare against the variable in multiple places… I now see that defeats the purpose. It would indicate the location of the variable declaration and wouldn’t live right next to the “interesting” line of code.
But bear in mind this article is out of date in terms of capturing my attempts to put identity-in-text…for reasons like that and others. For my own projects, I’ve bitten the bullet and drop Base64 UUIDs all over my source, which some people might think is pointless but I find it very valuable. The build process makes a table of them and what line numbers they are at off to the side. Winds up looking more like:
Guess it’s obvious that I do not care much for programming in text files! I feel like if I can point to something on the screen with my finger I should have a way of expressing that “place” in my program. I should be able to say “I mean that dot, there!” without having to say “the dot between phone and hangUp”. Of course, that requires a special kind of editor…one that recognizes the difference between moving that dot to another place and creating a new one…