SEATBELTS
DEFINITELY UNDER CONSTRUCTION
I'm trying to create as many seat belts and idiot lights as possible. Using C macros and automatic function declaration generation makes this much easier for me to do, and is far more maintainable.
Headerizer creates function declarations based on function definitions. It scans the source files passed to it and extracts the function declarations. Then it puts them into the appropriate .h file or, in the case of static functions, back into the source file itself.
The headerizer also adds function attributes as specified by the decorations on the source.
All of these macros are GCC-specific right now, but soon will have equivalent semantics for lint added to them. This will make lint a far more powerful tool. If/when we ever get splint going, too, we can add semantics there as well.
Also, where it says "compiler", it could also mean "lint or any other static analysis tool like splint."
What's a shim?
Think of "shim" as shorthand for "placeholder". It's 64% shorter.
GCC (and lint and splint) likes to complain (as well it should) if you pass an argument into a function and don't use it. If we know that we're not going to use an argument, we can either remove the argument from the function declaration, or mark it as unused.
Throwing the argument away is not always possible. Usually, it's because the function is one that gets referred to by a function pointer, and all functions of this group must have the same, say, three args: Interp, Foo and Bar. Maybe a given function doesn't use Foo, but we still have to accept Foo. In this case, we can in the body of the func, UNUSED(Foo)
, if we plan to use it in the future. Or, if we never will use it, mark it as a SHIM(Foo)
in the declaration.
Function Decorators
PARROT_WARN_UNUSED_RESULT
Tells the compiler to warn if the function is called, but the result is ignored.
PARROT_IGNORABLE_RESULT
Tells the compiler that it's OK to ignore the function's return value
PARROT_MALLOC
Functions marked with this are flagged as having received malloc
ed memory. This lets the compiler do analysis on memory leaks.
PARROT_CONST_FUNCTION
The function is a deterministic one that will always return the same value if given the same arguments, every time. Examples include functions like mod
or max
. An anti-example is rand()
which returns a different value every time.
PARROT_PURE_FUNCTION
Less stringent than PARROT_CONST_FUNCTION, these functions only operate on their arguments and the data they point to. Examples include strlen()
or strchr()
.
PARROT_DOES_NOT_RETURN
For functions that can't return, like Parrot_exit()
. This helps the compiler's flow analysis.
PARROT_CANNOT_RETURN_NULL
For functions that return a pointer, but the pointer is guaranteed to not be NULL.
PARROT_CAN_RETURN_NULL
For functions that return a pointer that could be null.
ARGUMENT & VARIABLE DECORATORS
NOTNULL(x)
For function arguments and variables that must never have NULL assigned to them, or passed into them. For example, if we were defining strlen()
in Parrot, we'd do it as strlen(NOTNULL(const char *p))
.
NULLOK(x)
For function arguments and variables where it's OK to pass in NULL. For example, if we wrote free()
in Parrot, it would be strlen(NULLOK(void *p))
.
SHIM(x)
PASSING AROUND INTERPRETERS
Most of the time, if you need an interpreter in your function, define that argument as PARROT_INTERP
. If your interpreter is a shim, then use SHIM_INTERP
, not SHIM(PARROT_INTERP)
.
Examples
PARROT_API
PARROT_WARN_UNUSED_RESULT
INTVAL
string_str_index(PARROT_INTERP, NOTNULL(const STRING *s),
NOTNULL(const STRING *s2), INTVAL start)
string_str_index
is part of the Parrot API, and returns an INTVAL. The interpreter is used somewhere in the function. String s
and s2
cannot be NULL. If the calling function ignores the return value, it's an error, because you'd never want to call string_str_index()
without wanting to know its value.
PARROT_API
PARROT_PURE_FUNCTION
INTVAL
parrot_hash_size(SHIM_INTERP, NOTNULL(const Hash *hash))
{
return hash->entries;
}
This function is a pure function because it only looks at its parameters or global memory. The interpreter doesn't get used, but needs to be passed because all PARROT_API functions have interpreters passed, so is flagged as a SHIM_INTERP.
We could put PARROT_WARN_UNUSED_RESULT
on this function, but since all PARROT_PURE_FUNCTION
s and PARROT_CONST_FUNCTION
s get flagged that way anyway, there's no need.