Passing struct as argument to class

Hi community,
I'm trying to pass a struct to a class function as a call by reference.
With a array it's working fine, with a struct I get a error message, can't understand.
Basically it's a LIN Slave Class but I stripped it down to the porblem ...
I want to pass several arguments and additional a array that will be filled with the message comming from the LIN line.

the main:

#include "Arduino.h"
#include "lin.h"
	Lin lin_1;
		uint8_t msg_eingang_slave[8] = { 0 };
		uint8_t msg_eingang_schalter[8] = { 0 };
		uint8_t test_msg[8] = {0};
		struct std_lin_save
		{
			uint8_t msg_id;
			uint8_t proto; // std LIN = 2    Lin diag = 1
			uint8_t nBytes; // anzahl bytes im Array
			boolean msg_request;
			uint8_t* message;
		};
		struct std_lin_save Nachrichten_slave[3] =
		{
		{0xC1,2,8, true,msg_eingang_schalter},
		{0xC1,2,8, false,msg_eingang_slave},
 	    {0x2F,2,4, false,test_msg}
		};
void setup()
{
	Serial.begin(115200);
	Serial.println("Start ...");
	lin_1.set_data(std_lin_save *Nachrichten_slave);
}

void loop()
{
delay(1000);
}

The LIN Class reduced:

#include <inttypes.h>
#include "Arduino.h"
#include <HardwareSerial.h>

#ifndef LIN_H
#define LIN_H

class Lin
{
protected:
		struct std_lin_save
		{
			uint8_t msg_id;
			uint8_t proto; // std LIN = 2    Lin diag = 1
			uint8_t nBytes; // anzahl bytes im Array
			boolean msg_request;
			uint8_t* message;
		};
public:
		void set_data(struct std_lin_save *Nachrichten_slave){
			Serial.print(Nachrichten_slave->nBytes);
		}
};
#endif

The error message:
..\LIN_test.ino:34:30: error: expected primary-expression before '*' token
lin_1.set_data(std_lin_save *Nachrichten_slave);

Who can help what's wrong?

try

lin_1.set_data(std_lin_save &Nachrichten_slave);

what do you expect from that ?

You probably meant to write a type cast (std_lin_save *) Nachrichten_slave as the function expects a pointer, not a reference.

but you pass an array when the function seems to expect only one element, you should also pass the size of the array

also the struct definition should be made only once as you can't convert from 'std_lin_save*' to 'Lin::std_lin_save*'. You know they are the same, but for the compiler they are different beasts.

➜ don't define the struct as private.

PS: side note, no need to repeat struct in C++

in a nutshell, look at something like this

struct std_lin_save
{
  uint8_t msg_id;
  uint8_t proto; // std LIN = 2    Lin diag = 1
  uint8_t nBytes; // anzahl bytes im Array
  boolean msg_request;
  uint8_t* message;
};

class Lin {
  public:
    void set_data(std_lin_save * data) {
      Serial.write(data->message, data->nBytes);
      Serial.println();
    }

    void set_data(std_lin_save * data, size_t count) {  // Updated parameter type to take array directly
      for (size_t i = 0; i < count; i++) {
        Serial.write(data[i].message, data[i].nBytes);  // Fixed to access correct element's nBytes
        Serial.println();
      }
    }
};

Lin lin_1;

uint8_t msg_eingang_slave[8] = {'1', '2', '3', '4', '5', '6', '7', '8'};
uint8_t msg_eingang_schalter[8] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'};
uint8_t test_msg[4] = {'A', 'B', 'C', 'D'};

std_lin_save Nachrichten_slave[3] =
{
  {0xC1, 2, 8, true, msg_eingang_schalter},
  {0xC1, 2, 8, false, msg_eingang_slave},
  {0x2F, 2, 4, false, test_msg}
};
const size_t nb_Nachrichten_slave = sizeof(Nachrichten_slave) / sizeof(*Nachrichten_slave);

void setup()
{
  Serial.begin(115200);
  lin_1.set_data(&(Nachrichten_slave[1]));                  // single element
  Serial.println("--------------");
  lin_1.set_data(Nachrichten_slave, nb_Nachrichten_slave);  // array of elements
  Serial.println("--------------");
}

