Linking error when implement C function in C++ source file

I’m trying to integrate GitHub - min-protocol/min: The MIN protocol specification and reference implementation to my project.

The MIN lib requires to implement the callback

void min_application_handler(uint8_t min_id, uint8_t const *min_payload, uint8_t len_payload, uint8_t port);

I tried to do it in my pc_comm.cpp file, (the feature/include-min branch), but got linking error:

Linking .pio/build/esp-wrover-kit/firmware.elf
/home/quan/.platformio/packages/toolchain-xtensa-esp32@8.4.0+2021r2-patch5/bin/../lib/gcc/xtensa-esp32-elf/8.4.0/../../../../xtensa-esp32-elf/bin/ld: .pio/build/esp-wrover-kit/libb33/libmin-proto.a(min.c.o):(.literal.min_poll+0x0): undefined reference to `min_application_handler'
/home/quan/.platformio/packages/toolchain-xtensa-esp32@8.4.0+2021r2-patch5/bin/../lib/gcc/xtensa-esp32-elf/8.4.0/../../../../xtensa-esp32-elf/bin/ld: .pio/build/esp-wrover-kit/libb33/libmin-proto.a(min.c.o): in function `valid_frame_received':
/home/quan/Works/AgriConnect/Embedded/ebee-esp32/lib/min-proto/min.c:431: undefined reference to `min_application_handler'
collect2: error: ld returned 1 exit status
*** [.pio/build/esp-wrover-kit/firmware.elf] Error 1

Could someone tell me how to fix it?

Note that, the main branch contains a working code, where I put


#include "min.c"

at the end of pc_comm.cpp, but I feel it is ugly. I try to include *.h file only.

If you want a function you write in .cpp file to be normally usable in a .c file, you must declare the function to have C-linkage in your C++ file.

You do that by using extern “C”.

1 Like

@maxgerhardt I already wrap in extern "C" {}:, as seen in pc_comm.h file:

extern "C" {
#include "min.h"
}

The min_application_handler is declared in MIN’s min.h as a placeholder and we need to implement it in our side.

Indeed. But then the missing part is that the function signatures don’t match.

min.h

void min_application_handler(uint8_t min_id, uint8_t const *min_payload, uint8_t len_payload, uint8_t port);

vs in pc_comm.cpp

void min_application_handler(uint8_t min_id, uint8_t *min_payload, uint8_t len_payload, uint8_t port)

so as far as C++ is concerned, these are two different functions due to missing const on min_payload, and the extern "C" linkage does not apply to the function define in pc_comm.cpp. So the function will not be found.

You’ll also have to pull that const through the other functions.

diff --git a/src/pc_comm.cpp b/src/pc_comm.cpp
index 10a532a..16e752a 100644
--- a/src/pc_comm.cpp
+++ b/src/pc_comm.cpp
@@ -35,7 +35,7 @@ uint32_t min_time_ms(void)
 // Handle the reception of a MIN frame. This is the main interface to MIN for receiving
 // frames. It's called whenever a valid frame has been received (for transport layer frames
 // duplicates will have been eliminated).
-void min_application_handler(uint8_t min_id, uint8_t *min_payload, uint8_t len_payload, uint8_t port)
+void min_application_handler(uint8_t min_id, uint8_t const *min_payload, uint8_t len_payload, uint8_t port)
 {
     ESP_LOGI(TAG, "Got MIN frame with ID %d", min_id);
     if (min_id == CMD_CHANGE_WIFI) {
@@ -57,7 +57,7 @@ void min_application_handler(uint8_t min_id, uint8_t *min_payload, uint8_t len_p
 
 /* --- End callbacks --- */
 
-void handleChangeSerialNumber(uint8_t *min_payload, uint8_t len_payload)
+void handleChangeSerialNumber(const uint8_t *min_payload, uint8_t len_payload)
 {
     String newSN = String(min_payload, len_payload);
     newSN.trim();
@@ -69,7 +69,7 @@ void handleChangeSerialNumber(uint8_t *min_payload, uint8_t len_payload)
     pref.end();
 }
 
-void handleChangeFarmCodename(uint8_t *min_payload, uint8_t len_payload)
+void handleChangeFarmCodename(const uint8_t *min_payload, uint8_t len_payload)
 {
     String newName = String(min_payload, len_payload);
     newName.trim();
@@ -81,7 +81,7 @@ void handleChangeFarmCodename(uint8_t *min_payload, uint8_t len_payload)
     pref.end();
 }
 
-void handleChangeWifi(uint8_t *min_payload, uint8_t len_payload)
+void handleChangeWifi(const uint8_t *min_payload, uint8_t len_payload)
 {
     // The JSON document will be an array to 2 strings,
     // each string is in form "ssid:password"
diff --git a/src/pc_comm.h b/src/pc_comm.h
index 94e081e..e40fa4f 100644
--- a/src/pc_comm.h
+++ b/src/pc_comm.h
@@ -9,7 +9,7 @@ uint16_t min_tx_space(uint8_t port);
 void min_tx_byte(uint8_t port, uint8_t byte);
 uint32_t min_time_ms(void);
 
-void handleChangeSerialNumber(uint8_t *min_payload, uint8_t len_payload);
-void handleChangeFarmCodename(uint8_t *min_payload, uint8_t len_payload);
-void handleChangeWifi(uint8_t *min_payload, uint8_t len_payload);
+void handleChangeSerialNumber(const uint8_t *min_payload, uint8_t len_payload);
+void handleChangeFarmCodename(const uint8_t *min_payload, uint8_t len_payload);
+void handleChangeWifi(const uint8_t *min_payload, uint8_t len_payload);
 void saveWifiAuth(JsonArray &creds, const uint8_t slot);

After you git apply that file, you’ll receive

Retrieving maximum program size .pio\build\esp-wrover-kit\firmware.elf
Checking size .pio\build\esp-wrover-kit\firmware.elf
Advanced Memory Usage is available via "PlatformIO Home > Project Inspect"
RAM:   [=         ]  14.1% (used 46348 bytes from 327680 bytes)
Flash: [======    ]  57.7% (used 756389 bytes from 1310720 bytes)
========================================================================================= [SUCCESS] Took 15.69 seconds =========================================================================================

as expected.

Thank you very much, Maximilian!