0

I have been trying to use this library in my C++ project for some time now and can't seem to figure out how to get around the issue of multiple definitions. The library contains only header files (and implementation header files) so the source folder looks like this:

├── src
│   ├── SomeHeaderFile.h
│   ├── SomeHeaderFile-Impl.h
│   ├── SomeOtherHeaderFile.h
│   ├── SomeOtherHeaderFile-Impl.h
│   ├── ...
├── ...

I have no issues using the library in my main .cpp file, but when I also try to include it in another .cpp file I get multiple definition errors.

There are definitions in the header files so I understand why #pragma and #ifndef guards don't protect the function/classes declarations. The library is also big enough that I can't justify going into the code and adding inline to all declarations, let alone dumping the declarations into a .cpp file. These are the main pointers/suggestions that I have ran into while trying to learn how to use this library. So is there a correct way to use this library without having to modify it?

EDIT: Adding a picture of my minimal reproducible example. This is just a snippet of the error/warning message, there are multiple definition errors for pretty much every definition in the library:

enter image description here

EDIT 2: Adding code for MRE:

The project structure is the following (using PlatformIO on VSCode):

├── .pio
│   ├── build
│   ├── libdeps
│   │   ├── WebSockets_Generic
│   │   ├──...
├── include
│   ├── other_file.h
├── lib
│   ├── TestLib
│   │   ├── TestLib.h
├── src
│   ├── main.cpp
│   ├── other_file.cpp

Inside main.cpp:

#include <Arduino.h>
#include <TestLib.h>

void setup() {}
void loop() {}

Inside other_file.cpp:

#include <other_file.h>

Inside other_file.h:

#ifndef OTHER_FILE_H
#define OTHER_FILE_H

#include <TestLib.h>

#endif

Inside TestLib.h:

#pragma once

#ifndef TESTLIB_H
#define TESTLIB_H

#define WEBSOCKETS_NETWORK_TYPE   NETWORK_QN_ETHERNET  //NETWORK_NATIVEETHERNET
#define _WEBSOCKETS_LOGLEVEL_     1
#include <WebSockets_Generic.h>

#endif

You would need to install the library through the PlatformIO manager, but the full compilation error is the following:

