user1151695
user1151695

Reputation: 39

Access a member of all structs in an array

I have a struct Foo;

typedef struct {

    int bar;
    char baz;

} Foo;

Suppose I then declare an array of Foo as;

Foo* arr = new Foo[300];

And proceed to initialize every member with a loop. I would like very much to be able to get an array of all members bar;

int* barr_arr = ...

What is the most efficient way to do this? Is there some way to exploit the memory layout such that I need not loop over the entire Foo array?

Since we know the memory layout in advance could we exploit the fact that we know the address of every member if we're clever about alignment?

Upvotes: 2

Views: 184

Answers (5)

Killzone Kid
Killzone Kid

Reputation: 6240

If you add a constructor to your Foo that takes the size of array, you could have only one object of Foo. You can then make it that you can access either the whole vector data or individual elements with subscript:

#include <iostream>
#include <vector>
#include <memory>

struct Foo
{
    std::vector<int> bars;
    std::vector<char> bazs;
    std::size_t size;

    Foo(size_t size, int bar = 0, char baz = 0) :
        bars(size, bar), bazs(size, baz), size{size}
    {
    }

    auto operator[](size_t n)
    {
        // if (n >= size) ...
        struct
        {
            int &bar;
            char &baz;
        } temp{ bars[n], bazs[n] };
        return temp;
    }
};


int main()
{
    Foo arr(30, 100, 'a'); // 30 items

    std::cout << arr[29].bar << std::endl;
    std::cout << arr[29].baz << std::endl;

    std::cout << arr.bars[29] << std::endl;
    std::cout << arr.bazs[29] << std::endl;

    std::unique_ptr<Foo> arr2 = std::make_unique<Foo>(25, 10, 'b'); // 25 items

    std::cout << arr2->operator[](15).bar << std::endl;
    std::cout << arr2->operator[](15).baz << std::endl;

    arr2->bars[15] = 11;
    std::cout << arr2->bars[15] << std::endl;
    arr2->bazs[15] = 'c';
    std::cout << arr2->bazs[15] << std::endl;

    return 0;
}

Demo: https://ideone.com/TiVwOT

100
a
100
a
10
b
11
c

Upvotes: 1

flamewave000
flamewave000

Reputation: 616

When you are initializing the first array, you can grab a pointer to the field inside each element and store that in a separate array.

struct Foo
{
    int bar;
    float baz;
};

const int SIZE = 5;
Foo foos[SIZE];
int *bars[SIZE];
for(int c = 0; c < SIZE; c++) {
    foos[c].bar = c;
    foos[c].baz = c;
    bars[c] = &foos[c].bar; // Grab pointer to field
}

for(int c = 0; c < SIZE; c++) {
    std::cout << "Bar Value: " << *bars[c] << std::endl;
}

Upvotes: 2

Ripi2
Ripi2

Reputation: 7198

The goal drives the design.

If your main use is to pass all bar members in a row, same for baz members, then create separate containers:

std::vector<int> bar;
std::vector<char> baz;

Then passing bar as an array is straightforward: just use bar.data().

Upvotes: 1

R Sahu
R Sahu

Reputation: 206737

What is the most efficient way to do this? Is there some way to exploit the memory layout such that I need not loop over the entire Foo array?

I don't think there is away to do that without looping. You can simplify your code by using std::transform but std::transform does loop.

Also, I would recommend using std::vector instead of allocating an array using new.

std::vector<Foo> arr(300);

....


std::vector<int> bArr(arr.size());
std::transform(arr.begin(), arr.end(), bArr.begin(), [] -> (Foo const& f) { return f.bar; });

Upvotes: 2

Linuxios
Linuxios

Reputation: 35788

If Foos typically exist in arrays, and corresponding arrays of bars and bazs often need to be accessed, I would suggest redesigning your data structures to better suit your problem. Obviously, we're not reading the code that inspired this question, but given the information provided, I might suggest something like:

struct FooArray {
  int* bars;
  char* bazes;
  size_t n_elements;
};

This removes the need to allocate a new buffer for the bar array, which, depending on how many Foos are being processed, might entail significant memory savings.

I would also note that, if you're not working at a low level and don't actually need an int* but can do with a std::vector<int>, then @R Sahu's answer is likely a more appropriate solution.

Upvotes: 1

Related Questions