0

I've occured problem looping through array of structs.

I've got this struct:

struct sensor
{
  float data = 0;
  bool status = false;
  Idx idx; //enum
  SensorType type; //enum
};

It contains some sensors for my arduino.

So to send data, that I've recieved from them I want to use array of sensors in loop.

This is my array definition:

sensor sensors[] = {light, pressure, pressureHpa, temperature, humidity, temperatureOut, humidityOut};

Finally, I want to loop through this array and call some methods, but when I try to access any sensor by index I get zero values for all properties.

  for (unsigned int i = 0; i < sizeof(sensors) / sizeof(sensor); i++)
  {
    Serial.print("type:");
    Serial.print(sensors[i].type);
    Serial.print(" sens.idx:");
    Serial.print(sensors[i].idx);
    Serial.print(" sens.data:");
    Serial.print(sensors[i].data);
    Serial.print(" sens.status:");
    Serial.print(sensors[i].status);
    Serial.println();
  }

In this loop I've got output like:

type:0 sens.idx:0 sens.data:0.00 sens.status:0
type:0 sens.idx:0 sens.data:0.00 sens.status:0
type:0 sens.idx:0 sens.data:0.00 sens.status:0
type:0 sens.idx:0 sens.data:0.00 sens.status:0
type:0 sens.idx:0 sens.data:0.00 sens.status:0
type:0 sens.idx:0 sens.data:0.00 sens.status:0
type:0 sens.idx:0 sens.data:0.00 sens.status:0

But, when I access any of my structs directly like:

Serial.print("type:");
Serial.print(pressure.type);
Serial.print(" pressure.idx:");
Serial.print(pressure.idx);
Serial.print(" pressure.data:");
Serial.print(pressure.data);
Serial.print(" sens.status:");
Serial.print(pressure.status);
Serial.println();

I get:

