structure in library

Hi there, I have in my sketch a sub to display a signed long on a standard LCD display. To pass the information to the sub I use a structure like

struct __Data_2_LCD {
    long Data;
    byte Digit_Start;
    byte Digit_End;
    byte Decimals;
    byte LCD_Row;
    byte LCD_Col;
    bool LeadingZeroes; 
    };

Then I declare a variable using struct __Data_2_LCD _Data_2_LCD = {0, 0, 0, 0, 0, 0, false};

Finally I have the code. All of this in my sketch works as expected. No problem so far!!

Now I want to move everything to my version of the LCD library but whatever I do I always get errors while compiling.

Because of this way of working I need only 1 instance of the structure, so I want it to be declared and initialized in the library.

No problem with the code in the cpp file: void LCD_ASI::Data_2_LCD( ) Nothing to pass to the routine since it uses the one and only available instance of the structure.

But how and where to declare the structure __Data_2_LCD and the instance of the structure _Data_2_LCD??

I read a few threads on this on this forum but none of them solved my questions.

I suppose the structure goes in the h file, in the public: part after the subroutine definitions?

The caller (my sketch) then just needs to fill in the instance of the structure and call the sub like any other sub in the library.

Thanks for your insights Regards

Please post ALL the code. Both the working version as all the files of your attempt to split it. Without it we have NO idea what you actually have. You may also use a MCVE :)

And things like public: and "oid LCD_ASI::Data_2_LCD( )" have nothing to do with separate library files. But you seem to try and do two things at the same time. Make a library (separate files) AND turn it into a class. Although most libraries contain a class, it's not mandatory.

Thanks for the reply!
The full code I can’t post here, way to long but I hope this MCVE can clear some things out

Header file of the lib (LCD_ASI.h)

#ifndef LCD_ASI_h
#define LCD_ASI_h

#include <inttypes.h>
#include "Print.h"


class LCD_ASI : public Print {
public:


// Structure to format number on LCD
struct __Data_2_LCD {
 __Data_2_LCD();
 long Data;
 uint8_t Digit_Start;
 uint8_t Digit_End;
 uint8_t Decimals;
 uint8_t LCD_Row;
 uint8_t LCD_Col;
 bool LeadingZeroes; 
 };
 
/*

  LCD_ASI(uint8_t rs, uint8_t rw, uint8_t enable,
  uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7); 

 void init(uint8_t rs, uint8_t rw, uint8_t enable,
  uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7);

 void begin(uint8_t cols, uint8_t rows, uint8_t charsize = LCD_5x8DOTS);

 void clear();
 void home();

 void DisplayOff();
 void DisplayOn();
 void BlinkOff();
 void BlinkOn();
 void CursorOff();
 
*/ 
 
 void Data_2_LCD();
 

  using Print::write;
private:
/*
 void send(uint8_t, uint8_t);
 void write4bits(uint8_t);
 void pulseEnable();
  
 uint8_t _RS_pin; // LOW: command HIGH: character
 uint8_t _RW_pin; // LOW: write to LCD HIGH: read from LCD
 uint8_t _EN_pin; // activated by a HIGH pulse.
 uint8_t _DB_pins[4];


 uint8_t _displayfunction;
 uint8_t _displaycontrol;
 uint8_t _displaymode;
*/
};
#endif

The cpp file (LCD_ASI.cpp)

#include "LCD_ASI.h"

#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include "Arduino.h"

struct __Data_2_LCD _Data_2_LCD = {
 Data = 100;
 Digit_Start = 3;
 Digit_End = 1;
 Decimals = 0;
 LCD_Row = 1;
 LCD_Col = 1;
 LeadingZeroes = false; 
 };



//=======================================================================
void LCD_ASI::Data_2_LCD( )
// sub to write numerical data (signed 32 bit integer) to LCD
// data and formatting information in variable _Data_2_LCD of type __Data_2_LCD 
//
{

  // set requested LCD position
  //setCursor( _Data_2_LCD.LCD_Row, _Data_2_LCD.LCD_Col );
  
  //check given digits to show
  if ( _Data_2_LCD.Digit_Start <= _Data_2_LCD.Digit_End )
  {
   // write( ascii_Question );
   return;
  }

  // code goes on to write data 2 LCD below
  // ... ... ...
  
  }

