Issues with Multiple Imports, Pre-Processor Guards and Project Structure

Hello Everyone.

This might be a simple resolve, but I’ve been looking for the last hour on how to fix it and came up with nothing so far.

I have an Arduino Uno project structure like so:
screenshot

My main source file is trying to import the headers from both “Thermistors.h” and “Probes.h.” Before mentioning anything else, note that this problem is still reproduced when leaving the files in the primary src directory (where main is).

Probes.h is a class handler to hold numerous types of sensor types. Because of this, it needs access to the header file from the thermistor header file.

I have included pre-processor guards to avoid double importing namespace issues. However, I keep getting compiler errors where the compiler cannot identify the instantiated object type (it’s class).

Specifically from Intelisense:
This decleration does not have a storage class or type specifier

From what I’ve read from StackOverflow and other communities, this issue is more commonly seen when #include guards are not implemented or whenever a class is declared, but called elsewhere in the program before it has been fully defined.

Could this possibly be an issue with Platormio’s cMake configuration or is my code at fault? I’ve looked it over many times and I think my code should not be an issue here. I followed the examples instructions in the lib/README file which is why they are separated into their own directories within the Lib parent folder.

main.cpp

#include <pins_arduino.h>
#include <HardwareSerial.h>
#include <USBAPI.h>
#include <Thermistor.h>
#include <Probes.h>

Probes list;

Thermistor Thermistor1(PIN_A0, 20000, 3950, 5, 20000);

list.addThermistor(Thermistor1);

Thermistor.h

//
// Created by Aspen on 10/19/2020.
//

#ifndef THERMISTOR_H
#define THERMISTOR_H

class Thermistor
{
private:
    Thermistor * head = nullptr;
    int * logicalPinAddress = nullptr;
    long * nominalResistance = nullptr;
    int * beta = nullptr;
    float * supplyVoltage = nullptr;
    float * vo = nullptr;
    float * r1 = nullptr;
    float * r2 = nullptr;
    float * temperatureCelsius = nullptr;
    float * temperatureFahrenheit = nullptr;

public:
    Thermistor();

    Thermistor(int pinAddress, long int nominalResistance, int beta, float supplyVoltage, float R1) :
            logicalPinAddress(&pinAddress), nominalResistance(&nominalResistance), beta(&beta), supplyVoltage(&supplyVoltage), r1(&R1) {};

    void setLogicalPinAddress(int * pinAddress);

    void setNominalResistance(long int * nominalResistance);

    void setBeta(int * beta);

    void setSupplyVoltage(float * supplyVoltage);

    void setVo(float * vo);

    void setR1(float * r1);

    void setR2(float * r2);

    void setTemperatureCelsius(float * temperatureCelsius);

    void setTemperatureFahrenheit(float * temperatureFahrenheit);
    
    int getLogicalPinAddress();

    long int getNominalResistance();

    int getBeta();

    float getSupplyVoltage();

    float getVo();

    float getR1();

    float getR2();

    float getTemperatureCelsius();

    float getTemperatureFahrenheit();

};


#endif //ARDUINO_TEMPERATURE_CONTROLLER_THERMISTOR

Probes.h

#ifndef PROBES_H
#define PROBES_H

#include <Thermistor.h>

class Probes 
{
    Thermistor * thermistors = nullptr;
    int numThermistors ;

public:
    Probes();

    void addThermistor(Thermistor &thermistor);

    void removeThermistor(Thermistor &thermistor);

    String activeProbes();

};

#endif //ARDUINO_TEMPERATURE_CONTROLLER_PROBES

Note: Thermistor.cpp and Probes.cpp also must include their header files (I think this goes without saying)…

You cannot call a function on an object in global space. It has to be inside a function, like setup().

Otherwise you have to rewrite the e.g. constructor of your Probes class to accept a list of Thermistor objects (e.g., std::initializer_list or duplication of constructors…). But just putting the call inside a function is the easiest way.

Oh my god. I completely forgot about that. I remember now I ran into this same issue when testing out some pseudo code almost a year ago and it completely stumped me for days. I feel like an idiot now.

Thank you. I wish the compiler’s error handling would be more indicative of these things sometimes :stuck_out_tongue:

Indeed the message is a bit… fuzzy here. After all, C/C++ is known for very cryptic error messages.

Basically it’s trying to tell you that it expected a decleration, like e.g. int myVar; but it wasn’t able to figure outs its type or storage duration (e.g., automatic, external, or static), which it can’t if the line of code is a function call.

Maybe the GCC compiler would give you a better error message upon compilation though, after all above message comes from Microsoft’s C/C++ Intellisense extension. If GCC’s message would be better of worse, I don’t know though :smiley:

I actually just started using VScode after taking a long Hiatus (and realizing that Jetbrain’s Clion doesn’t have good Micro-controller development support). I was poking around in the settings before getting started and noticed that it defaulted me to the GCC compiler and intellisense was on that as well.

Oh well. I guess that’s what makes programming fun sometimes.

Cheers mate, I owe you one!

PlatformIO provides a dedicated CLion plugin: https://plugins.jetbrains.com/plugin/13922-platformio-for-clion and CLion — PlatformIO latest documentation

I also highly prefer CLion’s Intellisense over VSCode’s. In my opinion, PlatformIO functions are better integrated in VSCode (the main plugin development effort is for VSCode), but the CLion plugin is acceptable in its functionality, too, and has the underlying power of CLion’s intellisense.

That’s fantastic! I was using Clion before it was published I think. I just got it up and running now. It seems to be a fantastic development environment setup and I have not had any issues so far, except for the debugger giving me problems. I’ll find a resolution on that though this week I’d imagine.

Thanks for the tip! :smiley: