First of all: I have posted my question on stackoverflow already, but I am not sure whether it will get much attention there... I think most Arduino developers use this forum, so I copied it here with little adaptions.
My code includes an enum class and a string conversion for this enum class . It compiles well under certain circumstances, but fails to compile under others; and I am not able to see how the differences are relevant.
The following code snippets are the shortest possible extract I could create, in which the failure can be reproduced.
EnumFailure.ino :
#include <Arduino.h>
#include <Streaming.h>
#include "STUC.h"
STUC* m_STUC;
void setup ()
{
Serial.begin (9600);
STUC::EResult result = STUC::CreateProtocolVersion (&m_STUC);
const char* resultText = STUC::GetResultText (result);
Serial << "Result: " << (int)result << " " << STUC::GetResultText (result) << endl;
// Serial << "Result: " << (int)result << " " << resultText << endl;
Serial << (unsigned long)m_STUC << endl;
}
void loop () { }
STUC.h:
#ifndef STUC_h
#define STUC_h
#include <Arduino.h>
#define COMMON_RESULTS \
X(None , 1) \
X(SUCCESS , 1)
#define COMMON_FAILURES \
X(FAIL_Pointer_Zero ) \
\
X(FAIL_Device_Exists ) \
X(FAIL_Device_Initialize ) \
X(FAIL_Device_InvalidID ) \
\
X(FAIL_Protocol_VersionInvalid )
class STUC
{
public:
enum class EResult : uint16_t
{
#define X(name,value) name = value,
COMMON_RESULTS
#undef X
Dummy_FirstFailure = 0x100,
#define X(name) name,
COMMON_FAILURES
#undef X
};
#define X(name,value) #name,
constexpr static char const *c_EnumNames_CommonResults[]
{
COMMON_RESULTS
};
#undef X
#define X(name) #name,
constexpr static char const *c_EnumNames_CommonFailures[]
{
COMMON_FAILURES
};
#undef X
public:
static EResult CreateProtocolVersion (STUC** i_ppSTUC);
static const char* GetResultText (EResult i_Result);
};
#endif
STUC.cpp :
#include "STUC.h"
STUC::EResult STUC::CreateProtocolVersion (STUC** i_ppSTUC)
{
if (*i_ppSTUC != 0)
return EResult::FAIL_Device_Exists;
*i_ppSTUC = new STUC ();
return EResult::SUCCESS;
}
const char* STUC::GetResultText (STUC::EResult i_Result)
{
if (i_Result < EResult::Dummy_FirstFailure)
return c_EnumNames_CommonResults[(int)i_Result];
return c_EnumNames_CommonFailures[((int)i_Result) - 0x100];
}
Compile log:
FQBN: arduino:avr:uno
Using board 'uno' from platform in folder: C:\Users\tk\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.7
Using core 'arduino' from platform in folder: C:\Users\tk\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.7
Detecting libraries used...
C:\Users\tk\AppData\Local\Arduino15\packages\arduino\tools\avr-gcc\7.3.0-atmel3.6.1-arduino7/bin/avr-g++ -c -g -Os -w -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -Wno-error=narrowing -flto -w -x c++ -E -CC -mmcu=atmega328p -DF_CPU=16000000L -DARDUINO=10607 -DARDUINO_AVR_UNO -DARDUINO_ARCH_AVR -IC:\Users\tk\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.7\cores\arduino -IC:\Users\tk\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.7\variants\standard C:\Users\tk\AppData\Local\arduino\sketches\0E962567DCB6363EE28BE6AC9462ACA9\sketch\EnumFailure.ino.cpp -o nul
Alternatives for Streaming.h: [Streaming@6.2.4]
ResolveLibrary(Streaming.h)
-> candidates: [Streaming@6.2.4]
C:\Users\tk\AppData\Local\Arduino15\packages\arduino\tools\avr-gcc\7.3.0-atmel3.6.1-arduino7/bin/avr-g++ -c -g -Os -w -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -Wno-error=narrowing -flto -w -x c++ -E -CC -mmcu=atmega328p -DF_CPU=16000000L -DARDUINO=10607 -DARDUINO_AVR_UNO -DARDUINO_ARCH_AVR -IC:\Users\tk\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.7\cores\arduino -IC:\Users\tk\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.7\variants\standard -Id:\Arduino\libraries\Streaming\src C:\Users\tk\AppData\Local\arduino\sketches\0E962567DCB6363EE28BE6AC9462ACA9\sketch\EnumFailure.ino.cpp -o nul
Using cached library dependencies for file: C:\Users\tk\AppData\Local\arduino\sketches\0E962567DCB6363EE28BE6AC9462ACA9\sketch\STUC.cpp
Generating function prototypes...
C:\Users\tk\AppData\Local\Arduino15\packages\arduino\tools\avr-gcc\7.3.0-atmel3.6.1-arduino7/bin/avr-g++ -c -g -Os -w -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -Wno-error=narrowing -flto -w -x c++ -E -CC -mmcu=atmega328p -DF_CPU=16000000L -DARDUINO=10607 -DARDUINO_AVR_UNO -DARDUINO_ARCH_AVR -IC:\Users\tk\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.7\cores\arduino -IC:\Users\tk\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.7\variants\standard -Id:\Arduino\libraries\Streaming\src C:\Users\tk\AppData\Local\arduino\sketches\0E962567DCB6363EE28BE6AC9462ACA9\sketch\EnumFailure.ino.cpp -o C:\Users\tk\AppData\Local\Temp\1585385427\sketch_merged.cpp
C:\Users\tk\AppData\Local\Arduino15\packages\builtin\tools\ctags\5.8-arduino11/ctags -u --language-force=c++ -f - --c++-kinds=svpf --fields=KSTtzns --line-directives C:\Users\tk\AppData\Local\Temp\1585385427\sketch_merged.cpp
Compiling sketch...
"C:\\Users\\tk\\AppData\\Local\\Arduino15\\packages\\arduino\\tools\\avr-gcc\\7.3.0-atmel3.6.1-arduino7/bin/avr-g++" -c -g -Os -Wall -Wextra -std=gnu++11 -fpermissive -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -Wno-error=narrowing -MMD -flto -mmcu=atmega328p -DF_CPU=16000000L -DARDUINO=10607 -DARDUINO_AVR_UNO -DARDUINO_ARCH_AVR "-IC:\\Users\\tk\\AppData\\Local\\Arduino15\\packages\\arduino\\hardware\\avr\\1.8.7\\cores\\arduino" "-IC:\\Users\\tk\\AppData\\Local\\Arduino15\\packages\\arduino\\hardware\\avr\\1.8.7\\variants\\standard" "-Id:\\Arduino\\libraries\\Streaming\\src" "C:\\Users\\tk\\AppData\\Local\\arduino\\sketches\\0E962567DCB6363EE28BE6AC9462ACA9\\sketch\\EnumFailure.ino.cpp" -o "C:\\Users\\tk\\AppData\\Local\\arduino\\sketches\\0E962567DCB6363EE28BE6AC9462ACA9\\sketch\\EnumFailure.ino.cpp.o"
Using previously compiled file: C:\Users\tk\AppData\Local\arduino\sketches\0E962567DCB6363EE28BE6AC9462ACA9\sketch\STUC.cpp.o
D:\Arduino\libraries\EnumFailure\EnumFailure.ino: In function 'void setup()':
D:\Arduino\libraries\EnumFailure\EnumFailure.ino:12:15: warning: unused variable 'resultText' [-Wunused-variable]
const char* resultText = STUC::GetResultText (result);
^~~~~~~~~~
Compiling libraries...
Compiling library "Streaming"
Compiling core...
Using precompiled core: C:\Users\tk\AppData\Local\arduino\cores\arduino_avr_uno_43d3ab6c5d44216509e220a6b38858bc\core.a
Linking everything together...
"C:\\Users\\tk\\AppData\\Local\\Arduino15\\packages\\arduino\\tools\\avr-gcc\\7.3.0-atmel3.6.1-arduino7/bin/avr-gcc" -Wall -Wextra -Os -g -flto -fuse-linker-plugin -Wl,--gc-sections -mmcu=atmega328p -o "C:\\Users\\tk\\AppData\\Local\\arduino\\sketches\\0E962567DCB6363EE28BE6AC9462ACA9/EnumFailure.ino.elf" "C:\\Users\\tk\\AppData\\Local\\arduino\\sketches\\0E962567DCB6363EE28BE6AC9462ACA9\\sketch\\EnumFailure.ino.cpp.o" "C:\\Users\\tk\\AppData\\Local\\arduino\\sketches\\0E962567DCB6363EE28BE6AC9462ACA9\\sketch\\STUC.cpp.o" "C:\\Users\\tk\\AppData\\Local\\arduino\\sketches\\0E962567DCB6363EE28BE6AC9462ACA9/..\\..\\cores\\arduino_avr_uno_43d3ab6c5d44216509e220a6b38858bc\\core.a" "-LC:\\Users\\tk\\AppData\\Local\\arduino\\sketches\\0E962567DCB6363EE28BE6AC9462ACA9" -lm
C:\Users\tk\AppData\Local\Temp\ccIvWBVO.ltrans0.ltrans.o: In function `GetResultText':
D:\Arduino\libraries\EnumFailure/STUC.cpp:17: undefined reference to `STUC::c_EnumNames_CommonResults'
D:\Arduino\libraries\EnumFailure/STUC.cpp:17: undefined reference to `STUC::c_EnumNames_CommonResults'
D:\Arduino\libraries\EnumFailure/STUC.cpp:18: undefined reference to `STUC::c_EnumNames_CommonFailures'
D:\Arduino\libraries\EnumFailure/STUC.cpp:18: undefined reference to `STUC::c_EnumNames_CommonFailures'
collect2.exe: error: ld returned 1 exit status
Using library Streaming at version 6.2.4 in folder: D:\Arduino\libraries\Streaming
exit status 1
Compilation error: exit status 1
Arduino IDE: Version: 2.3.8, Date: 2026-02-25T15:38:21.789Z, CLI Version: 1.4.1
Compile for Arduino UNO.
The weird things are:
The failure comes from the linker, not from the compiler.
if I enable the commented line in the .ino again, the compilation succeeds.
if I remove the if from CreateProtocolVersion(..), the compilation succeeds.
Where is my mistake? I don't see how the changes that let the compilation succeed are related to the failure.
Is there a mistake at all or is it a bug in the compiler (which is very unlikely, but the seem to exist sometimes)?
I already checked the differences between a successful and an aborted compilation:
If I uncomment the mentioned line in the .ino, the files in the sketches folder (C:\Users\tk\AppData\Local\arduino\sketches\) are 100% identical except this one line in .ino.cpp, but it compiles.