And finally my sketch

// include the library code:
#include <LCD_ASI.h>

// hardware
//
#define RS  37    // LCD pins
#define RW  35
#define ENA 36
#define D7  29
#define D6  28
#define D5  27
#define D4  26


// initialize the LCD library for 4 bit interface
//LCD_ASI lcd( RS, RW, ENA, D4, D5, D6, D7 );




//=====================================================================
void setup()
{
Serial.begin( 115200 ); 

  // instance _Data_2_LCD of structure __Data_2_LCD defined in LCD lib
  // just using it in ino file(s) of project
  //
  

      // fill the structure
      _Data_2_LCD.Data          = 123456;
      _Data_2_LCD.Digit_Start   = 3;
      _Data_2_LCD.Digit_End     = 1;
      _Data_2_LCD.Decimals      = 0;
      _Data_2_LCD.LCD_Row       = 2;
      _Data_2_LCD.LCD_Col       = 10;
      _Data_2_LCD.LeadingZeroes = false;

      // write data on LCD
      lcd.Data_2_LCD ( );


      Serial.print(_Data_2_LCD.Data);

}

//=====================================================================
void loop()
{
  
}

I think I have all the pieces of the puzzle, just don’t know how to put them together.

What I’m trying to do
a) Declare a variable _Data_2_LCD of type __Data_2_LCD in library LCD_ASI
b) Use that variable in my sketch

Compiles with error “_Data_2_LCD” was not declared in this scope, setup routine of the sketch.

All inputs are welcome… …

But why do you try to make a struct inside a class? In the evolution of C to C++ a class is the evolution of a struct. So why not have multiple classes with the settings you know have in the class?

Aka, what is the idea behind the struct and what's the idea behind the class?

PS I would go for a beter class/library name ;) PPS Stay away from Strings...

Your sketch includes a header where "_Data_2_LCD" is not declared, therefore the sketch cannot "see" the variable named "_Data_2_LCD". Your sketch does not (nor should it) include the *.cpp file, where the variable IS declared.

The idea behind the structure is to keep the code readable. I mean

     // fill the structure
      _Data_2_LCD.Data          = 123456;
      _Data_2_LCD.Digit_Start   = 3;
      _Data_2_LCD.Digit_End     = 1;
      _Data_2_LCD.Decimals      = 0;
      _Data_2_LCD.LCD_Row       = 2;
      _Data_2_LCD.LCD_Col       = 10;
      _Data_2_LCD.LeadingZeroes = false;

      // write data on LCD
      lcd.Data_2_LCD ( );

is, I think more readable than

lcd.Data_2_LCD ( 123456, 3, 1, 0, 2, 10, false )

when passing variables to the sub.

On the other hand it's more typing :)

Indeed, I declared variable "_Data_2_LCD" in the cpp file because doing it in the "h" file gets me other errors.

So, should I forget about the structure and pass the pararmeters to the sub as above??

lcd.Data_2_LCD ( 123456, 3, 1, 0, 2, 10, false )

mcs51mc: So, should I forget about the structure and pass the pararmeters to the sub as above??

Yes, declare the type definition of the struct in the LCD_ASI header file. Declare a variable of that type in the sketch and pass that variable as a reference to the method in the class.

the struct is in the class. do you want it there? you have a wrong syntax for struct initialization

LCD_ASI::__Data_2_LCD _Data_2_LCD = { .Data = 100, .Digit_Start = 3, .Digit_End = 1, .Decimals = 0, .LCD_Row = 1, .LCD_Col = 1, .LeadingZeroes = false };

remove the 'constructor' from the struct

and please use less underscores

mcs51mc: The idea behind the structure is to keep the code readable.