Linking .pio\build\teensy41\firmware.elf
.pio\build\teensy41\src\other_file.cpp.o: In function `WS_IPAddressToString(IPAddress const&)':
other_file.cpp:(.text._Z20WS_IPAddressToStringRK9IPAddress+0x0): multiple definition of `WS_IPAddressToString(IPAddress const&)'
.pio\build\teensy41\src\main.cpp.o:main.cpp:(.text._Z20WS_IPAddressToStringRK9IPAddress+0x0): first defined here
c:/users/user/.platformio/packages/toolchain-gccarmnoneeabi/bin/../lib/gcc/arm-none-eabi/5.4.1/../../../../arm-none-eabi/bin/ld.exe: Disabling relaxation: it will not work with multiple definitions
.pio\build\teensy41\src\other_file.cpp.o: In function `base64_init_encodestate':
other_file.cpp:(.text.base64_init_encodestate+0x0): multiple definition of `base64_init_encodestate'
.pio\build\teensy41\src\main.cpp.o:main.cpp:(.text.base64_init_encodestate+0x0): first defined here
.pio\build\teensy41\src\other_file.cpp.o: In function `base64_init_encodestate_nonewlines':
other_file.cpp:(.text.base64_init_encodestate_nonewlines+0x0): multiple definition of `base64_init_encodestate_nonewlines'
.pio\build\teensy41\src\main.cpp.o:main.cpp:(.text.base64_init_encodestate_nonewlines+0x0): first defined here
.pio\build\teensy41\src\other_file.cpp.o: In function `base64_encode_value':
other_file.cpp:(.text.base64_encode_value+0x0): multiple definition of `base64_encode_value'
.pio\build\teensy41\src\main.cpp.o:main.cpp:(.text.base64_encode_value+0x0): first defined here
.pio\build\teensy41\src\other_file.cpp.o: In function `base64_encode_block':
other_file.cpp:(.text.base64_encode_block+0x0): multiple definition of `base64_encode_block'
.pio\build\teensy41\src\main.cpp.o:main.cpp:(.text.base64_encode_block+0x0): first defined here
.pio\build\teensy41\src\other_file.cpp.o: In function `base64_encode_blockend':
other_file.cpp:(.text.base64_encode_blockend+0x0): multiple definition of `base64_encode_blockend'
.pio\build\teensy41\src\main.cpp.o:main.cpp:(.text.base64_encode_blockend+0x0): first defined here
.pio\build\teensy41\src\other_file.cpp.o: In function `base64_encode_chars':
other_file.cpp:(.text.base64_encode_chars+0x0): multiple definition of `base64_encode_chars'
.pio\build\teensy41\src\main.cpp.o:main.cpp:(.text.base64_encode_chars+0x0): first defined here
.pio\build\teensy41\src\other_file.cpp.o: In function `WebSockets::createHeader(unsigned char*, WSopcode_t, unsigned int, bool, unsigned char*, bool)':
other_file.cpp:(.text._ZN10WebSockets12createHeaderEPh10WSopcode_tjbS0_b+0x0): multiple definition of `WebSockets::createHeader(unsigned char*, WSopcode_t, unsigned int, bool, unsigned char*, bool)'
.pio\build\teensy41\src\main.cpp.o:main.cpp:(.text._ZN10WebSockets12createHeaderEPh10WSopcode_tjbS0_b+0x0): first defined here
.pio\build\teensy41\src\other_file.cpp.o: In function `WebSockets::sendFrameHeader(WSclient_t*, WSopcode_t, unsigned int, bool)':
other_file.cpp:(.text._ZN10WebSockets15sendFrameHeaderEP10WSclient_t10WSopcode_tjb+0x0): multiple definition of `WebSockets::sendFrameHeader(WSclient_t*, WSopcode_t, unsigned int, bool)'
.pio\build\teensy41\src\main.cpp.o:main.cpp:(.text._ZN10WebSockets15sendFrameHeaderEP10WSclient_t10WSopcode_tjb+0x0): first defined here
.pio\build\teensy41\src\other_file.cpp.o: In function `WebSockets::headerDone(WSclient_t*)':
other_file.cpp:(.text._ZN10WebSockets10headerDoneEP10WSclient_t+0x0): multiple definition of `WebSockets::headerDone(WSclient_t*)'
.pio\build\teensy41\src\main.cpp.o:main.cpp:(.text._ZN10WebSockets10headerDoneEP10WSclient_t+0x0): first defined here
.pio\build\teensy41\src\other_file.cpp.o: In function `WebSockets::base64_encode(unsigned char*, unsigned int)':
other_file.cpp:(.text._ZN10WebSockets13base64_encodeEPhj+0x0): multiple definition of `WebSockets::base64_encode(unsigned char*, unsigned int)'
.pio\build\teensy41\src\main.cpp.o:main.cpp:(.text._ZN10WebSockets13base64_encodeEPhj+0x0): first defined here
.pio\build\teensy41\src\other_file.cpp.o: In function `WebSockets::acceptKey(String&)':
other_file.cpp:(.text._ZN10WebSockets9acceptKeyER6String+0x0): multiple definition of `WebSockets::acceptKey(String&)'
.pio\build\teensy41\src\main.cpp.o:main.cpp:(.text._ZN10WebSockets9acceptKeyER6String+0x0): first defined here
.pio\build\teensy41\src\other_file.cpp.o: In function `WebSockets::write(WSclient_t*, char const*)':
other_file.cpp:(.text._ZN10WebSockets5writeEP10WSclient_tPKc+0x0): multiple definition of `WebSockets::write(WSclient_t*, char const*)'
.pio\build\teensy41\src\main.cpp.o:main.cpp:(.text._ZN10WebSockets5writeEP10WSclient_tPKc+0x0): first defined here
.pio\build\teensy41\src\other_file.cpp.o: In function `WebSockets::enableHeartbeat(WSclient_t*, unsigned long const&, unsigned long const&, unsigned char const&)':
other_file.cpp:(.text._ZN10WebSockets15enableHeartbeatEP10WSclient_tRKmS3_RKh+0x0): multiple definition of `WebSockets::enableHeartbeat(WSclient_t*, unsigned long const&, unsigned long const&, unsigned char const&)'
.pio\build\teensy41\src\main.cpp.o:main.cpp:(.text._ZN10WebSockets15enableHeartbeatEP10WSclient_tRKmS3_RKh+0x0): first defined here
.pio\build\teensy41\src\other_file.cpp.o: In function `WebSockets::handleHBTimeout(WSclient_t*)':
other_file.cpp:(.text._ZN10WebSockets15handleHBTimeoutEP10WSclient_t+0x0): multiple definition of `WebSockets::handleHBTimeout(WSclient_t*)'
.pio\build\teensy41\src\main.cpp.o:main.cpp:(.text._ZN10WebSockets15handleHBTimeoutEP10WSclient_t+0x0): first defined here
.pio\build\teensy41\src\other_file.cpp.o: In function `WebSockets::sendFrame(WSclient_t*, WSopcode_t, unsigned char*, unsigned int, bool, bool)':
other_file.cpp:(.text._ZN10WebSockets9sendFrameEP10WSclient_t10WSopcode_tPhjbb+0x0): multiple definition of `WebSockets::sendFrame(WSclient_t*, WSopcode_t, unsigned char*, unsigned int, bool, bool)'
.pio\build\teensy41\src\main.cpp.o:main.cpp:(.text._ZN10WebSockets9sendFrameEP10WSclient_t10WSopcode_tPhjbb+0x0): first defined here
.pio\build\teensy41\src\other_file.cpp.o: In function `WebSockets::clientDisconnect(WSclient_t*, unsigned short, char*, unsigned int)':
other_file.cpp:(.text._ZN10WebSockets16clientDisconnectEP10WSclient_ttPcj+0x0): multiple definition of `WebSockets::clientDisconnect(WSclient_t*, unsigned short, char*, unsigned int)'
.pio\build\teensy41\src\main.cpp.o:main.cpp:(.text._ZN10WebSockets16clientDisconnectEP10WSclient_ttPcj+0x0): first defined here
.pio\build\teensy41\src\other_file.cpp.o: In function `WebSockets::handleWebsocketPayloadCb(WSclient_t*, bool, unsigned char*)':
other_file.cpp:(.text._ZN10WebSockets24handleWebsocketPayloadCbEP10WSclient_tbPh+0x0): multiple definition of `WebSockets::handleWebsocketPayloadCb(WSclient_t*, bool, unsigned char*)'
.pio\build\teensy41\src\main.cpp.o:main.cpp:(.text._ZN10WebSockets24handleWebsocketPayloadCbEP10WSclient_tbPh+0x0): first defined here
.pio\build\teensy41\src\other_file.cpp.o: In function `WebSockets::write(WSclient_t*, unsigned char*, unsigned int)':
other_file.cpp:(.text._ZN10WebSockets5writeEP10WSclient_tPhj+0x0): multiple definition of `WebSockets::write(WSclient_t*, unsigned char*, unsigned int)'        
.pio\build\teensy41\src\main.cpp.o:main.cpp:(.text._ZN10WebSockets5writeEP10WSclient_tPhj+0x0): first defined here
.pio\build\teensy41\src\other_file.cpp.o: In function `WebSockets::readCb(WSclient_t*, unsigned char*, unsigned int, std::function<void (WSclient_t*, bool)>)':
other_file.cpp:(.text._ZN10WebSockets6readCbEP10WSclient_tPhjSt8functionIFvS1_bEE+0x0): multiple definition of `WebSockets::readCb(WSclient_t*, unsigned char*, unsigned int, std::function<void (WSclient_t*, bool)>)'
.pio\build\teensy41\src\main.cpp.o:main.cpp:(.text._ZN10WebSockets6readCbEP10WSclient_tPhjSt8functionIFvS1_bEE+0x0): first defined here
.pio\build\teensy41\src\other_file.cpp.o: In function `WebSockets::handleWebsocketWaitFor(WSclient_t*, unsigned int)':
other_file.cpp:(.text._ZN10WebSockets22handleWebsocketWaitForEP10WSclient_tj+0x0): multiple definition of `WebSockets::handleWebsocketWaitFor(WSclient_t*, unsigned int)'
.pio\build\teensy41\src\main.cpp.o:main.cpp:(.text._ZN10WebSockets22handleWebsocketWaitForEP10WSclient_tj+0x0): first defined here
.pio\build\teensy41\src\other_file.cpp.o: In function `WebSockets::handleWebsocketCb(WSclient_t*)':
other_file.cpp:(.text._ZN10WebSockets17handleWebsocketCbEP10WSclient_t+0x0): multiple definition of `WebSockets::handleWebsocketCb(WSclient_t*)'
.pio\build\teensy41\src\main.cpp.o:main.cpp:(.text._ZN10WebSockets17handleWebsocketCbEP10WSclient_t+0x0): first defined here
.pio\build\teensy41\src\other_file.cpp.o: In function `WebSockets::handleWebsocket(WSclient_t*)':
other_file.cpp:(.text._ZN10WebSockets15handleWebsocketEP10WSclient_t+0x0): multiple definition of `WebSockets::handleWebsocket(WSclient_t*)'
.pio\build\teensy41\src\main.cpp.o:main.cpp:(.text._ZN10WebSockets15handleWebsocketEP10WSclient_t+0x0): first defined here
.pio\build\teensy41\src\other_file.cpp.o:(.rodata._ZTV10WebSockets+0x0): multiple definition of `vtable for WebSockets'
.pio\build\teensy41\src\main.cpp.o:(.rodata._ZTV10WebSockets+0x0): first defined here
.pio\build\teensy41\src\other_file.cpp.o:(.bss._ZSt11__exidx_end+0x0): multiple definition of `std::__exidx_end'
.pio\build\teensy41\src\main.cpp.o:(.bss._ZSt11__exidx_end+0x0): first defined here
.pio\build\teensy41\src\other_file.cpp.o:(.bss._ZSt13__exidx_start+0x0): multiple definition of `std::__exidx_start'
.pio\build\teensy41\src\main.cpp.o:(.bss._ZSt13__exidx_start+0x0): first defined here
collect2.exe: error: ld returned 1 exit status
*** [.pio\build\teensy41\firmware.elf] Error 1
  • Recommend adding a sampling of the error messages to the question. Knowing the answer to "Multiple definitions of what?" often cracks the case open within seconds. – user4581301 Nov 08 '22 at 23:03
  • Library looks a little light on the `inline` keyword for a header-only library. – user4581301 Nov 08 '22 at 23:04
  • @user4581301 Added a snippet of the error messages with an MRE. I intend to use this library in a project that follows that structure. – Gabriel Bello Nov 08 '22 at 23:15
  • 2
    Note: [For many good reasons](https://meta.stackoverflow.com/q/285551/4581301) it's always best to present text as text. Images of code and error messages tend to attract more scorn than answers. – user4581301 Nov 08 '22 at 23:20
  • 1
    That's what I figured. Looks like the writer took a short-cut and didn't `inline` some of the functions they should have. If you implement a method inside the class, you're safe, but if you separate the implementation and class definition, you have to protect the implementation with `inline` or you'll break the [One Definition Rule](https://en.cppreference.com/w/cpp/language/definition) when the header is included by multiple [translation units](https://stackoverflow.com/questions/1106149/what-is-a-translation-unit-in-c). – user4581301 Nov 08 '22 at 23:29
  • 2
    The header guards can't help you. They prevent multiple inclusion in the same translation unit, but the headers are legitimately included exactly once in different translation units, resulting in a freak-out when the translation units are linked. – user4581301 Nov 08 '22 at 23:31
  • 1
    Anyway, library is broken, but since you have the code you can fix it with some measured application of `inline`. Consider submitting the fix to the developer because it's fairly well documented and looks (rough glance) well thought out. Worth saving from a simple mistake made when translating from cpp files to headers. – user4581301 Nov 08 '22 at 23:36
  • Note on the duplicate: The selected answer is correct but reads like mud. Top-voted answer is a better description. – user4581301 Nov 08 '22 at 23:49

0 Answers0