Bunch of small technical notes. There's a dedicated RSS feed for this section if you want to get notification on a new note.
(31 Aug 2023)
GCC has a nifty flag called -fwhole-program
which basically marks all
functions (except "main") and global variables as static
so that they can be
optimized more aggressively.
This is pretty great if you're trying to jumbo-build other programs
that weren't written with jumbo builds in mind.
However, if everything is manually marked as static
already then
(unsurprisingly) -fwhole-program
has zero effect.
On the other hand, it does have some downside for "libc-free" programs such as
having to mark non-conventional entry-points as externally_visible
or leaving
you with undefined references due to buggy optimization.
Clang also doesn't support this flag (or the externally_visible
attribute) so
that adds some more annoyances.
-fwhole-program
is still useful for jumbo building other software but if
you're writing something with jumbo-build in mind already then it's better and
more portable to just mark everything as static
manually and just avoid
-fwhole-program
.
(29 Mar 2023)
Assertions are an incredibly useful debugging tool (they are also incredibly misunderstood, novices often mistake it as an error-handler and packagers (apparently) mistake it for "security" checks).
Lua unfortunately doesn't have true assertions. It does have an
assert
function - but unlike a debugging tool (which is what
assertions are supposed to be) - the lua assert
sticks around in the source
no matter what.
To get around this limitation, I'm using the following method.
-- at the top of the file
local myassert = function() end
-- and then at "startup"
if debug then
myassert = assert
end
This turns the assert into a no-op in non-debug environment. However, unlike C, lua doesn't have a "pre-processor" and since lua is (typically) interpreted rather than compiled, there's no way (that I'm aware of) to avoid evaluating the argument itself.
For my use-cases it's not a fatal flaw. However, it is disappointing that lua's
assert
is basically just a fatal-error-handler - which just further propagates
the misunderstanding of assertions.
(25 Feb 2023)
One useful, but relatively unknown feature of realloc is that if
the ptr
argument is NULL, it acts as malloc
. So instead of writing:
if (p == NULL) /* first time around, allocate */
p = malloc(...);
else /* grow the buffer */
p = realloc(p, ...);
You can get rid of the if statement and write the following:
p = realloc(p, ...);
Also worth noting that older C standard says that if the size
argument of
realloc
is 0 and the pointer is non-null, it acts as free
. However, not all
implementation follows this and thus this is non-portable in practice. Due to
this, zero sized realloc
have been made undefined in the upcoming C2x
standard as well. So you should definitely avoid it.
(19 Feb 2023)
Given that strtoul
is supposed to parse an unsigned long, you'd expect something like "-1" to
error out due to ERANGE
due to being outside of [0, ULONG_MAX
].
However, that's not what it does. Instead it's defined to return -num
for
"-$num" input, i.e ULONG_MAX
for "-1" (1, 2).
So be wary if you decide to use this function (you also need to be wary of locales too).
(19 Feb 2023)
The idiomatic (x >> (32 - r)) | (x << r)
isn't safe when the rotation amount
r
can be 0 (assuming x
is 32bit, it'll lead to x >> 32
which is UB).
Use this instead (note that the
information about clang's optimizer on that article is outdated/fixed now):
(x >> (-(unsigned)r & 31)) | (x << r)
(19 Feb 2023)
[lindex [lsort -integer $list] end]
is significantly faster than
[tcl::mathfunc::max {*}$list]
. I'm not entirely sure why. Might be due to
max()
needing to deal with floating-point while the integer sort doesn't.