I come from the C++ world and I would like to use Julia more often. I generally use a lot of asserts in my code (a “defensive” programming approach), for instance imagine a function reading the first N records of a file. In pseudo C++ code you have something like:
// read N first records
//
void read_file_N(std::istream& file_in,const int N)
{
assert(N>=0); // logical error: coder/caller responsibility is involved
for(i=0;i<N;i++) {
file >> data;
// coder/caller responsibility is _not_ involved (I/O error)
//
if(!data.well_formed()) {
throw error("Error in file, data is not well formed");
}
...
}
}
There are (at least) two kind of errors. Logical ones, involving coder responsibility and errors (like corrupted I/O etc.) where coder is not responsible. For the first ones I use a lot of asserts, for the second one I use exceptions. In C/C++ I can easily remove the assert thanks to a compiler flag (-DNDEBUG) thus there is no run-time penalty in production code. However, when I code in Julia (I am quite new with this language) I have the feeling that these two notions are mixed and that we can not take the same approach: AFAIK Julia @assert/assert is close to my C++ exception usage, but there is no Julia equivalent to C++ assert. This is a real problem for me and the way I code.
Do I miss something?
What can I do to have a C/C++ equivalent to assert in Julia (with the constraint that we can remove them in production code)?
Thanks for the feedback (and sorry for my first post bad formatting).
I was thinking to something similar but I thought that maybe there was a special/intergrated Julia way for it.
I think that there is a typo, the macro should be something like
:($boolean || error(“Assertion $boolean failed”))
but this only print “false”, how to print it in its unevaluted form?
Here’s a way this can be done for modules without breaking precompilation.
module DebugAsserts
export debug_asserts, @dassert
function debug_asserts(m::Module, dodebug::Bool)
m.eval(:(_debug_enabled() = $dodebug))
nothing
end
# Like @assert, but only active in debug mode
macro dassert(exs...)
if !isdefined(__module__, :_debug_enabled)
eval(__module__, :(_debug_enabled() = false))
end
quote
if $__module__._debug_enabled()
@assert $(map(esc,exs)...)
end
end
end
end
AFAIU, The way this works is exploiting that #265 (reliable redefinition of methods) is fixed, this automatically recompiles all dependent functions that use @dassert every time debug_asserts is called. Depending on how many functions uses @dassert (or how many functions are in the call graph of any function that uses it) this will have to recompile a different number of functions but, in the worst case, using debug_asserts will cause a full recompilation of the whole package.
That’s right, this relies on the compiler to recompile all methods which use @dassert when the associated _debug_enabled is redefined via #265. You can use this @dassert in your own package exactly the same way as @assert, but the expression should be completely optimized away unless you call debug_asserts(MyModule, true).
Ah, clever. And since we don’t print the method replacement warning anymore, seems like this should be fairly seamless even too. I’m not sure we would want to put a trick like that in Base (to avoid potential confusion over when world-age changes become visible), but on-the-other-hand, it’s such a simple mechanism (and world-age visibility constraints usually aren’t too noticeable), so perhaps, why not!