Passing object array into object to store

Hi

I’m trying to create my own library and need a bit of help working out how to pass an array declared in the sketch into an object in the library. In Java I’d just use a simple setter method as I have done, but C++/Arduino IDE doesn’t seem to like it (compiler errors - will attach later if needed). Here’s snippets from the code:

// sketch

rfm69_node node;

Device uptimeDev(0, false, readUptime);
Device txIntDev(1, false, readTXInt, writeTXInt);
.
.
.
Device devices[] = {uptimeDev, txIntDev};
.
.
.
node.setDevices(devices);
// rfm69_node.h
#include <device.h>

class rfm69_node {
public:
	Device myDevices[];
.
.
.
}
// rfm_node69.cpp
rfm69_node::setDevices(Device devices[]) {
	myDevices = devices;
}
.
.
.

I imagine I’m overlooking some of the intricacies of pointers and arrays so please enlighten me!

Thanks

I don't know enough C++ to help here, but make darn sure that you pass a pointer to the array, and that the class does not make a copy of the array.

If I understand what I've read about C/C++ arrays, if you pass the array you're actually passing a pointer to the first element.

At the moment, I don't care whether the object points to the array or whether it has it's own "copy" as long as it's able to access the data within the array!

Any other suggestions anyone?

You can only pass the array pointer in

void functionname(int *inparam)

From there you can use memcpy to copy the bytes into a new destination. If your array is global an doesn't change, no need to copy, just access it like a normal array.

I'm a bit confused with what is going on.

In the sketch I have the following lines:

  DEBUG("Size of object :"); DEBUGln(sizeof(devices[0]));
  DEBUG("Size of array :"); DEBUGln(sizeof(devices));
  DEBUG("Elements: "); DEBUGln((sizeof(devices) / sizeof(devices[0])));
  node.setDevices(devices);

The output is: Size of object :7 Size of array :21 Elements: 3

In setDevices I have the following code:

void rfm69_node::setDevices(Device *devices) {

    DEBUG("Size of object :"); DEBUGln(sizeof(devices[0]));
  DEBUG("Size of array :"); DEBUGln(sizeof(devices));
  DEBUG("Elements: "); DEBUGln((sizeof(devices)/sizeof(devices[0])));
}

However the output is different: Size of object :7 Size of array :2 Elements: 0

Why is sizeof(devices) 2 and not 21?

sizeof(devices) in the function is the size of the pointer *devices which is 2 bytes. It sucks, but you'll have to pass in the size of array manually.

void rfm69_node::setDevices(Device *devices, int arraySize)

A pointer is the address of the first location of the array. That's all. It doesn't say how big the array is.

C/C++ is not java. I'm glad I didn't learn java first.

Thanks guys.

So to confirm that I understand....when I have a pointer to an array I have no way of knowing how big that array is?

I assume then that I need to add another field to my object that holds the length of the array so that I know how many elements it holds when I need to use it later?

As michinyon pointed out, using the array as a parameter to a pointer causes the pointer to ‘point’ at the first element.

If you want to get the size inside the function without passing a simple pointer and count, you’ll have to pass an actual array pointer or reference. As you are expecting different users arrays to be different sizes you’ll need to use a template to provide the length.

I have written an article on passing arrays to functions which looks at the topic in detail. And the above explanation should make more sense. Arduino FAQ - Passing arrays to functions

An example of what your function could look like using these methods is below.

//Passing a reference

template< long N >
  void rfm69_node::setDevices( Device (&devices)[N] ) {
     
    DEBUG("Size of object :"); DEBUGln(sizeof(devices[0]));
    DEBUG("Size of array :"); DEBUGln(sizeof(devices));
    DEBUG("Elements: "); DEBUGln((sizeof(devices)/sizeof(devices[0])));
}

//Passing a pointer

template< long N >
  void rfm69_node::setDevices( Device (*devices)[N] ) {
      
    DEBUG("Size of object :"); DEBUGln(sizeof(**devices));  //**devices is equivalent to: (*devices)[0]
    DEBUG("Size of array :"); DEBUGln(sizeof(*devices));
    DEBUG("Elements: "); DEBUGln((sizeof(*devices)/sizeof(**devices)));
}

