c++classstructarduinoarduino-c++

Calling struct pointer member from Class - (pointer) "...uninitialized", (pointer) "...was declared here"


I am attempting to develop a "wrapper" Class that integrates multiple peripherals in a controller board including sensors, RGB LEDs an many others.

To achieve this, I am trying to group all connected peripherals in a structure inside the Controller Class and access its members through a struct pointer.

My end goal is to declare a controller and use the peripherals as follows:

    Controller sys;
    sys.devices -> rgb.setColor(/*color code*/);

since I find it more readable/understandable during development.

I have reduced my code to the bare minimum after several corrections based on other SOVF posts.

Please note that I am testing my code in Codelite using mingw64, however this will be implemented using platformio on a Teensy board. This is just a skimmed version for the forum

Physical pins are declared as a struct, in a pinout.h file:

#ifndef PINOUT_H
#define PINOUT_H

struct Pinout {

    int R = 1;
    int G = 2;
    int B = 3;
};

#endif // PINOUT_H

then, there is an RGBLed class and a main file (I put them together here just for the sake of simplicity)

#include "pinout.h"
#include <iostream>

class RGBLed
{

public:
    
    RGBLed(int pinR, int pinG, int pinB)
    {
        this->_r = pinR;
        this->_g = pinG;
        this->_b = pinB;
    };
    
    // this is just a dumb example method
    bool turnOn()
    {

        this->_ledState = true;

        return _ledState;
    }

    int* getPinout()
    {

        static int pinPtr[3];

        pinPtr[0] = this->_r;
        pinPtr[1] = this->_g;
        pinPtr[2] = this->_b;

        return pinPtr;
    }

private:
    int _r, _g, _b;
    bool _ledState;
};

/* This is the _wrapper_ Class I am designing */
class Controller
{

public:
    // Default Constructor
    inline Controller(){};

    struct Peripherals {
        
        RGBLed rgb = RGBLed(_pins->R, _pins->G, _pins->B);
       // on a previous compilation I used:
       //  RGBLed rgb = RGBLed(Controller::_pins->R, Controller::_pins->G, Controller::_pins->B);
       // however I ran into linker issues

       /* In the future many more devices will be declared here */
    };

    struct Peripherals *devices;

private:
    // I want to keep the pins unreachable to the developer (aka myself and colleagues)
    static Pinout* _pins;       // I initially had a warning of "invalid use of non-static data member"
                                // after declaring it as static it went away
};

Then, my main() is:

int main()
{

    // Controller object instance
    Controller sys;

    // Testing if we can call methods from the struct member inside the Class
    if(sys.devices->rgb.turnOn())
        std::cout << "LED is ON" << std::endl;
        
    // Retrieve pinout
    int* conn = sys.devices->rgb.getPinout();

    std::cout << "RGBLed pins are: " << std::endl;

    for(int idx = 0; idx < 3; idx++) {

        //std::cout << std::to_string(conn[idx]);

        if(idx != 2)
            std::cout << ", ";
        else
            std::cout << " " << std::endl;
    }

    return 0;
}

If I comment the lines: int* conn = sys.devices->rgb.getPinout(); and std::cout << std::to_string(conn[idx]); my code compiles just fine and, evidently, I don't get any valuable output.

enter image description here

However, if I call the .getPinout() method from the rgb object inside the struct, I get the warnings:

"main.cpp...warning: 'sys.Controller::devices' is used uninitialized"

and

"main.cpp...note: sys.Controller::devices was declared here " pointing at the line: `Controller sys;"

I am absolutely certain that I might be butchering the C++ language hence I am asking here for some pointers (pun intended). I have mixed too many solutions I have found here in SOVF without any success.

What is the best way to compartmentalize my peripherals through a struct inside the Class? Is there a possibility that on top of my own mistakes, I am also facing compiler issues due to the mismatch between gcc (using 13) and the Arduino gcc compiler?

EDITS:

Based on the comments from @273K and @KIIV I realised that effectively I was missing the initialization of the struct devices since I was declaring only a pointer with nothing in it.

Now, I am facing another rather minor issue, I guess.

I am unable to access the elements from the pinout struct. Please see above how they are initialised inside of the struct declaration

If I do this inside the Controller class, where the RGBLed receives numerical values

class Controller
{

public:
    // Default Constructor
    inline Controller(){};

    struct Peripherals {

        RGBLed rgb = RGBLed(1, 2, 3) ; // <--- pay attention to this line
    };

    Peripherals devs, *devices = &devs;

    // devices = &devs;
    // devices = malloc(sizeof(Peripherals)); // = new Peripherals{RGBLed(_pins->R, _pins->G, _pins->B)};

private:
    static Pinout* _pins; // I initially had a warning of "invalid use of non-static data member"
};

I can successfully print the pinout:

enter image description here

However I am unable to access the predefined values inside the pinout struct.

If inside the struct Peripherals I do:

RGBLed rgb = RGBLed(Controller::_pins->R, 2, 3);

I get:

_"undefined reference to Controller::pins"

I am attempting to indicate that the scope is from the Class Controller

I have also tried moving the Pinout pointer inside of the Peripherals struct and changing the calling to the pins as:

RGBLed rgb = RGBLed(this->_pins->R, 2, 3);

getting a similar error _"undefined reference to Controller::Peripherals::pins"

Is it even possible to access the private member this way?


Solution

  • Based on @273K and @KIIV's comments, I realised that I was not declaring and initialising the pointer devices to the struct Peripherals.

    The corrected Controller Class is the following:

    class Controller
    {
    
    public:
        // Default Constructor
        inline Controller(){};
    
        struct Peripherals {
    
            RGBLed rgb = RGBLed(ledPins.R, ledPins.G, ledPins.B);
            /* More devices to be added */
        } devs;
    
        Peripherals *devices = &devs;
    
    };
    

    where I redeclared the Pinout struct as:

     struct Pinout {
    
         int R = 1;
         int G = 2;
         int B = 3;
    
    } ledPins;    // <-- predeclaring the struct object
    

    This way, I avoided unnecessary nesting of structs and I can use the Pinout object in the RGBLed constructor.

    I can finally run my code without a problem:

    enter image description here

    Despite I am not a big fan of having two Peripherals objects accessible in the main() function (devs and the pointer devices), it is fine for now. I really wanted to have only ONE object to get access to all the peripherals - the pointer devices.

    Thanks to all who helped!

    :Beers: