c++esp32

How to access data in a vector of struct, accessed by a pointer


I am trying to create a vector in PSRAM on an ESP32-S3 with PSRAM enabled. Here is my test code (using std=gnu++14).

I can: allocate some PSRAM memory, create a vector in the PSRAM, create a test record and add it to the vector and confirm the size goes up, delete records from the start of the vector and confirm the size goes down

But I can't work out how to read the vector data, see block in the middle commented out.

struct EventRecord_t { // owned by MESH_ROOT
    uint8_t  node = 0;
    uint8_t errorCount;
};

void eventsStart(){
    void* eventsRam;
    eventsRam = heap_caps_malloc(1000000, MALLOC_CAP_SPIRAM); // get memory from PSRAM
    std::vector<EventRecord_t>* eventStack; // declare pointer to vector of struct
    eventStack = (std::vector<EventRecord_t>*) eventsRam; // create vector in PSRAM
    EventRecord_t eventRecord; // create record entry and populate it
    eventRecord.node = 1;
    eventRecord.errorCount = 34;
    eventStack->push_back(eventRecord); // add record to eventstack 
    std::cout << "Size: " << eventStack->size() << std::endl; // confirm size is increasing
    eventRecord.node = 2;
    eventStack->push_back(eventRecord);
    std::cout << "Size: " << eventStack->size() << std::endl;
    eventRecord.node = 3;
    eventStack->push_back(eventRecord);
    std::cout << "Size: " << eventStack->size() << std::endl;

    // how to access eventStack data here;
    // eventRecord = eventStack[0];
    // uint8_t inode = eventStack[0]->node;

    eventStack->erase(eventStack->begin()); // delete first record from eventStack
    std::cout << "Size: " << eventStack->size() << std::endl; // confirm size is increasing
    eventStack->erase(eventStack->begin());
    std::cout << "Size: " << eventStack->size() << std::endl;
    eventStack->erase(eventStack->begin());
    std::cout << "Size: " << eventStack->size() << std::endl;
    free(eventsRam);
}

Output:

Size: 1
Size: 2
Size: 3
Size: 2
Size: 1
Size: 0

Solution

  • You need to write your own allocator

    template <typename T>
    class SPIRamAllocator {
    public:
        using value_type = T;
    
        SPIRamAllocator() = default;
    
        template <typename U>
        constexpr SPIRamAllocator(const SPIRamAllocator<U>&) noexcept {}
    
        [[nodiscard]] T* allocate(std::size_t n) {
            // Allocate memory using heap_caps_malloc with MALLOC_CAP_SPIRAM
            T* ptr = static_cast<T*>(heap_caps_malloc(n * sizeof(T), MALLOC_CAP_SPIRAM));
            if (!ptr) {
                /* do something */
            }
            return ptr;
        }
    
        void deallocate(T* ptr, std::size_t) noexcept {
            // Free the allocated memory
            heap_caps_free(ptr);
        }
    };
    
    template <typename T, typename U>
    bool operator==(const SPIRamAllocator<T>&, const SPIRamAllocator<U>&) {
        return true;
    }
    
    template <typename T, typename U>
    bool operator!=(const SPIRamAllocator<T>&, const SPIRamAllocator<U>&) {
        return false;
    }
    

    and then you can use it:

    std::vector<int, SpirAMAllocator<int>> vec;
    std::vector<EventRecord_t, SpirAMAllocator<EventRecord_t>> vec;
    
    

    This allocator is not too good, but it is just to show how to write them. You may want to provide max size for the vector, allocate in larger chunks or combine any other methods.