PlatformIO Community

Using libs folder -> undefined reference to

Hello,

I am new to using Platform.io and encountered a problem including a file in my main.cpp.

I am using this structure right now:

|–lib
| |
| |–dotenv-parser
| | |- dotenv-parser.cpp
| | |- dotenv-parser.h
|
|- platformio.ini
|–src
|- main.cpp

main.cpp:

#include <Arduino.h>
#include <dotenv-parser.h>

void setup()
{
  // launch on proper port I guess
  Serial.begin(9600);

  DotenvParser parser;
  parser.parseFileContent(); 
}

void loop()
{
}

dotenv-parser.cpp

#include <dotenv-parser.h>
#include <Arduino.h>
#include <iostream>
#include <string>
#include <SPIFFS.h>

// use strdup to parse string to char*
DotenvParser::DotenvParser(char const *givenFilepath = strdup(".env"))
{
    // set proper filepath
    filepath = givenFilepath;

    // check if fs could be mounted
    if (!SPIFFS.begin(true))
    {
        Serial.println("FS could not be mounted!");
    }
}

void DotenvParser::parseFileContent()
{
    // open file containing secrets
    File file = SPIFFS.open("/.env");
    if (!file)
    {
        Serial.println("Could not read file");
    }

    Serial.println("Content of file:");
    while (file.available())
    {
        Serial.write(file.read());
    }

    // close file after reading it
    file.close();
}

dotenv-parser.h:

// prevent from multiple defining
#ifndef dotenv_parser
#define dotenv_parser

class DotenvParser
{
public:
    DotenvParser(char const*filepath);
    DotenvParser();
    void parseFileContent();

private:
    const char *filepath;
};

#endif

Building the projects gives me the following error:

Linking .pio\build\esp32dev\firmware.elf
.pio\build\esp32dev\src\main.cpp.o:(.literal._Z5setupv+0x10): undefined reference to `DotenvParser::DotenvParser()'
.pio\build\esp32dev\src\main.cpp.o: In function `setup()':
D:\HomeAssistant\HomeAssistantESP/src/main.cpp:18: undefined reference to `DotenvParser::DotenvParser()'
collect2.exe: error: ld returned 1 exit status
*** [.pio\build\esp32dev\firmware.elf] Error 1

Anyone knows whats the problem. I tried every related posts solution for hours but I cant get it fixed!

I would be very happy about a little help :slight_smile:

You declare two constructors but only implement one.

I see what you’re trying to do there, remapping the default constructor with no arguments to the constructor calling it with ".env" as paramter, but that’s not the way to do it. Default parameters here should be given in the header file, not the implementation.

In the .cpp rewrite to

DotenvParser::DotenvParser(char const *givenFilepath)
{

in the .h rewrite to

public:
    DotenvParser(char const*filepath = ".env");
    //*no* second constructor!
   //DotenvParser() is DotenvParser(".env")

I’m also pretty sure you don’t have to strdup it. Actually you might have to, if the filepath argument doesn’t live in .rodata / is a constant string in flash, but e.g. a string on the stack – but then also free it in the destructor, or use Arduino’s String in the first place.

Hello @maxgerhardt ,

thank you very much for your answer. It did fix my problem and I can build the project now!

As you can see I am new to C++. I always tried to stick to TypeScript, Python etc.

But I still have one question to the following error, which occured before you gave me the solution:

Why did this error happen instead of giving me a readable error? Or at least telling me, that I have an error in my code?

It is readable – in your syntactically valid C++ code, you declared the two constructors

These need one implementation each, one with const char* value, and one without one. In your program you are constructing the object without argument, so default DotenvParser() constructor will be called

However since you’ve only implemented

the implementation for DotenvParser::DotenvParser() does not exist, only its declaration of existence does. Hence, you get an error at the linking stage when the linker tries to find your implementation – which it cannot, thus “undefined reference” – reference without a backing implementation.

Again, with the default constructor arguments written above, there is only one const char* accepting constructor function, and the compiler will interpret calls to the constructor without args as calling the const char* one with the default argument – there is no actual constructor without arguments in the implementation.

I got it now. This error is really readable, I just misinterpreted it.
Thank you for help!