1

I declare the following template class on my .h file

enum class Hal_uart_id
{
    Uart = 0,
    Usart0,
    Usart1,
    Usart2,
    Usart3,
    UsbUart,
};

template <class T> 
class HalUART
{
    public:
        HalUART(Hal_uart_id uart_id);
    private:
        Hal_uart_id _uart_id;
        T* _p_uart; 
}

On my .cpp file I implement the class constructor as follows:

#include "hal_uart_common.h"
#include "Arduino.h"

template <class T> 
HalUART<T>::HalUART(Hal_uart_id id)
{
    _uart_id = id;
    switch (_uart_id)
    {
        case Hal_uart_id::Uart:
            _p_uart = &Serial;
            break;
        case Hal_uart_id::UsbUart:
            _p_uart = &SerialUSB;
            break;
        case Hal_uart_id::Usart0:
            _p_uart = &Serial1;
            break;
        case Hal_uart_id::Usart1:
            _p_uart = &Serial2;
            break;
        case Hal_uart_id::Usart3:
            _p_uart = &Serial3;
            break;
        default:
            break;
    }
}

At the end of my .cpp file I instantiate the template class with the USARTClass class

template class HalUART<USARTClass>;

I am getting the following compilation error and I cannot understand why or how to fix it:

src/hal/uart/hal_uart_sam3x.cpp: In instantiation of 'HalUART<T>::HalUART(Hal_uart_id) [with T = USARTClass]':
src/hal/uart/hal_uart_sam3x.cpp:58:16:   required from here
src/hal/uart/hal_uart_sam3x.cpp:23:21: error: invalid conversion from 'UARTClass*' to 'USARTClass*' [-fpermissive]
             _p_uart = &Serial;
             ~~~~~~~~^~~~~~~~~
src/hal/uart/hal_uart_sam3x.cpp:26:21: error: cannot convert 'Serial_*' to 'USARTClass*' in assignment
             _p_uart = &SerialUSB;
             ~~~~~~~~^~~~~~~~~~~~
*** [.pio/build/due/src/hal/uart/hal_uart_sam3x.cpp.o] Error 1

These object are defined in the Arduino core

UARTClass Serial;
USARTClass Serial1;
USARTClass Serial2;
USARTClass Serial3;
Serial_ SerialUSB;

For UART/USART class and objects definitions please see: https://github.com/arduino/ArduinoCore-sam/blob/master/cores/arduino/UARTClass.h https://github.com/arduino/ArduinoCore-sam/blob/master/cores/arduino/UARTClass.cpp

https://github.com/arduino/ArduinoCore-sam/blob/master/cores/arduino/USARTClass.h https://github.com/arduino/ArduinoCore-sam/blob/master/cores/arduino/USARTClass.cpp

https://github.com/arduino/ArduinoCore-sam/blob/master/cores/arduino/USB/USBAPI.h https://github.com/arduino/ArduinoCore-sam/blob/master/cores/arduino/USB/CDC.cpp

