Dmitrey
Dmitrey

Reputation: 31

Why template specialization doesn't work in gcc

I'd like to write typelist methods to operate with microcontrollers GPIO's.

I'd like to create list of GPIO's and select only pins of specific port. So, GetPinWithPort template has specialisation which checks provided type.

template <typename... Ts>
struct tlist
{
    using type = tlist;
};

template <typename T> class debug_t;

#define MAKE_PORT(NAME, ID)\
    class NAME\
    {\
        public:\
        static void Set(uint32_t v) { };\
        static void Reset(uint32_t v) { };\
        enum { id = ID };\
    };

MAKE_PORT(Porta, 'A');
MAKE_PORT(Portb, 'B');

template <class PORT, uint8_t PIN>
class TPin
{
public:
    static void Set() { PORT::Set(1 << PIN); }
    static void Reset() { PORT::Reset(1 << PIN); }

    typedef PORT port;
    enum { pin = PIN };
};

template <class TPort, class T>
struct GetPinWithPort {
    using type = tlist<>;
};

template <typename TPort, uint32_t N>
struct GetPinWithPort<TPort, TPin<TPort, N>>
{
    using type = TPin<TPort, N>;
};

int main()
{

    using pina = GetPinWithPort<Porta, TPin<Porta, 1> >::type;

    // std::cout << typeid(pina).name() << std::endl;  //Visual Studio gives: class TPin<class Porta,1>
    debug_t<pina> d; //gcc output: tlist<>

}

Visual Studio gives expected result. But gcc - empty list. What is wrong here?

Upvotes: 3

Views: 98

Answers (2)

shargors
shargors

Reputation: 2157

In this line using pina = GetPinWithPort<Porta, TPin<Porta, 1> >::type; 1 has the type of int not uint32_t. So you instantiate GetPinWithPort<Porta, int> (the non-specialized definition), not GetPinWithPort<Porta, uint32_t>.

Here is what the specialization should look like to pass the right template parameter to TPin:

template <typename TPort, uint8_t N>
struct GetPinWithPort<TPort, TPin<TPort, N>>
{
    using type = TPin<TPort, N>;
};

Here is how it should be used:

using pina = GetPinWithPort<Porta, TPin<Porta, static_cast<uint8_t>(1)> >::type;

The reason for this is that C++ is very strict about using types instide of templates: very limited conversion of types is allowed.

Upvotes: 1

Martin Morterol
Martin Morterol

Reputation: 2870

It should be

template <typename TPort, uint8_t N>  // or auto
struct GetPinWithPort<TPort, TPin<TPort, N>>

not

template <typename TPort, uint32_t N>
struct GetPinWithPort<TPort, TPin<TPort, N>>

because (I am not a language lawer, it's just how I understand i):

template <class PORT, uint8_t PIN>
class TPin {}
// and 
using pina = GetPinWithPort<Porta, TPin<Porta, 1> >::type;

In the specialisation gcc have to choose between:

class T

and

TPin<TPort, uint32_t>

and the type it have is:

TPin<Porta, 1>

So may be gcc resolve the TPin<Porta, 1> to TPin<Porta, uint8_t> then fail the specialisation.

Upvotes: 2

Related Questions