Can a struct in a vector access its index in the vector?

I am building a vector of structs. The struct will contain a data member which is a pointer to an array of double. I need to have the struct member pointer to point to the corresponding element of the array of double. I could just iteratively set the value of the struct member pointers to point to the corresponding element of the array. I wonder though if there is a mechanism in which the struct already knows its index. Otherwise, each time I modify the vector by adding, deleting or concatenation, I will have to iterate through the entire vector to reset the member pointer.

Thanks,
Chris
Try a <map> instead of a vector.
Hi,

There is std::distance

https://en.cppreference.com/w/cpp/iterator/distance

Does that help? :+)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include <iostream>
#include <map>
#include <string>

struct ArrayPointer
{
    std::string name;
    double *ptrToArray{nullptr};
    int arraySize{0};

    ArrayPointer(){};
    ArrayPointer(std::string aName, int aSize, double* ptr)
    {
        name = aName;
        arraySize = aSize;
        ptrToArray = ptr;
    }

    double* getArray() { return ptrToArray; }
    double getElement(size_t aReference) { return getArray()[aReference]; }
};

int main()
{
    // A MAP
    std::map<std::string, ArrayPointer>::iterator iter;
    std::map<std::string, ArrayPointer> map_APtr;

    // SOME ARRAYS
    double arr1[]{1,11,111};
    double arr2[]{2,22,222,2222};
    double arr3[]{3,33,333,3333,33333};

    // SOME struct's
    ArrayPointer a1("ONE", 3, arr1), a2("TWO", 4, arr2), a3("THREE", 5, arr3);

    // FILL MAP
    map_APtr[a1.name] = a1;
    map_APtr[a2.name] = a2;
    map_APtr[a3.name] = a3;


    // ACCESS ARRAYS
    iter = map_APtr.find("TWO");
    std::cout << iter -> second.getElement(2) << '\n';

    iter = map_APtr.find("THREE");
    std::cout << iter -> second.getElement(4) << '\n';

    return 0;
}



222
33333
Program ended with exit code: 0
which is a pointer to an array of double
I modify the vector


You mention an array and vector. Which are you using - as these are not the same.

each time I modify the vector by adding, deleting or concatenation


If you are only adding or deleting at the end of the vector, then you need only check if a re-allocation has happened by examining the value of .capacity(). It won't happen every time an item is added at the end

Also, in this case if you know the maximum number if elements that a vector will hold, then you can use .reserve() to reserve enough memory so that re-allocation doesn't happen.

Unless you need direct access to each element, consider a container such as a list which doesn't re-allocate instead of a vector.
unclear if this helps but
the INDEX does not change.
that is if you have 0,1,2,3,4 in the array, and the struct points to &array[2] (the 3) and you stuff 1000 more numbers on it so it reallocates, then:
the index [2] is the same as it was (its still the 3) but the &array[2] is different.
therefore, store the index solves the problem, and if you need the pointer, you can cook it on the fly and it is valid until the next resize (return &array[2] and you get the current correct one, and you still kept the [2] as what you store, you just *return* the pointer).

this is fine for the internals deep inside the magic that you do not expose. If you expose this, some user may keep the pointer you provided around and it could go invalid and cause a crash/bug/problem. You must NOT expose the pointer to a vector's data to something that could HOLD ONTO it for use later, and multi-threaded programs it can go invalid almost as fast as you can pass it back if one of the threads is adding to the vector! Use caution with this if you use it at all. INSTEAD: expose the INDEX and that will be safe (or if not safe, you will KNOW that because you called pop back or clear or whatever that invalidated it or you can use .at instead of []).
Last edited on
Thanks guys for all the responses.

I am building a program that requests variables from a game. I want to make it demand driven. So I have an array of perhaps 100 game variables. The clients will ask for certain variables and as soon as a request is made I put that variable on a vector. If the variable is not requested after perhaps 10 cycles, it gets removed from the vector. So far, the data seems to be coming in at the same size ( double ). Instead of making N requests for N variables, I could actually make 1 request for N variables. The data returns as an array of double. So I could actually memcpy the array into an array of double. The challenge though now is I need to associate a pointer data member to the corresponding location in the array of double:

1
2
3
4
5
6
7
class SimRequest {
      int id;
      double * index;  // index to array of double.
      static double dvalue[100];
};
SimRequest::dvalue[] = {};


Basically, what I need is something like :

1
2
3
4
5
6

double getvalue(){
        return dvalue[getmyvectorposition()];
}



If that is possible with very minimal performance impact, then I don't have to iterate over the vector to reassign the index pointer to double whenever I make a change to the vector. Each time I use the object, it automatically knows its associated buffer.

I am also thinking that instead of a vector of SimRequest objects, I will just use a vector of Simrequest pointers. That way, I avoid making a copy. The issue is the same though.

There is probably a significant performance impact with the vector resizing with inserts and deletes. I am open to a more performant solution if there is one.

Can a copy or move constructor be utilized to achieve this? As a vector resizes, it uses a copy constructor to move things up and down I think. I don't know if move constructor can work in this scenario because vector memory has to be contiguous. You have to copy the old object into a new memory address. Can the copy constructor drop an index value in the object, perhaps based on the distance between the new copy and the first vector element? The copy constructor will have to distinguish whether the copy is because of a vector being modified or if it is because of some other function in the program that requires a copy to be made. Perhaps as a test, the address of the object must fall within the memory address reserved for the vector. But if it is an index of SimRequest pointers moving up and down a vector, then perhaps it is unique enough. I might be going down the rabbit hole here.


