[gelöst] Template-Methode erzeugt Linkerfehler



  • Ich habe eine Template-Methode geschrieben, die die Bytes einer übergebenen Variablen in MSB-Reihenfolge in einen Puffer schreiben soll:

    // add() - add a single data element MSB first to MM_data. Returns updated MM_index or 0
    template <class T> uint16_t ModbusMessage::add(T v) {
      uint16_t sz = sizeof(v);    // Size of value to be added
    
      // Will it fit?
      if (MM_data && sz <= (MM_len - MM_index)) {
        // Yes. Copy it MSB first
        while (sz) {
          sz--;
          MM_data[MM_index++] = (v >> (sz << 3)) & 0xFF;
        }
        // Return updated MM_index (logical length of message so far)
        return MM_index;
      }
      // No, will not fit - return 0
      return 0;
    }
    

    Ich hatte das unter Lubuntu mal ausprobiert und es schien zu klappen. Der Code dazu (die Klassenhierarchie ist irrelevant, damit hatte ich was anderes ausprobiert - es geht nur um die add()-Methode):

    #include <iostream>
    #include <iomanip>
    
    using std::cout;
    using std::endl;
    using std::setw;
    using std::setfill;
    using std::hex;
    
    class A {
    public:
      uint8_t *getData() { return buffer; }
      uint16_t getLength() { return index; }
      template <class T> uint16_t add(T a) {
        if(buffer && index<length) {
          uint8_t sz = sizeof(a);
          while(sz && index<length) {
            sz--;
            buffer[index++] = (a>>(sz<<3))&0xFF;
          }
        }
        return index;
      }
          
    protected:
      ~A() { if(buffer) delete[] buffer; }
      virtual void doNotInstantiate() = 0;
      uint8_t *buffer;
      uint16_t length;
      uint16_t index;
      A(uint8_t sid, uint8_t fc, uint16_t a, uint16_t w) {
        buffer = new uint8_t[6];
        length = 6;
        index = 0;
        add(sid);
        add(fc);
        add(a);
        add(w);
      }
    };
    
    class B : public A {
    public:
      explicit B(uint8_t sid, uint8_t fc, uint16_t a, uint16_t w, uint32_t t) :
        A(sid, fc, a, w) {
        token = t;
      }
    protected:
      void doNotInstantiate() { return; }
      uint32_t token;
    };
    
    int main()
    {
      B bb(1, 0x03, 12, 6, 0xdeadbeef);
    
      uint8_t *cp = bb.getData();
      uint16_t cl = bb.getLength();
    
      while(cl--)
      {
        cout << hex << setw(2) << setfill('0') << (unsigned int)*cp << ' ';
        cp++;
      }
      cout << endl;
    
      return 0;
    }
    

    Ausgabe davon ist wie erwartet:

    01 03 00 0c 00 06
    

    Jetzt baue ich die oben angegebene Methode in der arduino-esp32-Umgebung und da wirft der Linker(!) diese Fehler:

    Linking .pio\build\az-delivery-devkit-v4\firmware.elf
    .pio\build\az-delivery-devkit-v4\lib9a5\libTheBase.a(ModbusMessageRTU.cpp.o):(.literal._ZN10RTURequest16createRTURequestERN12ModbusClient5ErrorEhhttj+0x0): undefined reference to `unsigned short ModbusMessage::add<unsigned char>(unsigned char)'
    .pio\build\az-delivery-devkit-v4\lib9a5\libTheBase.a(ModbusMessageRTU.cpp.o):(.literal._ZN10RTURequest16createRTURequestERN12ModbusClient5ErrorEhhttj+0x4): undefined reference to `unsigned short ModbusMessage::add<unsigned short>(unsigned short)'
    .pio\build\az-delivery-devkit-v4\lib9a5\libTheBase.a(ModbusMessageRTU.cpp.o): In function `RTURequest::createRTURequest(ModbusClient::Error&, unsigned char, unsigned char, unsigned short, unsigned short, unsigned int)':
    C:\Users\Micha\Documents\PlatformIO\Projects\Unibridge/lib\TheBase\src/ModbusMessageRTU.cpp:204: undefined reference to `unsigned short ModbusMessage::add<unsigned char>(unsigned char)'
    C:\Users\Micha\Documents\PlatformIO\Projects\Unibridge/lib\TheBase\src/ModbusMessageRTU.cpp:204: undefined reference to `unsigned short ModbusMessage::add<unsigned char>(unsigned char)'
    C:\Users\Micha\Documents\PlatformIO\Projects\Unibridge/lib\TheBase\src/ModbusMessageRTU.cpp:204: undefined reference to `unsigned short ModbusMessage::add<unsigned short>(unsigned 
    short)'
    C:\Users\Micha\Documents\PlatformIO\Projects\Unibridge/lib\TheBase\src/ModbusMessageRTU.cpp:204: undefined reference to `unsigned short ModbusMessage::add<unsigned short>(unsigned 
    short)'
    collect2.exe: error: ld returned 1 exit status
    *** [.pio\build\az-delivery-devkit-v4\firmware.elf] Error 1
    

    Beide Aufrufe mit uint8_t (aka unsigned char) und uint16_t (aka unsigned short) werden also moniert.
    Die Aufrufe sehen so aus (RTURequest ist über eine Zwischenklasse von ModbusMessage abgeleitet und erbt so add()):

    RTURequest *RTURequest::createRTURequest(Error& returnCode, uint8_t serverID, uint8_t functionCode, uint16_t count, uint8_t *arrayOfBytes, uint32_t token) {
      RTURequest *returnPtr = nullptr;
      // Check parameter for validity
      returnCode = checkData(serverID, functionCode, count, arrayOfBytes);
      // No error? 
      if (returnCode == SUCCESS)
      {
        // Yes, all fine. Create new RTURequest instance
        returnPtr = new RTURequest(2 + count, token);
        returnPtr->add(serverID);
        returnPtr->add(functionCode);
        for (uint8_t i = 0; i < count; ++i) {
          returnPtr->add(arrayOfBytes[i]);
        }
        returnPtr->CRC = RTUCRC::calcCRC(returnPtr->MM_data, returnPtr->MM_index);
      }
      return returnPtr;
    }
    

    Warum nur? Ich kann mir keinen Reim darauf machen.



  • Argh. Der Depp wieder vor dem Keyboard...

    Die Lösung ist, dass ich die Template-Methode natürlich im Headerfile definieren muss, nicht im CPP-File, weil der Compiler die Varianten erzeugen muss, nicht der Linker.

    Sorry für die Störung! 😳



  • @Miq sagte in [gelöst] Template-Methode erzeugt Linkerfehler:

    Argh. Der Depp wieder vor dem Keyboard...

    Die Lösung ist, dass ich die Template-Methode natürlich im Headerfile definieren muss, nicht im CPP-File, weil der Compiler die Varianten erzeugen muss, nicht der Linker.

    Sorry für die Störung! 😳

    Das ist glaube jedem schon mal passiert. Ich kenne das jedenfalls gut 😆


Log in to reply