PlatformIO Community

Correct organization and linking of multiple cpp and h files

Hi everybody,
I am using VScode with PlatformIO to program my ESP32 board. Here my platformio.ini

[env:firebeetle32]
platform = espressif32
board = firebeetle32
framework = arduino
upload_port = COM5
lib_deps = 
	256dpi/MQTT@^2.5.0
	bblanchon/ArduinoJson@^6.17.3
	paulstoffregen/Time@^1.6.1
	adafruit/Adafruit ADS1X15@^2.2.0
	adafruit/Adafruit BusIO@^1.9.3

My current structure of files is as follows:

  1. My main.cpp is in the src folder.
  2. My include files (header files) are all in the include folder. E.g. foo.h and bar.h.
  3. My libs are either external in the lib_deps directories or one local library is in the lib directory.

So there are currently three folders src, inlude and lib in my pio project folder.

What I want to achieve is a better structure since most of the header files should be converted into a cpp + h-file. For example, bar.cpp + bar.h. The problem I am facing is that I do not know where to put the new cpp/h files, I always get either linking errors or compilation errors.

  • I already tried leaving the cpp/h files in the include folder → linkin error: undefined reference

  • Putting bar.cpp + bar.h in a seperate src folder in the lib directory, e.g. lib/bar/src, leads to a compilation error because bar.h includes foo.h and compiler says: foo.h: No such file or directory

EDIT: I was able to get the second approach to work but ONLY if bar.h is not including anything placed in the directory include. When I use the absolute path “C:User\project\include\foo.h” for including foo.h than the folder is detected during compilation but all other dependencies included in foo.h are not…

From reading other threads I would prefer putting the cpp/h file pairs into a local lib folder. However, how can I achieve, that the compiler finds the other h-files placed in the include folder and which are included in my “new” cpp/h files?

I hope I made myself clear. Thanks for any suggestion.

Correct, .c/.cpp files in include/ are not compiled ("added to the build system to produce a .o object file that will be linked in the final .elf).

If foo.h is in include/, this is correct. Libraries by default don’t see the files in include/ without the user explicitly adding build_flags = -Iinclude/ in the platformio.ini.

If lib/bar/* depends on a foo.h file, then foo.h should either be in a library (so that the LDF can detect a dependency to it and include it), or if that file truly belongs to the project itself, add the build_flags as above.

Thank you for your reponse max. This brought me one step forward. Following my approach putting the cpp/h files into lib/bar/src and adding the build flag compiles fine BUT I get a linking error. Within the header file foo.h, there are three globally defined varibales (paths to an SD card). foo.h is included in main.cpp and in bar.h. It looks like the linking process detects a redefinition of these global variables:

c:/users/.../.platformio/packages/toolchain-xtensa-esp32/bin/../lib/gcc/xtensa-esp32-elf/8.4.0/../../../../xtensa-esp32-elf/bin/ld.exe: .pio\build\firebeetle32\lib3d0\libBar.a(Bar.cpp.o):C:\SomeProject/include/foo.h:103: multiple definition of `data_dir_SD'; 
.pio\build\firebeetle32\src\main.cpp.o:C:\SomeProject/include/foo.h:103: first defined here

I could hard code the paths but would like to stay with the global variables, although I know that they might not be best practice. If there is no straight forward solution I will try to get rid of the global varibales though I do not know yet how to in an elegant way.

You would get this error if you blindly create a global variable in the header without using extern. Then every .c/.cpp file that includes your header would want to create that global variable → multiple definition. Your header file has to have the form of

#ifndef _FOO_H
#define _FOO_H

// declare existance of this global variable
extern const char data_dir_SD[];

#endif

and in the corresponding foo.cpp file

#include "foo.h"

// define global variable in **one** compilation unit.
const char data_dir_SD[] = "/sd/";

(or, whatever the datattype you used there was).

Hi Max, thank you again. I was able to solve all issues with both your approaches and I do now have a way more clearer structure in my project.

One more thing. During build, a specific warning is thrown quite often

warning: ISO C++ forbids converting a string constant to 'char*' [-Wwrite-strings]

How to correctly set my build flags to ignore this specific warning (not all warnings).

I tried -Wno-conversion and -Wno-write-strings. I also checked out build_flags but it did not help me that much. maybe you could give me a hint.

String constants are supposed to be const char* or const char <variable name>[], but not (modifyable) char*, that’s against ISO C++.