Is it possible to write a "complete" C++ class with Zero Length Array member?

375 Views Asked by At

I have some data type which, if I were to use plain old C, would be implemented as

typedef struct {
    ...many other members here...
    unsigned short _size;
    char           _buf[0];
} my_data; 

What I'd like to do, is to basically make that a class and add the usual operators like less, equality, copy constructor, operator assignment, and so on. As you can imagine I would then be using such class in associative containers like std::map as its key.

I need the buffer to be ideally at the same level of the object itself, otherwise when I have to compare two of them (buffers) I would have the CPU to take the pointer and load it in memory; I don't want to use std::vector because memory allocated wouldn't be contiguous with the rest of the data members.

Main issue for me is the fact that in C I would have a function which, given the size of the buffer would allocate proper memory size for it. In C++ such thing can't be done.

Am I right? Cheers

3

There are 3 best solutions below

6
On BEST ANSWER

This is quite impossible. Your object is effectively of variable size but the std::map will always treat it as a fixed size, and there is no way to implement copying or moving. You would need an old C-style container to use such a hack.

Edit: Custom allocator. Interesting solution, I hadn't thought of that. I don't know if you could make it work but it would be worth looking into.

13
On

You can approach it this way in C++:

struct MyData {
    unsigned short size_;
    char * buf () { return reinterpret_cast<char *>(&size_ + 1); }
    //...
};

You are likely going to want to overload the new operator to use malloc, and delete to use free, so that you can grow your data structure on demand with realloc.

As DeadMG points out, this cannot be used with STL containers very easily. One approach would be to use pointers to MyData in the containers. Another would be a custom smart pointer wrapper class for MyData.

Edit: This is a hack, where MyData acts as a kind of smart pointer, but the intelligence is managed by vector.

struct MyData {
    struct State {
        unsigned short size_;
        //...
    };
    std::vector<State> data_;
    MyData () {};
    MyData (unsigned short size)
        : data_(1 + size/sizeof(State) + !!size%sizeof(State)) {
        data_[0].size_ = size;
    }
    unsigned short size () const { return data_[0].size_; }
    //...
    char * buf () { return reinterpret_cast<char *>(&data_[1]); }
};
0
On

No, it's not possible. Zero length arrays aren't legal C++.

You can1 do something very similar with an array of length 1, but you would still have to manage creation of instances yourself, so no copy constructor and no storing the objects in std::map.

Perhaps something like this:

class my_data {
public:
  static my_data* create(int size) {
    void* memory = malloc(sizeof(_size) + size);
    return new(memory) my_data(size);
  }

  static void destroy(my_data* ptr) {
    ptr->~my_data();
    free(ptr);
  }

private:
  //disable construction and destruction except by static methods above
  explicit my_data(int size) : _size(size) {}
  ~my_data(){}

  //disable copying
  my_data(const my_data&);
  my_data& operator=(const my_data&);

  unsigned short _size;
  char           _buf[1];
};

Note that the default constructor, destructor, copy constructor and assignment operator are all private, which greatly restricts how the class can be used.


1 In practical terms - it's not standards compliant, but it will work almost everywhere.