3

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?

ShadowRanger
  • 143,180
  • 12
  • 188
  • 271
Dmitrey
  • 31
  • 1
  • You have only declared debug_t not defined it anywhere. – PapaDiHatti May 29 '19 at 13:49
  • 2
    @Kapil That's on purpose. It is a trick to get the compiler to give you an error that gives you the exact type given to the template. – NathanOliver May 29 '19 at 13:51
  • 1
    clang also gives the results I would expect. Not sure what is going on with GCC but changing `template ` to `template ` gets it to work. No idea why it is needed though. – NathanOliver May 29 '19 at 13:52
  • It's crazy that Visual Studio gets the template right and GCC has a weird result. I originally thought that VS must be the wrong one, but if clang also works than maybe this is actually a bug with GCC. – Weak to Enuma Elish May 29 '19 at 13:59
  • @NathanOliver: there is probablyy a dupe with `std::array` when trying to specialise for `std::array` with `N` of type `int` instead of `std::size_t`. – Jarod42 May 29 '19 at 14:33

2 Answers2

2

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.

Martin Morterol
  • 2,560
  • 1
  • 10
  • 15
  • Can you say why it should be so? MSVS and clang both work for `uint32_t`. – NathanOliver May 29 '19 at 14:03
  • I have add some explanation but i am not a language lawyer, it's more how I feel it more than C++ norm or what ever – Martin Morterol May 29 '19 at 14:06
  • Because unsigned int is implementation dependent and not established as a standard. GCC, if I recall correctly, chooses the smallest (storage-wise) type that it can when initializing if you don't specify fixed width at time of creation. – Sean Brookins May 29 '19 at 14:38
1

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.

shargors
  • 2,147
  • 1
  • 15
  • 21
  • If you change the specialization to use an `int` it still does not work: http://coliru.stacked-crooked.com/a/1d795435e7a29d75 – NathanOliver May 29 '19 at 14:34
  • @NathanOliver You are right. I should have tried it myself first. This works: http://coliru.stacked-crooked.com/a/a5e94bc8cc1657ae – shargors May 29 '19 at 14:44