[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
(akaunsigned char
) unduint16_t
(akaunsigned short
) werden also moniert.
Die Aufrufe sehen so aus (RTURequest
ist über eine Zwischenklasse vonModbusMessage
abgeleitet und erbt soadd()
):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