-1

I have the following struct and map

struct dataStruct{
  unsigned long time;
  int32_t ch0;
  int32_t ch1;
  uint8_t state;
  int16_t temp;
  uint16_t vbat;
  int8_t rssi;
  };

std::map<uint32_t,struct dataStruct> uuidData = {};

And a loop which waits for new data and fills the map with it. (1)

for(;;)
{
    if (data_debug.is_new_data_available())
    {
        uint32_t uuid = data_debug.get_ID();
        uuidData[uuid] = {
            millis(), 
            data_debug.get_data1(), 
            data_debug.get_data2(), 
            data_debug.get_state(), 
            data_debug.get_temperature(),
            data_debug.get_battery_level(),
            data_debug.get_RSSI()
        };
    }
}

This works, but when I get data with an existing UUID, my intuition is that the old struct never gets deleted and will eventually leak.

I had an alternative code (2) below that tried to bypass this, but when the UUID was duplicated, the struct was filled with garbage. Is the code in (1) leaking or c++ automatically frees the space when the struct is no longer needed? (I know this sounds like garbage collection, but i have no idea how c++ handle structs)

(2)

    if(uuidData.count(uuid)){
        uuidData[uuid].time = millis();
        uuidData[uuid].ch0 = data_debug.get_data1();
        uuidData[uuid].ch1 = data_debug.get_data2();
        uuidData[uuid].state = data_debug.get_state();
        uuidData[uuid].temp = data_debug.get_temperature();
        uuidData[uuid].vbat = data_debug.get_battery_level();
        uuidData[uuid].time = data_debug.get_RSSI();
    }
    else{
        uuidData[uuid] = {
            millis(), 
            data_debug.get_data1(), 
            data_debug.get_data2(), 
            data_debug.get_state(), 
            data_debug.get_temperature(),
            data_debug.get_battery_level(),
            data_debug.get_RSSI()
            };
}
  • 3
    If `uuid` exists, it is reused. `uuidData[uuid] = { ... };` copies the temporary created by the `{ ... }` into the existing object in the `map`. The `map` will handle correct destruction and deallocation of the object when `uuid` is removed or the map goes out of scope. – user4581301 Oct 08 '21 at 20:48
  • 2
    Why is your intuition that the old struct never gets deleted? – Nathan Pierson Oct 08 '21 at 20:48
  • @NathanPierson Because I never explicity called delete. – Cristian Tamblay Oct 08 '21 at 20:50
  • 3
    Only `delete` what you `new`. If your code has no `new`, it needs no `delete`. – user4581301 Oct 08 '21 at 20:51
  • 1
    Minor note: as you are using C++ rather than C, it is not necessary to write `std::map`. You can simplify that to `std::map`. – Chris Oct 08 '21 at 21:08
  • @user4581301 I believe that my confusion rises on the [struct initialization](https://en.cppreference.com/w/c/language/struct_initialization) and how the memory is handled. I feel like there is not much information on that aspect. – Cristian Tamblay Oct 08 '21 at 21:17
  • If you're concerned about memory leaks, it's easy to test by executing the code in a loop and calling `ESP.getFreeHeap()` or the underlying ESP-IDF functions to confirm whether there's actually a leak. – romkey Oct 09 '21 at 17:42

1 Answers1

2

Let's break down what happens with example 1 (the correct way to do this task )

uuidData[uuid] = {
            millis(), 
            data_debug.get_data1(), 
            data_debug.get_data2(), 
            data_debug.get_state(), 
            data_debug.get_temperature(),
            data_debug.get_battery_level(),
            data_debug.get_RSSI()
        };

The

        {
            millis(), 
            data_debug.get_data1(), 
            data_debug.get_data2(), 
            data_debug.get_state(), 
            data_debug.get_temperature(),
            data_debug.get_battery_level(),
            data_debug.get_RSSI()
        };

creates and initializes a temporary dataStruct variable, an automatic variable that will only exist until it's no longer needed. When the expression ends, the temporary goes out of scope and is destroyed.

uuidData[uuid]

looks in uuidData for a key that matches uuid. If it does not find one, it creates an empty dataStruct and maps it to the key. This new dataStruct is managed by uuidData. Where it comes from and where it goes is not your concern. Once a dataStruct mapped to uuid exists, a reference to it is returned.

Now we have a reference to an existing dataStruct in the map and the temporary dataStruct. The = simply copies the contents of the temporary on the right into the the object represented by the reference on the left. Whatever was in the object on the left is overwritten but the object is still there and still managed by uuidData. It will be properly destroyed and deallocated when it is either removed from uuidData or uuidData goes out of scope. The temporary is destroyed automatically (ergo the name Automatic variable) for you when the expression is complete.

There is no possibility for a leak here.

user4581301
  • 33,082
  • 7
  • 33
  • 54