The first method (reference) is called identically to the version using a pointer to the element type.
If you use the pointer version, you must pass the pointer:

Device array[10];
rfm69_node thing;
thing.setDevices( array ); //Reference
thing.setDevices( &array ); //Pointer

Excellent pYro, that seems to be exactly what I need.

Passing the array reference seems more intuitive to me (since I mainly code in Java) and funnily enough I had tried that with the exception that I didn’t have the template N bit, and hadn’t included N in the parameter which I guess is what the compiler errors I was getting were.

Since I’m at work at the moment (lunch break) I’ll try it when I get home and report back.

Thanks again.

I haven't heard of template before and did a quick read. It seems to be a compiler directive, where the compiler decides what to do with the template. So it would not work at run time? Say the array is form during run time.

In my case I'm expecting the user of the library to define the array as part of the sketch code however it will be interesting to see what happens if we had the scenario that user input during run time altered the array.

Passing the array in seems to only be half the battle. How do I assign the array to a field in rfm69_node?

I'm not sure what the variable declaration in rfm69_node.h needs to look like.

tavalin:
Passing the array in seems to only be half the battle. How do I assign the array to a field in rfm69_node?

I’m not sure what the variable declaration in rfm69_node.h needs to look like.

I was waiting for you to ask this question.

When using arrays as parameters the length must be known (at compile time), like my article mentions. Without it all you declare is a pointer. This shouldn’t be a problem as you cannot change the length of an array. User input will not make a difference, the array is still a certain size. And if you use dynamic memory, you’ll have to use a run-time approach (new/malloc returns a pointer, so the array information is already lost).

The alternative is to store a pointer and length like others mentioned, this is defeating the benefit of using the template, however i’ll show it for completeness as it shows the evolution between each method:

struct rfm69_node{
  
  template< long N >
    void setDevices( Device (&devices)[N] ) {
      ptr = +devices;
      count = n;
  }

  Device *ptr;
  int count;
};

To overcome forcing the array to a pointer and using extra memory to store something that is already known we need to move the template to the class, rather than the single function.

template< long N >
  struct rfm69_node{
    
    void setDevices( Device (&devices)[N] ) {
      array = &devices;
    }
    
  Device (*array)[N];
};

//Usage...

Device array[10];

rfm69_node< 10 > thing;

thing.setDevices( array );

As you can see the variable is a pointer so when using ‘array’ you need to dereference it (*array) and return to the actual array.

To use a reference you must use the constructor to initialize it, this means the array must be passed when the class is created.

template< long N >
  struct rfm69_node{
   
    rfm69_node( Device (&devices)[N] ) : array(devices) {}

    Device (&array)[N];
};

//Usage...

Device array[10];

rfm69_node< 10 > thing( array );

And now the array is associated when the class is created (the elements can be assigned later if wanted). If the array should belong to the class, the separation of the array and class is just an additional step for a user, which is basically combining them in their own code. This can be avoided.

If you construct your class as a POD, you can take advantage of the brace-enclosed-initializer syntax and get rid of the reference copying and allow users to setup the class in a single step.

I have replaced the type Device with int so the usage makes sense.

template< long N >
  struct rfm69_node{

    int array[N];
};

//Usage
rfm69_node< 4 > thing = {{ 1, 2, 3, 4 }};

Now setDevices has basically been implemented in zero lines of code. As a POD cannot have private members, the array is still directly accessible (thing.array).

Hi pYro

Putting aside the assigning issue, I seem to be banging my head on this template thing at the moment! Even trying to simplify it down I’m getting compile issues!

rfm69_node.h:

template< int N >
class rfm69_node {
	public:
	rfm69_node<N>();  // not sure if necessary here
	~rfm69_node<N>(); // not sure if necessary here
	void init(uint8_t frequency, uint8_t nodeID, uint8_t networkID, bool promiscuous, bool highPower);
	void loop();
	Device dev[N];
  };

rfm69_node.cpp:

template< int N > 
void rfm69_node<N>::init(uint8_t frequency, uint8_t nodeID, uint8_t networkID, bool promiscuous, bool highPower) {
// some code here
}