type:3 pressure.idx:13 pressure.data:0.00 sens.status:1
  • 1
    Please **[edit]** your question with an [mre] or [SSCCE (Short, Self Contained, Correct Example)](http://sscce.org) – NathanOliver Sep 10 '19 at 16:39
  • Do not count on the value returned by sizeof() for an struct. – TonySalimi Sep 10 '19 at 16:41
  • Please post the complete code – Suraj Sep 10 '19 at 16:42
  • @Gupta Do you mean "for an array"? – Max Langhof Sep 10 '19 at 16:46
  • @Gupta I've already checked sizeof() values, they are correct – Ivan Kostiashov Sep 10 '19 at 16:54
  • Assuming `sensor sensors[] =...` and `for (unsigned int i = 0; i < sizeof(sensors) / sizeof(sensor); i++)` are in the same scope, calculating the size should work. – user4581301 Sep 10 '19 at 16:54
  • @Suraj https://www.paste.org/100357 – Ivan Kostiashov Sep 10 '19 at 16:56
  • Recommend placing the code in the question (in [mcve] form) . Links rot and get blocked by firewalls, reducing their utility as a data source. – user4581301 Sep 10 '19 at 16:57
  • `sensor sensors[] = {light, ...};` is making copies. Is `sensor` safely copy-able? – user4581301 Sep 10 '19 at 16:59
  • 2
    Ah smurf. That's sort of the problem. You set `light`, and print `sensors[0]`. Because `sensors[0]` is a copy of `light` made before `light `is set, `sensors[0]` contains the default values. – user4581301 Sep 10 '19 at 17:02
  • @Gupta Why not? Do you have some source that says that sizeof() does not work for structs? – Will Eccles Sep 10 '19 at 18:09
  • @MaxLanghof https://stackoverflow.com/questions/7351654/why-does-sizeof-give-wrong-measurement – TonySalimi Sep 12 '19 at 10:30
  • @WillEccles https://stackoverflow.com/questions/7351654/why-does-sizeof-give-wrong-measurement – TonySalimi Sep 12 '19 at 10:31
  • @Gupta You still haven't told us why that is a problem here. `sizeof(someStruct)` gives you exactly the number of bytes that each element of an array of `somStruct` is apart, which is the property that is used here correctly. Please explain what you think is being done wrong. – Max Langhof Sep 13 '19 at 08:15
  • @MaxLanghof I did not tell that it is a problem. I just mentioned "Do not count on the value returned by sizeof() for an struct." just as a hint. In addition, on my VS2015 compiler, the sizeof(sensor) returns 16, that is not the EXACT size of the struct. – TonySalimi Sep 13 '19 at 08:55
  • @Gupta You seem to misunderstand/have the wrong expectations for what `sizeof` does. `sizeof` returns _by definition_ the size of the struct (the number of bytes occupied by a standalone instance of it). There is no "exact" size other than what `sizeof` returns, because there simply is no other size. You seem to have some idea/expectation that the sum of the struct member sizes is "more exact", but this value is simply irrelevant for the size of a struct. Everything becomes really simple if you accept that instead of considering `sizeof` to be inexact. – Max Langhof Sep 13 '19 at 09:18

2 Answers2

2

light is defined and left at its default values.

sensor sensors[] = {light, pressure, pressureHpa, temperature, humidity, temperatureOut, humidityOut};

copies light into sensors[0]. This means sensors[0] contains the same data as light, but is not the same thing as light. Later when the code assigns values to light, sensors[0]'s copy is left untouched.

Option 1

Discard light and friends. Add an enumerated type that names the sensors

enum sensor_names
{
    LIGHT, // will be 0
    PRESSURE, //will be 1
    ...
    NR_SENSORS // must be last. Used to size the array and for loop terminators
};

And make sensors a simple array of the correct size

sensor sensors[NR_SENSORS];

Feel free to use std::array and the smarter enums of modern C++ if they are available. I'm weak on what is and isn't available to Arduino, so I've stuck to basic C-Style arrays and enums. If you have better tools, use them.

Then access that which was light as sensors[LIGHT];

Option 2

Make sensors store pointers and change the accessing accordingly.

sensor * sensors[] = {&light, &pressure,  .... };

I prefer the first option. No duplication and a lower chance of placing an item in the wrong place in the array making a difference.

Community
  • 1
  • 1
user4581301
  • 33,082
  • 7
  • 33
  • 54
1

When you create an array of structs:

sensor sensors[] = {light, pressure, pressureHpa, temperature, humidity, temperatureOut, humidityOut};

This creates a copy of each struct you're using to make the list. You either have to use an array of pointers:

sensor* sensors[] = {&light, &pressure, &pressureHpa, &temperature, &humidity, &temperatureOut, &humidityOut};

Or you have to create your own class, which stores a reference to them:

class SensorArray {
    sensor** arr = nullptr;
    size_t count = 0; 
   public:
    size_t size() const {
        return count; 
    }
    // Default-construct it
    SensorArray() = default;

    // Make a copy of the SensorArray 
    SensorArray(SensorArray const& source)
      : arr(new sensor*[source.size()])
      , count(source.size())
    {
        std::copy_n(source.arr, source.count, arr);
    }

    // Move-construct SensorArray
    SensorArray(SensorArray&& source)
      : arr(source.arr)
      , count(source.count)
    {
        source.arr = nullptr; 
        source.count = 0; 
    }

    template<class T...>
    SensorArray(T&... sensors)
      : arr(new sensor*[] { &sensors... }) // Initialize arr
      , count(sizeof...(sensors))          // Initialize count
    {} // Constructor is empty because we already initialized stuff

    SensorArray& operator=(SensorArray const& source) {
        if(this == &source) return *this;

        SensorArray s = source;
        swap(s);
        return *this; 
    }

    // Copy-and-swap idiom
    // This automatically handles the case of self-assignment
    SensorArray& operator=(SensorArray&& source) {
        SensorArray s = std::move(source);
        swap(s);
        return *this;
    }
    sensor& operator[](int index) const {
        return *arr[index];
    }

    ~SensorArray() {
         delete[] arr;
    }
    void swap(SensorArray& other) {
        std::swap(arr, other.arr);
        std::swap(count, other.count);
    }
};

This class will work as intended in your code because it'll store a reference to each sensor, rather than a copy:

SensorArray sensors { light, pressure, pressureHpa, temperature, humidity, temperatureOut, humidityOut };

  // sensors will update correctly
for (unsigned int i = 0; i < sensors.size(); i++)
{
    Serial.print("type:");
    Serial.print(sensors[i].type);
    Serial.print(" sens.idx:");
    Serial.print(sensors[i].idx);
    Serial.print(" sens.data:");
    Serial.print(sensors[i].data);
    Serial.print(" sens.status:");
    Serial.print(sensors[i].status);
    Serial.println();
}
Alecto Irene Perez
  • 10,321
  • 23
  • 46