Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fast approach to wrapping data in a struct/class

Tags:

c++

EDIT: The main intent is to allow manipulating underlying data as part of an encapsulated struct as opposed to direct data manipulation.

Which of the following approaches is recommended when it comes to wrapping some data inside a struct:

  1. Keep a pointer to the data within the struct:

    new s(buf), which stores buf in a local field (s->buf = buf)

  2. reinterpret_cast-ing a memory address to a struct:

    reinterpret_cast<s*>(buf)

  3. Use the new operator against the memory address where the data is located:

    new(buf) s;

Here is a sample program for these approaches:

#include <iostream>
using namespace std;

struct s {
    int* i;
    s(int* buf) : i(buf) {}
    int getValue() { return *i * 2; }
};

struct s2 {
    int i;
    int getValue() { return i * 2; }
};

int main() {
    int buf = 10;
    s a(&buf);
    cout << "value: " << a.getValue() << ", size: " << sizeof(a) << ", address: " << &a << ", buf-address: " << &buf << endl;

    s2* a2 = new(&buf) s2;
    cout << "value: " << a2->getValue() << ", size: " << sizeof(*a2) << ", address: " << a2 << ", buf-address: " << &buf << endl;

    s2* a3 = reinterpret_cast<s2*>(&buf);
    cout << "value: " << a3->getValue() << ", size: " << sizeof(*a3) << ", address: " << a3 << ", buf-address: " << &buf << endl;
}

And the output:

value: 20, size: 4, address: 0027F958, buf-address: 0027F964
value: 20, size: 4, address: 0027F964, buf-address: 0027F964
value: 20, size: 4, address: 0027F964, buf-address: 0027F964

Both size & time are important. Also, maintainability is important, e.g. someone might add by mistake a virtual function to s2 (which will mess up the data alignment).

Thanks!

like image 995
Nick Avatar asked Jun 20 '11 15:06

Nick


2 Answers

None of those are even remotely good ideas, although the first one is passable with some modifications. reinterpret_cast doesn't work the way you think it does, and I'm not sure what exactly you're trying to achieve with placement new. Store a smart pointer of some sort in the first one to avoid the obvious issues with lifetime, and the first option isn't bad.

There is a fourth option: just store the data in a struct, and provide whatever encapsulated access you desire.

struct data {
    data(int i_) : i(i_) { }
    int i;
};

struct s {
    s(int i_) : i(i_) { }
    data i;
};

Rereading your question, it appears as though maybe your intent is for this struct to be an internal detail of some larger object. In that case, the lifetime issues with the first solution are likely taken care of, so storing a raw pointer is less of a bad idea. Absent additional details, though, I still recommend the fourth option.

like image 199
Dennis Zickefoose Avatar answered Nov 15 '22 07:11

Dennis Zickefoose


Placement new will still call the constructor, wiping out anything that's in the buffer already if such a constructor exists (or is created unknowingly in the future) so I don't think that's a safe option. reinterpret_cast is undefined behavior even though it may appear to work for you. Storing a local pointer seems to be the best option although you've only given a very tiny inkling of what you're trying to do.

If you're attempting serialization here, remember important issues like sizeof(int) and endianness.

like image 36
Mark B Avatar answered Nov 15 '22 09:11

Mark B