Nilsie
Nilsie

Reputation: 100

Calling functions using conditional template c++

I'm writing an embedded device driver library (Arduino Mega) where code size and performance goes hand in hand.

The SPI controller has a sequence of operation that goes:

  1. Select SPI slave node
  2. Transfer data
  3. Deselect slave node

The select/deselect are the start/stop of a transaction. This is done by asserting a pin either high or low. The SPI slave node circuitry may use standard, non-inverted digital signals (select == high voltage, deselect == ground) or inverted signals (select == ground, deselect == high voltage).

select() can be implemented using a run-time condition for checking if the pin is inverted (then issue a clearPin()) or not (issue a setPin()). The same goes for deselect(). Using consts and static definitions, the compiler should be able to solve the condition at compile time. As it turns out, the compiler (Avr-gcc 4.9) doesn't always realize that these expressions can be reduced (and inlined).

So, to get around this I'm trying to use conditional templates like this:

enum class SelectPinType : const bool
    {
    STANDARD = false,
    INVERTED = true
    };

template <SelectPinType tInvertedSelect>
    struct SelectPin;

template<>
    struct SelectPin<SelectPinType::INVERTED>
        {
        const Pin::IOPin &mChipSelectPin;

        inline void select() const __attribute__((always_inline))
            {
            Pin::clr(mChipSelectPin); // Select inverted pin
            }
        inline void deselect() const __attribute__((always_inline))
            {
            Pin::set(mChipSelectPin); // Deselect inverted pin
            }
        };

template<>
    struct SelectPin<SelectPinType::STANDARD>
        {
        const Pin::IOPin &mChipSelectPin;

        inline void select() const __attribute__((always_inline))
            {
            Pin::set(mChipSelectPin); // Select standard pin
            }
        inline void deselect() const __attribute__((always_inline))
            {
            Pin::clr(mChipSelectPin); // Deselect standard pin
            }
        };

The select() and deselect() can then be used like:

// Uses digital pin # 33 as Select pin
static struct SelectPin<SelectPinType::INVERTED> SpiSel
   {
   Pin::PIN_DIO_33
   };
...
SpiSel.select();
doTransfer(dataBuffer);
SpiSel.deselect();

Everything works fine and the select()/deselect() compiles to less than 5 assembler instructions. However, I can't find a solution of how to pass an instance of the struct SelectPin without having to specify the template condition in the function parameter list. In other words, if I want to wrapp the Select-Transfer-Deselect in a function I have to do the following:

// Inverted select pin transaction
inline void doTransaction(const SelectPin<SelectPinType::INVERTED> &fSelectPin, Spi::Transfer &fTransfer)
        {

        // Perform the SPI transaction
        fSelectPin.select();
        doTransfer(fTransfer);
        fSelectPin.deselect();

        // Done
        return;
        }

// Non-inverted select pin transaction
inline void doTransaction(const SelectPin<SelectPinType::STANDARD> &fSelectPin, Spi::Transfer &fTransfer)
        {

        // Perform the SPI transaction
        fSelectPin.select();
        doTransfer(fTransfer);
        fSelectPin.deselect();

        // Done
        return;
        }

Since I could have written overloaded function for inverted and non-inverted select pins without using templates, i'm basically back to square one.

Is there a way arround this so that I in essence can use the base template as a parameter in a single function definition? Something along the lines of:

// Accept any condition for the SelectPin template
inline void doTransaction(const SelectPin<SelectPinType::*> &fSelectPin, Spi::Transfer &fTransfer)
        {

        // Perform the SPI transaction
        fSelectPin.select();
        doTransfer(fTransfer);
        fSelectPin.deselect();

        // Done
        return;
        }

Note Yes, I realize that 2 overloaded functions would do the job right away but I feel that this may have generic implications besides my crapy select pins.

Upvotes: 0

Views: 97

Answers (1)

Jarod42
Jarod42

Reputation: 217398

The syntax you want is:

template <SelectPinType E>
void doTransaction(const SelectPin<E> &fSelectPin, Spi::Transfer &fTransfer)
{
    // Perform the SPI transaction
    fSelectPin.select();
    doTransfer(fTransfer);
    fSelectPin.deselect();
}

or even shorter, but with less constraint:

template <typename T>
void doTransaction(const T& fSelectPin, Spi::Transfer &fTransfer)
{
    // Perform the SPI transaction
    fSelectPin.select();
    doTransfer(fTransfer);
    fSelectPin.deselect();
}

Upvotes: 1

Related Questions