Thanks,
Chris
Last edited on
I still don't understand. I guess there's a vector<request> and an array of double and some request type that contains a double*, but somehow the value of the double* depends on the position of the request object in the vector's buffer. And maybe there's a least-recent-use data structure involved.

What is the purpose of this?
What should a use case look like?
How should the data flow through the application?

We're far into XY problem territory.
Last edited on
This is a Simconnect App. Basically it is an intermediary between the game MSFS2020 and any device or peripheral that would require the in game data. For example, LED displays attached to an arduino could display the in game settings for Navigation Radio Frequency, or an LED light can signal that the plane is flying on autopilot. In this scenario, then the arduino which is attached via usb to the PC can request data from the SimConnect App which in turn requests the data from MSFS 2020. There could be multiple arduino's requesting data.

The ingame variables can be requested in multiple formats. I pick the one where I get an array of double in the data field, with each double corresponding to a ingame variable. I needed to quickly read the data ( into the array of double ) and associate each element of the array of double with each active SimRequest object.
@cmsip

At the considerable risk of adding again to what I agree is an xy problem of your making, given your last comments and references to Arduino hardware, this situation appears ( famous last words ) to be analogous to a GPS data output being read and processed by an Arduino. The stream of data (NMEA) format/meaning is emitted and received as a serial stream that is tokenized and processed by the Arduino.

All of this can be done via a class structure or as non O-O with or without library functionality you can't write yourself. There is no magic.

If you are aware of how an Arduino works and how to program it then I suggest you look at the following sites. The value is not that you are making a gps tracker but how you can handle (tagged $abcd in this case) data via the Serial port, with I2c for multiple sources coming later once you have one working.

1. https://create.arduino.cc/projecthub/muchika/vehicle-tracking-system-based-on-gps-and-gsm-57b814

2. https://www.gpsworld.com/what-exactly-is-gps-nmea-data/

I would start just by getting the Arduino to capture and reproduce the data it receives.

Of course none of this might be what you are on about so good luck wit xy -> z
so far I have not heard anything new that indicates the index into the array isnt a better and easier solution to your issue.
The value is not that you are making a gps tracker but how you can handle (tagged $abcd in this case) data via the Serial port


I am actually working on that right now. I am finding out that it seems to be less a pipe and more a "soup". Arduino requests data by sending a uint8_t ID. Then it reads a uint16_t. I thought that was enough, that I am supposed to get the data I requested because on the arduino, I try to read the port after I write the ID to the port. The next uint16_t has to be the response. However, while the order of transmit on the PC side is correct, they are not received in the proper order on the Arduino. The only reliable way so far is to have the PC return the uint8_t ID with the uint16_t data. Then by doing a do while loop on the arduino, when I check the first byte, I know the next two bytes are correct.

Regarding the topic of this question. And there may be better solutions that I haven't considered. Basically I will have an array (perhaps a vector) of objects of SimRequest. This will be maybe 75 elements in size. However, I only probably am interested in 25 at the moment. So I decided to have a bool class member indicate which of the 75 I am interested in. As the arduino sends its request for data, I will mark the bool class member as active and increment a global counter of number of new data subscriptions requested by the arduino.

1
2
3
4
5
6
7
8
9
class SimRequest {
      int id;
      double * index;  // index to array of double.
      static double dvalue[100];
      bool active=false;
      int counter=0; //how many requests for this data so far
};
SimRequest::dvalue[] = {};



So on program start, I iterate over the 75 elements, pick out the ones where active==true and build a smaller vector containing maybe 25 objects. Then iterate over the vector of 25 elements and set their index pointer value to consecutive memory addresses of dvalue[] array.

Whenever a serial port requests data about a particular SimRequest object, I increment a counter in the object.

Whenever I finnish a data retrieval cycle (PC app requests data from game). I increment a global int num_cycles.

After a certain amount of time, if there are new subscriptions requested, I push that into the smaller vector. At the same time, I evaluate all the vector's elements and if an object's internal counter is below a certain threshold ( less than num_cycles), I delete that from the vector. I also zero out the object internal counter.

If I delete an element in the middle of the vector, what I was hoping for is a way that the elements after the deleted element would automatically change the memory address "SimRequest::index" is pointing to by one memory address up.

I think the solution will have to be, since I know which element to delete from the smaller vector, I need to null its index pointer, move to the next element and adjust its index upwards until I get to the last element. Then I can delete the element in the middle whose pointer I just nulled.

I am trying to avoid having to iterate over 75 objects whenever I need to make a subscription change.

Thanks,
Chris
I am trying to avoid having to iterate over 75 objects whenever I need to make a subscription change.

maybe try unordered map<> ?

75 items is effectively 1 on a current PC. Its still good to think about a better way, but this is too small to spend a ton of effort on, even if you do it 1000 times a second. The computer can iterate well over a million items in a second for even casual code.
Last edited on
You're right. There's not much to gain going down this road. The simpler solution would be best.
Thanks,
Chris
Topic archived. No new replies allowed.