/* * Copyright (C) 2016 Kaspar Schleiser * * This file is subject to the terms and conditions of the GNU Lesser * General Public License v2.1. See the file LICENSE in the top level * directory for more details. */ /** * @ingroup core_util * @brief Cross File Arrays * @{ * * This macro, in combination with an entry in the linker scripts, allows the * definition of constant arrays to be spread over multiple C compilation * units. These arrays are called "cross-file arrays" or short xfa. * * @experimental This API is considered experimental and will probably change * without notice! * * @file * @author Kaspar Schleiser */ #ifndef XFA_H #define XFA_H /* * Unfortunately, current gcc trips over accessing XFA's because of their * zero-size start/end array that are used of symbol markers, with an "array * index out of bounds" warning. So until a solution for that is found, we * need to disable array bounds checks for files using XFAs. */ #ifndef DOXYGEN _Pragma("GCC diagnostic ignored \"-Warray-bounds\"") #endif /** * @brief helper macro for other XFA_* macros * * @internal */ #define _XFA(name, prio) __attribute__((used, section(".xfa." #name "." #prio))) /** * @brief helper macro for other XFA_* macros * * @internal */ #define _XFA_CONST(name, \ prio) __attribute__((used, \ section(".roxfa." #name "." #prio))) /** * @brief Define a read-only cross-file array * * This macro defines the symbols necessary to use XFA_START() and XFA_END(). * It needs to be part of one single compilation unit. * * The pragmas prevent these errors: * * error: ISO C forbids empty initializer braces * error: ISO C forbids zero-size array ‘xfatest_const_end’ * * @param[in] type name of the cross-file array * @param[in] name name of the cross-file array */ #define XFA_INIT_CONST(type, name) \ _Pragma("GCC diagnostic push") \ _Pragma("GCC diagnostic ignored \"-Wpedantic\"") \ _XFA_CONST(name, 0_) const volatile type name [0] = {}; \ _XFA_CONST(name, 9_) const volatile type name ## _end [0] = {}; \ _Pragma("GCC diagnostic pop") \ extern const unsigned __xfa_dummy /** * @brief Define a writable cross-file array * * This macro defines the symbols necessary to use XFA_START() and XFA_END(). * It needs to be part of one single compilation unit. * * The pragmas prevent these errors: * * error: ISO C forbids empty initializer braces * error: ISO C forbids zero-size array ‘xfatest_end’ * * @param[in] type name of the cross-file array * @param[in] name name of the cross-file array */ #define XFA_INIT(type, name) \ _Pragma("GCC diagnostic push") \ _Pragma("GCC diagnostic ignored \"-Wpedantic\"") \ _XFA(name, 0_) type name [0] = {}; \ _XFA(name, 9_) type name ## _end [0] = {}; \ _Pragma("GCC diagnostic pop") \ extern const unsigned __xfa_dummy /** * @brief Declare an external read-only cross-file array * * This macro defines the symbols necessary to use XFA_START() and XFA_END(). * Think of this as XFA_INIT() but with "extern" keyword. * It is supposed to be used in compilation units where the cross file array is * being accessed, but not defined using XFA_INIT. * * @param[in] type name of the cross-file array * @param[in] name name of the cross-file array */ #define XFA_USE_CONST(type, name) \ extern const type name []; \ extern const type name ## _end [] /** * @brief Declare an external writable cross-file array * * This macro defines the symbols necessary to use XFA_START() and XFA_END(). * Think of this as XFA_INIT() but with "extern" keyword. * It is supposed to be used in compilation units where the cross file array is * being accessed, but not defined using XFA_INIT. * * @param[in] type name of the cross-file array * @param[in] name name of the cross-file array */ #define XFA_USE(type, name) \ extern type name []; \ extern type name ## _end [] /** * @brief Define variable in writable cross-file array * * Variables will end up sorted by prio. * * Add this to the type in a variable definition, e.g.: * * XFA(driver_params, 0) driver_params_t _onboard = { .pin=42 }; * * @param[in] xfa_name name of the xfa * @param[in] prio priority within the xfa */ #define XFA(xfa_name, prio) _XFA(xfa_name, 5_ ## prio) /** * @brief Define variable in read-only cross-file array * * Variables will end up sorted by prio. * * Add this to the type in a variable definition, e.g.: * * XFA(driver_params, 0) driver_params_t _onboard = { .pin=42 }; * * @param[in] xfa_name name of the xfa * @param[in] prio priority within the xfa */ #define XFA_CONST(xfa_name, prio) _XFA_CONST(xfa_name, 5_ ## prio) /** * @brief Add a pointer to cross-file array * * Pointers will end up sorted by prio. * * @note This implementation uses the __typeof__() C extension. * It is available both in GCC and LLVM, and both don't complain with * plain "-std=c11". If deemed necessary, use of __typeof__ can be removed * and the type can be added as parameter, at the cost of much less * convenience. * * @param[in] xfa_name name of the xfa * @param[in] prio priority within the xfa * @param[in] name symbol name * @param[in] entry pointer variable to add to xfa */ #define XFA_ADD_PTR(xfa_name, prio, name, entry) \ _XFA_CONST(xfa_name, 5_ ## prio) \ const __typeof__(entry) xfa_name ## _ ## prio ## _ ## name = entry /** * @brief Calculate number of entries in cross-file array */ #define XFA_LEN(type, \ name) (((const char *)name ## _end - (const char *)name) / \ sizeof(type)) #ifdef __cplusplus extern "C" { #endif /* making externc happy */ #ifdef __cplusplus } #endif #endif /* XFA_H */ /** @} */