template< int N > 
void rfm69_node<N>::loop() {
// some code here
}

sketch:

Device uptimeDev(98, false, readUptime);
Device testDev(99, false, readUptime);
Device devices[4] = {uptimeDev, testDev, testDev, uptimeDev};
rfm69_node<4> node;
.
.
.
 node.init(FREQUENCY, NODEID, NETWORK, false, true);
...
node.loop();

But I’m getting the following compile errors:

sketch_mar11a.cpp.o: In function setup': C:\Program Files (x86)\Arduino/sketch_mar11a.ino:36: undefined reference to rfm69_node<4>::init(unsigned char, unsigned char, unsigned char, bool, bool)’
sketch_mar11a.cpp.o: In function loop': C:\Program Files (x86)\Arduino/sketch_mar11a.ino:43: undefined reference to rfm69_node<4>::loop()’
sketch_mar11a.cpp.o: In function __static_initialization_and_destruction_0': C:\Program Files (x86)\Arduino/sketch_mar11a.ino:28: undefined reference to rfm69_node<4>::rfm69_node()’
C:\Program Files (x86)\Arduino/sketch_mar11a.ino:28: undefined reference to `rfm69_node<4>::~rfm69_node()’
collect2.exe: error: ld returned 1 exit status

Any ideas?

Thanks

I'd keep it simple and not use template. Just pass in the pointer and size and use memcpy.

Besides, you said the array is a constant or global? Why make a copy of it in the first place. It's a waste of memory. Just pass in the pointer. You can read the array like usual.

mistergreen:
I’d keep it simple and not use template. Just pass in the pointer and size and use memcpy.

Besides, you said the array is a constant or global? Why make a copy of it in the first place. It’s a waste of memory. Just pass in the pointer. You can read the array like usual.

Storing a pointer and length is a waste of memory when the information is already known, this is what the template avoids. Your method is best suited to a non constant design (dynamic memory).

@OP

The ‘<4>’ inside the class (on constructor/destructors) is not needed. You also need to move your function definitions for the class out of the .cpp and place underneath the class in the header.

Your array inside the class is not a pointer or reference, and you have an array of devices outside the class. This means you’ll have to manually assign each value to the internal class (and have two copies).

You should remove the constructors and create like the POD version added last. Otherwise the version using a constructor and reference is more appropriate (second last version).

If it's a constant, why bother using template and making a copy? You can access the original array from anywhere by just passing the pointer.

mistergreen: If it's a constant, why bother using template and making a copy? You can access the original array from anywhere by just passing the pointer.

None of the methods I have shown copy the data. In fact the first method is exactly what you propose.

The second and third methods do relatively the same task, just far more efficient in terms of code that can be produced by the compiler.

The fourth method does away with taking a pointer/reference altogether.

mistergreen:
I’d keep it simple and not use template. Just pass in the pointer and size and use memcpy.

Besides, you said the array is a constant or global? Why make a copy of it in the first place. It’s a waste of memory. Just pass in the pointer. You can read the array like usual.

mistergreen:
If it’s a constant, why bother using template and making a copy?
You can access the original array from anywhere by just passing the pointer.

@mistergreen
If I pass a pointer then I need to store the length (which isn’t as bad as storing a whole new copy I guess) so I know how long the array is.

At present I’ve gone for this approach as it’s working for me but it’s probably not the optimum solution.

I’m ideally looking for a solution where the rfm69_node class can access the user defined structure/memory locations for the Devices array and has access to the metadata like the size.

@pYro
I’m still lost with your template advice. I’ve done more reading and seem to understand the concept, however I don’t seem to be able to implement it.

pYro_65:
The ‘<4>’ inside the class (on constructor/destructors) is not needed.

The <4> was from when I declared the object in the sketch. The <4> isn’t present in the .h or .cpp files.

pYro_65:
You also need to move your function definitions for the class out of the .cpp and place underneath the class in the header.

If I do this can I get rid of the .cpp file?

pYro_65:
You should remove the constructors and create like the POD version added last. Otherwise the version using a constructor and reference is more appropriate (second last version).

Are PODS just structs or can a POD be a class?