void loop() {}

the result is:

..\LIN_test.ino:29:30: error: expected primary-expression before '&' token
lin_1.set_data(std_lin_save &Nachrichten_slave);

@J-M-L's code from Post #4 compiles error-free for me.

not surprising

Type casting need to be in parenthesis (and this would need to be (std_lin_save*) not just (std_lin_save))

I think OP was testing @bojan_jurca's code.

Hi J-M-L,

I took over your reccomentation but the compiler has several issues with that.
Looks like the struct needs to be delared in the class ...
Some output:

In file included from ..\sloeber.ino.cpp:9:0:
..\lin.h:12:19: error: 'std_lin_save' has not been declared
void set_data(std_lin_save * data) {
^
..\lin.h:17:19: error: 'std_lin_save' has not been declared
void set_data(std_lin_save * data, size_t count) { // Updated parameter type to take array directly
^
..\lin.h: In member function 'void Lin::set_data(int*)':
..\lin.h:13:26: error: request for member 'message' in '* data', which is of non-class type 'int'
Serial.write(data->message, data->nBytes);
^
..\lin.h:13:41: error: request for member 'nBytes' in '* data', which is of non-class type 'int'
Serial.write(data->message, data->nBytes);
^
..\lin.h: In member function 'void Lin::set_data(int*, size_t)':
..\lin.h:19:30: error: request for member 'message' in '(data + ((sizetype)(i * 4u)))', which is of non-class type 'int'
Serial.write(data[i].message, data[i].nBytes); // Fixed to access correct element's nBytes
^
..\lin.h:19:47: error: request for member 'nBytes' in '
(data + ((sizetype)(i * 4u)))', which is of non-class type 'int'
Serial.write(data[i].message, data[i].nBytes); // Fixed to access correct element's nBytes
^
In file included from ..\sloeber.ino.cpp:14:0:
..\LIN_test.ino: In function 'void setup()':
..\LIN_test.ino:31:43: error: no matching function for call to 'Lin::set_data(std_lin_save*)'
lin_1.set_data(&(Nachrichten_slave[1])); // single element

no the struct needs to be known to the class and to the .ino, so your LIN Class should be

LIN.h

#ifndef LIN_H
#define LIN_H

#include <Arduino.h>

struct std_lin_save
{
  uint8_t msg_id;
  uint8_t proto; // std LIN = 2    Lin diag = 1
  uint8_t nBytes; // anzahl bytes im Array
  boolean msg_request;
  uint8_t* message;
};

class Lin {
  public:
    void set_data(std_lin_save * data);
    void set_data(std_lin_save * data, size_t count);  // Updated parameter type to take array directly
};

#endif

then your LIN.cpp

#include "Lin.h"

void Lin::set_data(std_lin_save * data) {
  Serial.write(data->message, data->nBytes);
  Serial.println();
}

void Lin::set_data(std_lin_save * data, size_t count) {
  for (size_t i = 0; i < count; i++) {
    Serial.write(data[i].message, data[i].nBytes); 
    Serial.println();
  }
}

your arduino code would then be

#include "Lin.h"
Lin lin_1;

uint8_t msg_eingang_slave[8] = {'1', '2', '3', '4', '5', '6', '7', '8'};
uint8_t msg_eingang_schalter[8] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'};
uint8_t test_msg[4] = {'A', 'B', 'C', 'D'};

std_lin_save Nachrichten_slave[3] =
{
  {0xC1, 2, 8, true, msg_eingang_schalter},
  {0xC1, 2, 8, false, msg_eingang_slave},
  {0x2F, 2, 4, false, test_msg}
};
const size_t nb_Nachrichten_slave = sizeof(Nachrichten_slave) / sizeof(*Nachrichten_slave);

void setup()
{
  Serial.begin(115200);
  lin_1.set_data(&(Nachrichten_slave[1]));                  // single element
  Serial.println("--------------");
  lin_1.set_data(Nachrichten_slave, nb_Nachrichten_slave);  // array of elements
  Serial.println("--------------");
}

void loop() {}

typed here, so mind typos

@J-M-L
Sorry, I splitted your code into test.ino and lin.h

All together in test.ino it's compiled without problem but I must split it up because the compete program has a size of several thousend lines ....

Would be perfect if you can tell me how to separate it into a separate lin.h class .

Target is to read the incomming messages and fill the (call by reference) array's
msg_eingang_schalter
to work with that in another part of the program and

Send the content of (called by reference) msg_eingang_slave thats changing in another part of the program

This already works fine with a standard array but I need more information to handle the different messages.
It will be a loop comparing the incomming iD with Nachrichten_slave[x].msg_id and depending on that see if sending or receiving.

If you want to do it right, you need a Lin.h and a Lin.cpp as highlighted above.

The .h is the header, that's where you declare all the functions, types etc (what other codes need to know to use the Lin class) and the .cpp is the implementation, that's where you define the functions etc...

here is what it would look like in the simulator

if you pass a pointer, then it's not by reference, it's just a pointer


EDIT I modified the wokwi example to add passing by reference

@J-M-L
This looks great, the "reference" is what i need.
I added to Lin.h the loop and a pointer to work inside the class with the array:

protected:
	std_lin_save * data2;
  public:
    void set_data(std_lin_save * data);
    void set_data(std_lin_save * data, size_t count);  // Updated parameter type to take array directly
    void set_data(std_lin_save & data);  // New function to accept by reference
    void loop();

in Lin.cpp a small change to simulate changes made by the class


void Lin::loop() {
	data2->message[0] ++;
	if (data2->message[0] > 200){
		data2->message[0] = 0;
	}
}

and in the .ino a output of the change

void loop() {
				for (int i = 0; i < 8 + 1; i++) {
					if (msg_eingang_slave[i] <= 0x0f)
						Serial.print("0x0");
					else
						Serial.print("0x");
					Serial.print(msg_eingang_slave[i], HEX);
					Serial.print(" ");
				}
				Serial.println(" ");
				lin_1.loop();
delay(1000);
}

This works - so far :slight_smile:

But ... with

set_data(std_lin_save & data)

I have just 1 Element out of the struct but I need all of them.

--> Is it too easy to add to Lin.h

void set_data(std_lin_save & data, size_t count);

to Lin.cpp

void Lin::set_data(std_lin_save & data, size_t count) {
  for (size_t i = 0; i < count; i++) {
    Serial.write(data[i].message, data[i].nBytes);
    Serial.println();
  }
}

and to test.ino

lin_1.set_data(Nachrichten_slave, nb_Nachrichten_slave); 

to get all data by reference?

at least my compiler has a problem with that :thinking:

if you want to pass the array by reference and it's always made of 3 elements, add to the header (.h)

    void set_data(std_lin_save (&data)[3]);  // New function to accept array by reference (with fixed size)

and add the implementation to the .cpp

void Lin::set_data(std_lin_save (&data)[3]) {  // Accepting array by reference (with fixed size)
  for (size_t i = 0; i < 3; i++) {
    Serial.write(data[i].message, data[i].nBytes);
    Serial.println();
  }
}

you would then pas the entire array by reference in the .ino by doing

  lin_1.set_data(Nachrichten_slave);  // passing entire array by reference (fixed size)

if the array can have a variable size, then the easiest way would be to have a template

In the header:

    template <size_t N>
    void set_data(std_lin_save (&data)[N]);  // Templated function to handle array of variable size

in the implementation:

template <size_t N>
void Lin::set_data(std_lin_save (&data)[N]) {  // Templated function for array of variable size
  for (size_t i = 0; i < N; i++) {
    Serial.write(data[i].message, data[i].nBytes);
    Serial.println();
  }
}

and then calling in the code

  lin_1.set_data(Nachrichten_slave);  // passing entire array by reference (variable size)

should work.

Typed here, so for you to test.

I'm thinking the entire implementation must be in the .h file for the templated member function.

yes, good point - probably an .hpp

@J-M-L @gfvalvo
Now it works and I have to review how and why it works.
Especially the "Template" is something I'm not really aware.

Thank you both so much :slightly_smiling_face: :+1:

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.