Santos
  • 37
  • 7
  • You are most probably mixing types of inherited and inheriting objects so that your pointer assignments are illegal. However to be sure I need definitions of Serial, SerialUSB and other used in the constructor and also USARTClass. – Fëamarto Sep 16 '19 at 13:45
  • Related: [Why can templates only be implemented in the header file?](https://stackoverflow.com/q/495021/) – L. F. Sep 16 '19 at 13:47
  • 2
    Please provide a [mre]. You don't provide the definition of `Serial`, so we can't really say for sure what it is. – L. F. Sep 16 '19 at 13:48
  • @L.F.: unrelated to template in cpp, as OP instantiates explicitly the class. – Jarod42 Sep 16 '19 at 13:50
  • @Jarod42 It is possible that the OP uses this template class in another translation unit, right? – L. F. Sep 16 '19 at 13:51
  • How are `UARTClass*` and `USARTClass*` related? – Jarod42 Sep 16 '19 at 13:51
  • @L.F.: Yes, and thanks to `template class HalUART;`, it would be usable for that type. Strange thing is if it is the only instantiation, then template would be the wrong tool. – Jarod42 Sep 16 '19 at 13:54
  • Sorry for the lack of detail, I thought it was not needed. I have updated my OP to provide requested definitions. Thank you for the quick replies – Santos Sep 16 '19 at 14:05
  • @Jarod42 USARTClass inherits from UARTClass class USARTClass : public UARTClass – Santos Sep 16 '19 at 14:09
  • @Jarod42 My objective is to instantiate the template with UARTClass and Serial_ as well, however, for simplicity, I have not included those in the OP – Santos Sep 16 '19 at 14:11
  • It seems you don't need template, and have only `UARTClass` (the base class) pointer. – Jarod42 Sep 16 '19 at 14:17
  • He also needs `Serial_` – João Paulo Sep 16 '19 at 14:20
  • @Jarod42 I also plan to use the template with other types in the future, apart from the ones mentioned – Santos Sep 16 '19 at 14:23
  • Currently `T` should be a base of all of your global. `template HalUART` might make sense though. – Jarod42 Sep 16 '19 at 14:28

1 Answers1

1

Template types must be solved at compiler time. Instantiate your class with template class HalUART<UARTClass>; instead of USARTClass (once class USARTClass : public UARTClass;). And, also, in the Serial_ case, for instance, use if constexpr (c++17 only) with std::is_same:

case Hal_uart_id::UsbUart:
    if constexpr (std::is_same_v<T, Serial_>)
        _p_uart = &SerialUSB;
    break;

I recommend using if constexpr for the other cases too, once you instantiate your class with Serial_, for example, then you'll also be able to template class HalUART<USARTClass>;:

switch (_uart_id)
{
case Hal_uart_id::Uart:
    if constexpr (std::is_same_v<UARTClass, T>)
        _p_uart = &Serial;
    break;
case Hal_uart_id::UsbUart:
    if constexpr (std::is_same_v<T, Serial_>)
        _p_uart = &SerialUSB;
    break;
case Hal_uart_id::Usart0:
    if constexpr (std::is_same_v<USARTClass, T>)
        _p_uart = &Serial1;
    break;
case Hal_uart_id::Usart1:
    if constexpr (std::is_same_v<USARTClass, T>)
        _p_uart = &Serial2;
    break;
case Hal_uart_id::Usart3:
    if constexpr (std::is_same_v<USARTClass, T>)
        _p_uart = &Serial3;
    break;
default:
    break;
}

With this type checking, perhaps your switch may no longer be necessary. Try to evaluate this.

Consider using std::is_base_of if necessary.


If c++17 is not available, try specializing your function:

template <class T>
HalUART<T>::HalUART(Hal_uart_id id) {}

template <>
HalUART<UARTClass>::HalUART(Hal_uart_id id) {
    if (id == Hal_uart_id::Uart)
        _p_uart = &Serial;
}
template <>
HalUART<Serial_>::HalUART(Hal_uart_id id) {
    if (id == Hal_uart_id::UsbUart)
        _p_uart = &SerialUSB;
}
template <>
HalUART<USARTClass>::HalUART(Hal_uart_id id) {
    if (id == Hal_uart_id::Usart0)
        _p_uart = &Serial1;
    else if (id == Hal_uart_id::Usart1)
        _p_uart = &Serial2;
    else if (id == Hal_uart_id::Usart3)
        _p_uart = &Serial3;
}

live example

João Paulo
  • 6,300
  • 4
  • 51
  • 80
  • Thanks, this solution works for C++ 11, but unfortunately this is an embedded system and C++ 11 features as std::is_same_v are not supported Hopefully there is some other way of doing it? – Santos Sep 16 '19 at 14:48
  • actually for C++17. You can workaround this specializing your class. Check the edit. – João Paulo Sep 16 '19 at 15:03
  • That solution works perfect. I was actually investigating on how to specialize template classes properly, but you also have done that for me. Really appreciated – Santos Sep 16 '19 at 15:23