[En-Nut-Discussion] Using boost::preprocessor (preprocessor metaprogramming)

Thiago A. Corrêa thiago.correa at gmail.com
Wed Feb 11 03:19:16 CET 2009


On Mon, Feb 9, 2009 at 1:16 AM, Henrik Maier <hmnews at proconx.com> wrote:
> Hi Thiago,
>
> Maybe some smaller examples would help to understand the benefits and how
> such a metalanguage can be applied to a problem?
>

Sure.

This snippet from nut/dev/spibus0gpio.c:
/*!
 * \brief Set the specified chip select to a given level.
 */
static GSPIREG *GpioSpi0ChipSelect(uint_fast8_t cs, uint_fast8_t hi)
{
    GSPIREG *rc;

    switch (cs) {
    case 0:
        /* If CS0 is undefined, we assume permanent selection. */
#if defined(SBBI0_CS0_BIT)
        GpioPinSet(SBBI0_CS0_PORT, SBBI0_CS0_BIT, hi);
        GpioPinConfigSet(SBBI0_CS0_PORT, SBBI0_CS0_BIT, GPIO_CFG_OUTPUT);
#endif
        rc = &gspi_reg0;
        break;
#if defined(SBBI0_CS1_BIT)
    case 1:
        GpioPinSet(SBBI0_CS1_PORT, SBBI0_CS1_BIT, hi);
        GpioPinConfigSet(SBBI0_CS1_PORT, SBBI0_CS1_BIT, GPIO_CFG_OUTPUT);
        rc = &gspi_reg1;
        break;
#endif
#if defined(SBBI0_CS2_BIT)
    case 2:
        GpioPinSet(SBBI0_CS2_PORT, SBBI0_CS2_BIT, hi);
        GpioPinConfigSet(SBBI0_CS2_PORT, SBBI0_CS2_BIT, GPIO_CFG_OUTPUT);
        rc = &gspi_reg2;
        break;
#endif
#if defined(SBBI0_CS3_BIT)
    case 3:
        GpioPinSet(SBBI0_CS3_PORT, SBBI0_CS3_BIT, hi);
        GpioPinConfigSet(SBBI0_CS3_PORT, SBBI0_CS3_BIT, GPIO_CFG_OUTPUT);
        rc = &gspi_reg3;
        break;
#endif
    default:
        errno = EIO;
        rc = NULL;
        break;
    }
    return rc;
}

Could become:

/*!
 * \brief Set the specified chip select to a given level.
 */
static GSPIREG *GpioSpi0ChipSelect(uint_fast8_t cs, uint_fast8_t hi)
{
    GSPIREG *rc;

    switch (cs) {
#define BOOST_PP_LOCAL_MACRO( n ) \
    case n: \
        GpioPinSet(SBBI0_CS##n_PORT, SBBI0_CS##n_BIT, hi); \
        GpioPinConfigSet(SBBIO_CS##n_PORT, SBBI0_CS#n_BIT, GPIO_CFG_OUTPUT); \
        rc = &gspi_reg##n; \
        break; \
#define BOOST_PP_LOCAL_LIMITS (0, SBBI0_CS_COUNT)
#define BOOST_PP_LOCAL_ITERATE()

    default:
        errno = EIO;
        rc = NULL;
        break;
    }
    return rc;
}

Provided that we set a SBBI0_CS_COUNT in NutConf. Then, the code would
be generated during compile time for any number of SPI_CS's.
Similarly this could be used for USARTs. In the atmega128 family, we
have chips with 2 (supported) and 4 (only 2 supported) USART's.

In my specific case, the AVR32 port could easily support all of the
UC3A, UC3B and AP700x families, because they are almost the same, with
diferences in peripherals mostly. Such code generation tecnique allows
me to generate interrupt handlers for existing and future chips.
There are also support for tuples, arrays, lists, etc (never used
myself), that could help simplify the configuration preamble of most
drivers, that are repetitive and predictable:

#if (UART1_RTS_AVRPORT == AVRPORTB)
#define UART_RTS_PORT   PORTB
#define UART_RTS_DDR    DDRB

#elif (UART1_RTS_AVRPORT == AVRPORTD)
#define UART_RTS_PORT   PORTD
#define UART_RTS_DDR    DDRD

#elif (UART1_RTS_AVRPORT == AVRPORTE)
#define UART_RTS_PORT   PORTE
#define UART_RTS_DDR    DDRE

#elif (UART1_RTS_AVRPORT == AVRPORTF)
#define UART_RTS_PORT   PORTF
#define UART_RTS_DDR    DDRF

#elif (UART1_RTS_AVRPORT == AVRPORTG)
#define UART_RTS_PORT   PORTG
#define UART_RTS_DDR    DDRG

#elif (UART1_RTS_AVRPORT == AVRPORTH)
#define UART_RTS_PORT   PORTH
#define UART_RTS_DDR    DDRH
#endif


It's very powerfull and mind bogling at times. If Harald is open to
it, I can go ahead and try to produce some working code.

Kind Regards,
    Thiago A. Correa


More information about the En-Nut-Discussion mailing list