Introduction
The c-stdaux project contains support-macros and auxiliary functions around the functionality of common C standard libraries. This includes helpers for the ISO-C Standard Library, but also other common specifications like POSIX or common extended features of wide-spread compilers like gcc and clang.
API
The c-stdaux.h
header contains a collection of auxiliary macros and
helper functions around the functionality provided by the different C
standard library implementations, as well as other specifications
implemented by them.
Most of the helpers provided here provide aliases for common library and compiler features. Furthermore, several helpers simply provide other calling conventions than their standard counterparts (e.g., they allow for NULL to be passed with an object length of 0 where it makes sense to accept empty input).
The namespace used by this project is:
c_*
for all common C symbols or definitions that behave like proper C entities (e.g., macros that protect against double-evaluation would use lower-case names).C_*
for all constants, as well as macros that may not be safe against double evaluation.c_internal_*
andC_INTERNAL_*
for all internal symbols that should not be invoked by the caller and are not part of the API guarantees.
Target Properties
Since multiple target compilers and systems are supported, c-stdaux
exports a set of symbols that identify the target of the current compilation.
The following pre-processor constants are defined (and evaluate to 1
) if
the current compilation targets the specific system. Note that multiple
constants might be defined at the same time if compatibility to multiple
targets is available.
C_COMPILER_CLANG
: The compiling software is compatible to the CLang LLVM Compiler.C_COMPILER_DOCS
: The compilation is part of generating documentation.C_COMPILER_GNUC
: The compiling software is compatible to the GNU C Compiler.C_COMPILER_MSVC
: The compiling software is compatible to Microsoft Visual Studio (use_MSC_VER
to check for specific version support).C_OS_LINUX
: The target system is compatible to Linux.C_OS_MACOS
: The target system is compatible to Apple MacOS.C_OS_WINDOWS
: The target system is compatible to Microsoft Windows.C_MODULE_GENERIC
: The *-generic.h module was included.C_MODULE_GNUC
: The *-gnuc.h module was included.C_MODULE_UNIX
: The *-unix.h module was included.
Note that other exported symbols might depend on one of these constants to
be set in order to be exposed. See the documentation of each symbol for
details. Furthermore, if stub implementations do not violate the guarantees
of a symbol, they will be provided for targets that do not provide the
necessary infrastructure (e.g., _c_likely_()
is a no-op on MSVC).
Guaranteed STD-C Includes
c-stdaux includes a set of C Standard Library headers. All those includes are guaranteed and part of the API. See the actual header for a comprehensive list.
Generic Compiler Intrinsics
This section provides access to compiler extensions and intrinsics which are either portable or have generic fallbacks.
-
_c_always_inline_
Always-inline attribute
Annotate a symbol to be inlined more aggressively. On GNUC targets this is an alias for
__attribute__((__always_inline__))
. On MSVC targets this is and alias for__forceinline
. On other systems, this is a no-op.
-
_c_boolean_expr_(_x)
Evaluate a boolean expression
- Parameters:
_x – Expression to evaluate
Evaluate the given expression and convert the result to 1 or 0. In most cases this is equivalent to
(!!(_x))
. However, for given compilers this avoids the parentheses to improve diagnostics with-Wparentheses
.Outside of macros, this has no added value.
- Returns:
Evaluates to the value of
!!_x
.
-
_c_likely_(_x)
Likely attribute
- Parameters:
_x – Expression to evaluate
Alias for
__builtin_expect(!!(_x), 1)
.- Returns:
The expression
!!_x
is evaluated and returned.
-
_c_public_
Public attribute
Mark a symbol definition as public, to be exported by the linker. On GNUC-compatible systems, this is an alias for
__attribute__((__visibility__("default")))
. On all other systems, this is a no-op.Note that this explicitly does not resolve to
__declspec(dllexport)
on MSVC targets, since that would require knowing whether to compile for export or inport and whether to compile for static or dynamic linking. Instead, the_c_public_
attribute is meant to be used unconditionally on definition only. For MSVC exports, we recommend module definition files.
-
_c_unlikely_(_x)
Unlikely attribute
- Parameters:
_x – Expression to evaluate
Alias for
__builtin_expect(!!(_x), 0)
.- Returns:
The expression
!!_x
is evaluated and returned.
Generic Utility Macros
A set of utility macros which is portable to all supported platforms or has generic fallback variants.
-
C_STRINGIFY(_x)
Stringify a token, but evaluate it first
- Parameters:
_x – Token to evaluate and stringify
- Returns:
Evaluates to a constant string literal
-
C_CONCATENATE(_x, _y)
Concatenate two tokens, but evaluate them first
- Parameters:
_x – First token
_y – Second token
- Returns:
Evaluates to a constant identifier
-
C_EXPAND(_x)
Expand a tuple to a series of its values
- Parameters:
_x – Tuple to expand
- Returns:
Evaluates to the expanded tuple
-
C_VAR(...)
Generate unique variable name
- Parameters:
_x – Name of variable, optional
_uniq – Unique prefix, usually provided by
__COUNTER__
, optional
This macro shall be used to generate unique variable names, that will not be shadowed by recursive macro invocations. It is effectively a
C_CONCATENATE
of both arguments, but also provides a globally separated prefix and makes the code better readable.The second argument is optional. If not given,
__LINE__
is implied, and as such the macro will generate the same identifier if used multiple times on the same code-line (or within a macro). This should be used if recursive calls into the macro are not expected. In fact, no argument is necessary in this case, as a mereC_VAR
will evaluate to a valid variable name.This helper may be used by macro implementations that might reasonable well be called in a stacked fasion, like:
c_max(foo, c_max(bar, baz))
Such a stacked call of
c_max()
might cause compiler warnings of shadowed variables in the definition ofc_max()
. By usingC_VAR()
, such warnings can be silenced as each evaluation ofc_max()
uses unique variable names.- Returns:
This evaluates to a constant identifier.
Generic Standard Library Utilities
The C Standard Library lacks some crucial and basic support functions. This section describes the set of helpers provided as extension to the standard library.
-
c_assume_aligned(_ptr, _alignment, _offset)
Hint alignment to compiler
- Parameters:
_ptr – Pointer to provide alignment hint for
_alignment – Alignment in bytes
_offset – Misalignment offset
This hints to the compiler that _ptr - _offset is aligned to the alignment specified in _alignment.
On platforms without support for __builtin_assume_aligned() this is a no-op.
- Returns:
_ptr is returned.
-
c_assert(_x)
Runtime assertions
- Parameters:
_x – Result of an expression
This function behaves like the standard
assert(3)
macro. That is, ifNDEBUG
is defined, it is a no-op. In all other cases it will assert that the result of the passed expression is true.Unlike the standard
assert(3)
macro, this function always evaluates its argument. This means side-effects will always be evaluated! However, if the macro is used with constant expressions, the compiler will be able to optimize it away.
-
int c_errno(void)
Return valid errno
This helper should be used to silence warnings if you know
errno
is valid (ie.,errno
is greater than 0). Instead ofreturn -errno;
, usereturn -c_errno();
It will suppress bogus warnings in case the compiler assumeserrno
might be 0 (or smaller than 0) and thus the caller’s error-handling might not be triggered.This helper should be avoided whenever possible. However, occasionally we really want to silence warnings (especially with static/inline functions). In those cases, the compiler usually cannot deduce that some error paths are guaranteed to be taken. Hence, making the return value explicit allows it to better optimize the code.
Note that you really should never use this helper to work around broken libc calls or syscalls, not setting ‘errno’ correctly.
- Returns:
Positive error code is returned.
-
void *c_memset(void *p, int c, size_t n)
Fill memory region with constant byte
- Parameters:
p – Pointer to memory region, if non-empty
c – Value to fill with
n – Size of the memory region in bytes
This function works like
memset(3)
ifn
is non-zero. Ifn
is zero, this function is a no-op. Therefore, unlikememset(3)
it is safe to call this function withNULL
asp
ifn
is 0.- Returns:
p
is returned.
-
void *c_memzero(void *p, size_t n)
Clear memory area
- Parameters:
p – Pointer to memory region, if non-empty
n – Size of the memory region in bytes
Clear a memory area to 0. If the memory area is empty, this is a no-op. Similar to
c_memset()
, this function allowsp
to beNULL
if the area is empty.- Returns:
p
is returned.
-
void *c_memcpy(void *dst, const void *src, size_t n)
Copy memory area
- Parameters:
dst – Pointer to target area
src – Pointer to source area
n – Length of area to copy
Copy the memory of size
n
fromsrc
todst
, just asmemcpy(3)
does, except this function allows either to beNULL
ifn
is zero. In the latter case, the operation is a no-op.- Returns:
p
is returned.
-
int c_memcmp(const void *s1, const void *s2, size_t n)
Compare memory areas
- Parameters:
s1 – Pointer to one area
s2 – Pointer to other area
n – Length of area to compare
Compare the memory of size
n
ofs1
ands2
, just asmemcmp(3)
does, except this function allows either to beNULL
ifn
is zero.- Returns:
Comparison result for ordering is returned.
Memory Access
This section provides helpers to read and write arbitrary memory locations. They are carefully designed to follow all language restrictions and thus work with strict-aliasing and alignment rules.
The C language does not allow aliasing an object with a pointer of an incompatible type (with few exceptions). Furthermore, memory access must be aligned. This function uses exceptions in the language to circumvent both restrictions.
Note that pointer-offset calculations should avoid exceeding the extents of the object, even if the object is surrounded by other objects. That is, ptr+offset should point to the same object as ptr. Otherwise, pointer provenance will have to be considered.
-
uint8_t c_load_8(const void *memory, size_t offset)
Read a u8 from memory
- Parameters:
memory – Memory location to operate on
offset – Offset in bytes from the pointed memory location
This reads an unsigned 8-bit integer at the offset of the specified memory location.
- Returns:
The read value is returned.
-
uint16_t c_load_16be_unaligned(const void *memory, size_t offset)
Read an unaligned big-endian u16 from memory
- Parameters:
memory – Memory location to operate on
offset – Offset in bytes from the pointed memory location
This reads an unaligned big-endian unsigned 16-bit integer at the offset of the specified memory location.
- Returns:
The read value is returned.
-
uint16_t c_load_16be_aligned(const void *memory, size_t offset)
Read an aligned big-endian u16 from memory
- Parameters:
memory – Memory location to operate on
offset – Offset in bytes from the pointed memory location
This reads an aligned big-endian unsigned 16-bit integer at the offset of the specified memory location.
- Returns:
The read value is returned.
-
uint16_t c_load_16le_unaligned(const void *memory, size_t offset)
Read an unaligned little-endian u16 from memory
- Parameters:
memory – Memory location to operate on
offset – Offset in bytes from the pointed memory location
This reads an unaligned little-endian unsigned 16-bit integer at the offset of the specified memory location.
- Returns:
The read value is returned.
-
uint16_t c_load_16le_aligned(const void *memory, size_t offset)
Read an aligned little-endian u16 from memory
- Parameters:
memory – Memory location to operate on
offset – Offset in bytes from the pointed memory location
This reads an aligned little-endian unsigned 16-bit integer at the offset of the specified memory location.
- Returns:
The read value is returned.
-
uint32_t c_load_32be_unaligned(const void *memory, size_t offset)
Read an unaligned big-endian u32 from memory
- Parameters:
memory – Memory location to operate on
offset – Offset in bytes from the pointed memory location
This reads an unaligned big-endian unsigned 32-bit integer at the offset of the specified memory location.
- Returns:
The read value is returned.
-
uint32_t c_load_32be_aligned(const void *memory, size_t offset)
Read an aligned big-endian u32 from memory
- Parameters:
memory – Memory location to operate on
offset – Offset in bytes from the pointed memory location
This reads an aligned big-endian unsigned 32-bit integer at the offset of the specified memory location.
- Returns:
The read value is returned.
-
uint32_t c_load_32le_unaligned(const void *memory, size_t offset)
Read an unaligned little-endian u32 from memory
- Parameters:
memory – Memory location to operate on
offset – Offset in bytes from the pointed memory location
This reads an unaligned little-endian unsigned 32-bit integer at the offset of the specified memory location.
- Returns:
The read value is returned.
-
uint32_t c_load_32le_aligned(const void *memory, size_t offset)
Read an aligned little-endian u32 from memory
- Parameters:
memory – Memory location to operate on
offset – Offset in bytes from the pointed memory location
This reads an aligned little-endian unsigned 32-bit integer at the offset of the specified memory location.
- Returns:
The read value is returned.
-
uint64_t c_load_64be_unaligned(const void *memory, size_t offset)
Read an unaligned big-endian u64 from memory
- Parameters:
memory – Memory location to operate on
offset – Offset in bytes from the pointed memory location
This reads an unaligned big-endian unsigned 64-bit integer at the offset of the specified memory location.
- Returns:
The read value is returned.
-
uint64_t c_load_64be_aligned(const void *memory, size_t offset)
Read an aligned big-endian u64 from memory
- Parameters:
memory – Memory location to operate on
offset – Offset in bytes from the pointed memory location
This reads an aligned big-endian unsigned 64-bit integer at the offset of the specified memory location.
- Returns:
The read value is returned.
-
uint64_t c_load_64le_unaligned(const void *memory, size_t offset)
Read an unaligned little-endian u64 from memory
- Parameters:
memory – Memory location to operate on
offset – Offset in bytes from the pointed memory location
This reads an unaligned little-endian unsigned 64-bit integer at the offset of the specified memory location.
- Returns:
The read value is returned.
-
uint64_t c_load_64le_aligned(const void *memory, size_t offset)
Read an aligned little-endian u64 from memory
- Parameters:
memory – Memory location to operate on
offset – Offset in bytes from the pointed memory location
This reads an aligned little-endian unsigned 64-bit integer at the offset of the specified memory location.
- Returns:
The read value is returned.
-
c_load(_type, _endian, _aligned, _memory, _offset)
Read from memory
- Parameters:
_type – Datatype to read
_endian – Endianness
_aligned – Aligned or unaligned access
_memory – Memory location to operate on
_offset – Offset in bytes from the pointed memory location
This reads a value of the same size as _type at the offset of the specified memory location. _endian must be either be or le, _aligned must be either aligned or unaligned.
This is a generic macro that maps to the respective c_load_*() function.
- Returns:
The read value is returned.
Generic Destructors
A set of destructors is provided which extends standard library destructors to adhere to some adjuvant rules. In particular, they return an invalid value of the particular object, rather than void. This allows direct assignment to any member-field and/or variable they are defined in, like:
foo = c_free(foo);
foo->bar = c_fclose(foo->bar);
Furthermore, all those destructors can be safely called with the “INVALID” value as argument, and they will be a no-op.
-
void *c_free(void *p)
Destructor-wrapper for free()
- Parameters:
p – Value to pass to destructor, or NULL
Wrapper around
free()
, but always returnsNULL
.- Returns:
NULL is returned.
-
FILE *c_fclose(FILE *f)
Destructor-wrapper for fclose()
- Parameters:
f – File handle to pass to destructor, or NULL
Wrapper around
fclose()
, but a no-op ifNULL
is passed. Always returnsNULL
.- Returns:
NULL is returned.
Generic Cleanup Helpers
A set of helpers that aid in creating functions suitable for use with
_c_cleanup_()
. Furthermore, a collection of predefined cleanup
functions of a set of standard library objects ready for use with
_c_cleanup_()
.
Those cleanup helpers are always suffixed with a p
.
The helpers that are provided are:
c_freep()
: Wrapper aroundc_free()
.c_fclosep()
: Wrapper aroundc_fclose()
.
-
C_DEFINE_CLEANUP(_type, _func)
Define cleanup helper
- Parameters:
_type – Type of object to cleanup
_func – Destructor of the respective type
Define a C static inline function that takes a single argument of type _type and calls _func on it, if its dereferenced value of its argument evaluates to true. Otherwise, it is a no-op.
This macro allows for very simple and fast creation of cleanup helpers for use with
_c_cleanup_()
, based on any destructor and type you provide to it.
-
C_DEFINE_DIRECT_CLEANUP(_type, _func)
Define direct cleanup helper
- Parameters:
_type – Type of object to cleanup
_func – Destructor of the respective type
This works like
C_DEFINE_CLEANUP()
but does not check the dereferenced value of its argument. It always unconditionally invokes the destructor.
GNUC Compiler Attributes
The GCC compiler uses the __attribute__((__xyz__()))
syntax to annotate
language entities with special attributes. Aliases are provided by this
header which map one-to-one to the respective compiler attributes.
These attributes are not supported by all compilers, but are always provided by this header. They are pre-processor macros and do not affect the compilation, unless used. Note that most compilers support these, not just GCC.
-
_c_cleanup_(_x)
Cleanup attribute
- Parameters:
_x – Cleanup function to use
Alias for
__attribute__((__cleanup__(_x)))
.
-
_c_const_
Const attribute
Alias for
__attribute__((__const__))
.
-
_c_deprecated_
Deprecated attribute
Alias for
__attribute__((__deprecated__))
.
Hidden attribute
Alias for
__attribute__((__visibility__("hidden")))
.
-
_c_packed_
Packed attribute
Alias for
__attribute__((__packed__))
.
-
_c_printf_(_a, _b)
Printf attribute
- Parameters:
_a – Format expression argument index
_b – First format-parameter argument index
Alias for
__attribute__((__format__(printf, _a, _b)))
.
-
_c_pure_
Pure attribute
Alias for
__attribute__((__pure__))
.
-
_c_sentinel_
Sentinel attribute
Alias for
__attribute__((__sentinel__))
.
-
_c_unused_
Unused attribute
Alias for
__attribute__((__unused__))
.
GNUC-Specific Utility Macros
A set of utility macros is provided which aids in creating safe macros suitable for use in other pre-processor statements as well as in C expressions.
-
C_EXPR_ASSERT(_expr, _assertion, _message)
Create expression with assertion
- Parameters:
_expr – Expression to evaluate to
_assertion – Arbitrary assertion
_message – Message associated with the assertion
This macro simply evaluates to
_expr
. That is, it can be used in any context that expects an expression like_expr
. Additionally, it takes an assertion as_assertion
and evaluates it through_Static_assert()
, using_message
as debug message.The
_Static_assert()
builtin of C11 is defined as statement and thus cannot be used in expressions. This macro circumvents this restriction.- Returns:
Evaluates to
_expr
.
-
C_CC_MACRO1(_call, _x1, ...)
Provide safe environment to a macro
- Parameters:
_call – Macro to call
_x1 – First argument
... – Further arguments to forward unmodified to
_call
This function simplifies the implementation of macros. Whenever you implement a macro, provide the internal macro name as
_call
and its argument as_x1
. Inside of your internal macro, you…are safe against multiple evaluation errors, since
C_CC_MACRO1
will store the initial parameters in temporary variables.support constant folding, as
C_CC_MACRO1
takes care to invoke your macro with the original values, if they are compile-time constant.have unique variable names for recursive callers and will not run into variable-shadowing-warnings accidentally.
have properly typed arguments as
C_CC_MACRO1
stores the original arguments in an__auto_type
temporary variable.
- Returns:
Result of
_call
is returned.
-
C_CC_MACRO2(_call, _x1, _x2, ...)
Provide safe environment to a macro
- Parameters:
_call – Macro to call
_x1 – First argument
_x2 – Second argument
... – Further arguments to forward unmodified to
_call
This is the 2-argument equivalent of
C_CC_MACRO1()
.- Returns:
Result of
_call
is returned.
-
C_CC_MACRO3(_call, _x1, _x2, _x3, ...)
Provide safe environment to a macro
- Parameters:
_call – Macro to call
_x1 – First argument
_x2 – Second argument
_x3 – Third argument
... – Further arguments to forward unmodified to
_call
This is the 3-argument equivalent of
C_CC_MACRO1()
.- Returns:
Result of
_call
is returned.
Standard Library Utilities
The C Standard Library lacks some crucial and basic support functions. This section describes the set of helpers provided as extension to the standard library.
-
C_ARRAY_SIZE(_x)
Calculate number of array elements at compile time
- Parameters:
_x – Array to calculate size of
- Returns:
Evaluates to a constant integer expression.
-
C_DECIMAL_MAX(_arg)
Calculate maximum length of a decimal representation
- Parameters:
_type – Integer variable/type
This calculates the bytes required for the decimal representation of an integer of the given type. It accounts for a possible +/- prefix, but it does NOT include the trailing terminating zero byte.
- Returns:
Evaluates to a constant integer expression
-
c_container_of(_ptr, _type, _member)
Cast a member of a structure out to the containing type
- Parameters:
_ptr – Pointer to the member or NULL
_type – Type of the container struct this is embedded in
_member – Name of the member within the struct
This uses
offsetof(3)
to turn a pointer to a structure-member into a pointer to the surrounding structure.- Returns:
Pointer to the surrounding object.
-
c_max(_a, _b)
Compute maximum of two values
- Parameters:
_a – Value A
_b – Value B
Calculate the maximum of both passed values. Both arguments are evaluated exactly once, under all circumstances. Furthermore, if both values are constant expressions, the result will be constant as well.
The comparison of their values is performed with the types given by the caller. It is the caller’s responsibility to convert them to suitable types if necessary.
- Returns:
Maximum of both values is returned.
-
c_min(_a, _b)
Compute minimum of two values
- Parameters:
_a – Value A
_b – Value B
Calculate the minimum of both passed values. Both arguments are evaluated exactly once, under all circumstances. Furthermore, if both values are constant expressions, the result will be constant as well.
The comparison of their values is performed with the types given by the caller. It is the caller’s responsibility to convert them to suitable types if necessary.
- Returns:
Minimum of both values is returned.
-
c_less_by(_a, _b)
Calculate clamped difference of two values
- Parameters:
_a – Minuend
_b – Subtrahend
Calculate
_a - _b
, but clamp the result to 0. Both arguments are evaluated exactly once, under all circumstances. Furthermore, if both values are constant expressions, the result will be constant as well.The comparison of their values is performed with the types given by the caller. It is the caller’s responsibility to convert them to suitable types if necessary.
- Returns:
This computes
_a - _b
, if_a > _b
. Otherwise, 0 is returned.
-
c_clamp(_x, _low, _high)
Clamp value to lower and upper boundary
- Parameters:
_x – Value to clamp
_low – Lower boundary
_high – Higher boundary
This clamps
_x
to the lower and higher bounds given as_low
and_high
. All arguments are evaluated exactly once, and yield a constant expression if all arguments are constant as well.The comparison of their values is performed with the types given by the caller. It is the caller’s responsibility to convert them to suitable types if necessary.
- Returns:
Clamped integer value.
-
c_div_round_up(_x, _y)
Calculate integer quotient but round up
- Parameters:
_x – Dividend
_y – Divisor
Calculates
x / y
but rounds up the result to the next integer. All arguments are evaluated exactly once, and yield a constant expression if all arguments are constant.Note:
(x + y - 1) / y
suffers from an integer overflow, even though the computation should be possible in the given type. Therefore, we usex / y + !!(x % y)
. Note that on most CPUs a division returns both the quotient and the remainder, so both should be equally fast. Furthermore, if the divisor is a power of two, the compiler will optimize it, anyway.The operationsare performed with the types given by the caller. It is the caller’s responsibility to convert the arguments to suitable types if necessary.
- Returns:
The quotient is returned.
-
c_align_to(_val, _to)
Align value to a multiple
- Parameters:
_val – Value to align
_to – Align to multiple of this
This aligns
_val
to a multiple of_to
. If_val
is already a multiple of_to
,_val
is returned unchanged. This function operates within the boundaries of the type of_val
and_to
. Make sure to cast them if needed.The arguments of this macro are evaluated exactly once. If both arguments are a constant expression, this also yields a constant return value.
Note that
_to
must be a power of 2, otherwise the behavior will not match expectations.- Returns:
_val
aligned to a multiple of_to
.
Guaranteed Unix Includes
c-stdaux-unix includes a set of Unix headers. All those includes are guaranteed and part of the API. See the actual header for a comprehensive list.
Common Unix Destructors
A set of destructors is provided which extends standard library destructors to adhere to some adjuvant rules. In particular, they return an invalid value of the particular object, rather than void. This allows direct assignment to any member-field and/or variable they are defined in, like:
foo->bar = c_close(foo->bar);
Furthermore, all those destructors can be safely called with the “INVALID” value as argument, and they will be a no-op.
-
int c_close(int fd)
Destructor-wrapper for close()
- Parameters:
fd – File-descriptor to pass to destructor, or negative value
Wrapper around
close()
, but a no-op if a negative value is provided. Always returns-1
.- Returns:
-1 is returned.
-
DIR *c_closedir(DIR *d)
Destructor-wrapper for closedir()
- Parameters:
d – Directory handle to pass to destructor, or NULL
Wrapper around
closedir()
, but a no-op ifNULL
is passed. Always returnsNULL
.- Returns:
NULL is returned.
Common Cleanup Helpers
A set of helpers that aid in creating functions suitable for use with
_c_cleanup_()
. Furthermore, a collection of predefined cleanup
functions of a set of standard library objects ready for use with
_c_cleanup_()
.
Those cleanup helpers are always suffixed with a p
.
The helpers that are provided are:
c_closep()
: Wrapper aroundc_close()
.c_closedirp()
: Wrapper aroundc_closedir()
.