2

Suppose I have two structures as follows:

struct address{
  int x;
  int y;
} addr;

struct details{
  int count;
  int size;
  addr addre[1];// Instances of count number of addresses
} detail;                 

How to create one variable, say det, which have multiple instances of addre as defined by the count number?

YSC
  • 38,212
  • 9
  • 96
  • 149
  • 3
    Are you trying to make array of one-element arrays? How about using `std::vector` instead? – Yksisarvinen May 09 '18 at 09:43
  • 2
    Apparently, `c++` tag is sufficient. You might want to remove `algorithm` and `data-structures` tag. – Shridhar R Kulkarni May 09 '18 at 09:45
  • 1
    Can someone explain the question to me, I don't get it. Furthermore, how is `addr addre[1]` valid code? `addr` is a variable, not a type. – Timo May 09 '18 at 09:56
  • I assume this is a legacy from C, like Windows' `BITMAPINFO`. The idea is that you define an array of size 1 as last member of a struct, and `malloc` (resp `new char[]`) enough memory for the struct + all array elements. This guaranees continuous storage of the data. In this case, it would be `sizeof(details + (count-1)*sizeof(address))` – king_nak May 09 '18 at 10:14
  • Are you looking for [Flexible_array_member](https://en.wikipedia.org/wiki/Flexible_array_member) ? unsupported in C++ see [are-flexible-array-members-valid-in-c++](https://stackoverflow.com/questions/4412749/are-flexible-array-members-valid-in-c) – Jarod42 May 09 '18 at 10:30
  • thanks @king_nak could you help in finding a proper place to study about this – user3902834 May 09 '18 at 10:37
  • It's apparently called variable sized struct. You can read about it for example here: http://www.drdobbs.com/questions-answers-creating-variable-siz/184403480 But if you're using C++, I'd use the answer by Maxim Egorushkin below – king_nak May 09 '18 at 11:31

3 Answers3

2

This is common trick to reduce the number of memory allocations and improve locality of references by allocating containers of a dynamic size at the end of an object (with a fixed size).

In C++ though, using that extra member[1] causes a bit of trouble - that member is automatically initialized, while the rest of the elements are not. It is better to avoid declaring that member at all and rather provide accessors/iterators for the elements. And then initialize and destroy all the members manually. E.g.:

struct address {
    int x;
    int y;
};

struct details {
    int count;
    int size;

    address* addr_begin() { return reinterpret_cast<address*>(this + 1); }
    address* addr_end() { return addr_begin() + count; }

    static void* operator new(size_t sizeof_details, int count) {
        return ::operator new(sizeof_details + count * sizeof(address));
    }

    static void operator delete(void* p) {
        ::operator delete(p);
    }

    static std::unique_ptr<details> create(int count, int size) {
        return std::unique_ptr<details>(new(count) details(count, size));
    }

    ~details() {
        std::for_each(addr_begin(), addr_end(), [](address& a) { a.~address(); });
    }

private:
    details(int count, int size)
        : count(count)
        , size(size)
    {
        std::uninitialized_fill(addr_begin(), addr_end(), address{});
    }
};

int main() {
    auto det = details::create(10, 10);
}

If you cannot change the structures, then:

#include <new>
#include <algorithm>

struct address {
    int x;
    int y;
};

struct details {
    int count;
    int size;
    address addre[1];
};

details* create_details(int count, int size) {
    void* mem = ::operator new(sizeof(details) + (count - 1) * sizeof(address));
    auto* p = new (mem) details{count, size};
    std::uninitialized_fill(p->addre + 1, p->addre + count, address{});
    return p;
}

void destroy_details(details* p) {
    std::for_each(p->addre + 1, p->addre + p->count, [](address& a) { a.~address(); });
    p->~details();
    ::operator delete(p);
}

int main() {
    auto* p = create_details(10, 10);
    destroy_details(p);
}
Maxim Egorushkin
  • 131,725
  • 17
  • 180
  • 271
0

If I get your question correctly (I'm not sure as it is quite unclear), you have a structure

struct address {
  int x;
  int y;
};

and you want to define another, details, which holds a collection of details.count instances of address. You basically have two choices.

details.count is known at compilation time

In this case, your best option is to define count as a non-type template parameter and use std::array:

template <std::size_t COUNT>
struct details {
    static constexpr std::size_t count = COUNT;
    std::array<address, COUNT> addresses;
};

// ...

details<42> det;
// det.addresses[0] to det.addresses[41] are valid (but uninitialized) addresses

details.count is unknown at compilation time

In this case, your best option is to use std::vector and provide count to the construction, or even to add addresses to details.addresses after construction:

struct details {
    std::vector<address> addresses;
};

// ...

details det;
det.addresses.emplace_back(address{0, 0}); // adds and initializesdet.addresses[0]
YSC
  • 38,212
  • 9
  • 96
  • 149
0

Beaten by some seconds by YSC, just leaving me some addition:

If size and capacity is all that you need additionally, you might even use the vector directly:

using Details = std::vector<address>;

As you apparently need size and capacity, doing the same with std::array probably won't fit your needs, so you likely will stay with aggregation then...

Aconcagua
  • 24,880
  • 4
  • 34
  • 59