Yeah, but you could do that alone with the class as well. So why mix the two? Why have a class AND a struct?

Thanks for the reactions people!

@Juraj: I’ll try that and keep you posted of the outcome! I suppose that by “constructor” you mean this

__Data_2_LCD();

in the declaration of the structure in the “h” file, right?

@septillion: Fine for me to do this with the class alone, but do you have some example code? You know, C is for sure not my cup of tea, so some example code is always welcome.

Regards

But can you tell us what you think the struc should hold en what the class should hold? Aka, what is the extra function of the class (in your head)?

To do it all in the class I would think alone the following:

#ifndef LCD_ASI_h
#define LCD_ASI_h

#include "Print.h"


class LCD_ASI : public Print {
  public:
    LCD_ASI();
  
  private:
    long Data;
    uint8_t Digit_Start;
    uint8_t Digit_End;
    uint8_t Decimals;
    uint8_t LCD_Row;
    uint8_t LCD_Col;
    bool LeadingZeroes;
};
#endif

Or make them public if you don't want to mess with getters and setters (but that will reduce the encapsulation).

You could do something as simple as:

//test.h

struct PARAMETERS {
    int a, b;
};

class StructTest {
public:
    PARAMETERS& getParams() { return params; }
    void printParams() {
        Serial.print("a=");
        Serial.print(params.a);
        Serial.print(", b=");
        Serial.println(params.b);
    }

private:
    PARAMETERS params;
};



//Sketch
#include "test.h"

void setup()
{
  Serial.begin(9600);

  StructTest st;

  st.getParams() = { 12, 34 };
  st.printParams();

  st.getParams().a = 123;
  st.getParams().b = 456;
  st.printParams();
}

The code is explanatory.

But why you want to do that (not even talking about returning a pointer to a private variable) I would not know…

septillion: But why you want to do that (not even talking about returning a pointer to a private variable) I would not know...

All the variables in your example in post #10 are private and (I hope) ment to be exposed to change through getters/setters. My example was explanatory, I would not write "public" code like that because there is no validation of the values applied to the struct :)

I think I saw the light 8) !! :astonished:

Forget about the structure in a class, it’s like building a box in a box, right? Everything that belongs to the class is kept together by the class, so no need for another container to keep things even more together. Is that it??

Declaring a variable in the “public” part of the class, any sketch can access it via

lcd.MyVar = 127;

Can you please elaborate on “getters and setters” and “reduce the encapsulation” or do you have a link explaining what you mean by this?

Thanks again people for the time putting into this! Regards

mcs51mc:
Everything that belongs to the class is kept together by the class, so no need for another container to keep things even more together.
Is that it??

There is absolutely nothing wrong in assembling large number of arguments into a single structure - it is often done in order to apply the same set of arguments to multiple objects.

mcs51mc:
Can you please elaborate on “getters and setters” and “reduce the encapsulation” or do you have a link explaining what you mean by this?

Encapsulation means that you contain something within a class and control how it is accessed.

class dummy {
public:
  dummy() : value(0) {};

  int getValue() { return value; } //Getter

  void setValue(int v) //Setter
  {
    if (v < 0) v = 0;
    else if (v > 100) v = 100;
    value = v;
  }
private:
  int value;
};

As you can see, access to the private “value” is restricted to the class itself - but you can both safely get and set it from outside of the class. When setting it, it is validated to be within the allowed boundaries. It is all about eliminating someone from passing bad values which may break the functionality handled by the class.

Why do you want a box in a box? If you just want a box inside a box to supply all parameters, why can't that be done with the original box?

Same analogy applies to code and real life. Why do you want to put stuff inside a box, just to put that box into a box? What's wrong with the original box? Why not improve that one?

If you have some stuff belonging together and you want to add that to another set (put a box of resistor together with snips and tweezers to form a toolbox) that's fine. But why not make both classes or both structs? Why do you think you need two types :)

Not that it can't be done, but why :)

And yeah, Danois90 explained why programmers like encapsulation. You can ensure input in order to not create errors/trouble :)