Smart pointer for arrays of structs?

H

Henrik Goldman

Hi,

I have some data which is stored as plain old C structures. Until now I've
had arrays of these structs on stack but due to stack overrun I need to move
these to heap.
I've been trying out some smart pointers to help but so far the
implementation I've been dealing with has had problems with [] operator
since it could conflict when using the same class with "unsigned char".

Do you have any recommendations for smart pointers able to work with arrays?

I would need something like:

ptr = (new or malloc) MY_STRUCT [16];

ptr.member

Thanks.

-- Henrik
 
G

Gianni Mariani

Henrik said:
Hi,

I have some data which is stored as plain old C structures. Until now I've
had arrays of these structs on stack but due to stack overrun I need to move
these to heap.
I've been trying out some smart pointers to help but so far the
implementation I've been dealing with has had problems with [] operator
since it could conflict when using the same class with "unsigned char".

Do you have any recommendations for smart pointers able to work with arrays?

I would need something like:

ptr = (new or malloc) MY_STRUCT [16];

ptr.member


What is wrong with std::vector ?
 
H

Henrik Goldman

What is wrong with std::vector ?

I forgot to mention that it's a requirement that the data needs to be
accessible from external C functions. For this reason it needs to be
serialized just like an ordinary array.

-- Henrik
 
S

Salt_Peter

Henrik said:
I forgot to mention that it's a requirement that the data needs to be
accessible from external C functions. For this reason it needs to be
serialized just like an ordinary array.

-- Henrik

The std::vector is then perfect for the job. Not only are its elements
organized in contiguous memory and dynamic, its also much easier to
overload a global op<< to stream/serialize the data.

Show me code with a primitive array, and i'll show you a std::vector
that does the same but better, and then some. And that includes
performance.
 
H

Henrik Goldman

Thank you Gianni, Jim, Peter, for your answers.

It seems to be a good choice. It certainly saved me from a day of work
trying to get a smart pointer class working to do the same job.

The way I use it is:

vector <mytype> v;

v.resize(nElements);
and then
memset(&v[0], 0, sizeof(mytype) * v.size());

It's too bad I cannot do all the initialization at once. However the data
needs to be zero initialized and have a certain size. All in all it does
save me from some work so it's a cheap price to pay.

-- Henrik
 
B

BobR

Henrik Goldman wrote in message
Thank you Gianni, Jim, Peter, for your answers.

It seems to be a good choice. It certainly saved me from a day of work
trying to get a smart pointer class working to do the same job.
The way I use it is:

vector <mytype> v;

v.resize(nElements);
and then
memset(&v[0], 0, sizeof(mytype) * v.size());

It's too bad I cannot do all the initialization at once. However the data
needs to be zero initialized and have a certain size. All in all it does
save me from some work so it's a cheap price to pay.
--- Henrik

std::size_t nElements( 100 );
std::vector <mytype> v( nElements, mytype(0) );
 
S

Salt_Peter

Henrik said:
Thank you Gianni, Jim, Peter, for your answers.

It seems to be a good choice. It certainly saved me from a day of work
trying to get a smart pointer class working to do the same job.

The way I use it is:

vector <mytype> v;

v.resize(nElements);
and then
memset(&v[0], 0, sizeof(mytype) * v.size());

It's too bad I cannot do all the initialization at once. However the data
needs to be zero initialized and have a certain size. All in all it does
save me from some work so it's a cheap price to pay.

-- Henrik

Oh yes you can. In more ways than one.
There is no need to use memset, let a ctor and copy-ctor do the work
for you.

#include <vector>

template< typename T >
class mytype
{
T t;
public:
mytype() : t() { }
mytype(const T& r_t) : t(r_t) { }
};

int main()
{
std::vector< mytype< double > > instance(1000); // same as
instance(1000, 0.0);
std::vector< mytype< double > > another(1000, 11.1);
std::vector< std::string > vstrings(1000, "default string");
std::vector< int > vn(1000, 99);
}

The first creates an instance of a std::vector with 1000 elements all
initialized to 0.0 (by def ctor)
The second initializes 1000 elements with a value of 11.1 (using
parametized ctor)
The third initializes 1000 std::strings with the same default.
The fourth has 1000 integers set to 99.

The only requirement is that the element-type be copyable and
assignable (op=). In the case involving the class above, mytype's
compiler-generated copy ctor and assignment operator fits the bill. In
the event your class doesn't have a compiler-generated ctor or op=,
make them.
 
C

Clark S. Cox III

Henrik said:
I forgot to mention that it's a requirement that the data needs to be
accessible from external C functions. For this reason it needs to be
serialized just like an ordinary array.

Again, what's wrong with std::vector? :)

std::vector<T> vec = ...;

extern "C" T *GetCArray()
{
return &vec.front();
}
 
E

Earl Purple

Henrik said:
vector <mytype> v;

v.resize(nElements);
and then
memset(&v[0], 0, sizeof(mytype) * v.size());

It's too bad I cannot do all the initialization at once. However the data
needs to be zero initialized and have a certain size. All in all it does
save me from some work so it's a cheap price to pay.

If mytype is a class then its constructor will automatically be called.

If mytype is a C struct that needs to be used with C headers too (thus
you can't give it any constructors), then you can still do it using a
0-initialised element to constructor your vector or call resize. You
just have to create one such struct and use it to initialise all the
other members.

Note that you could use a "static" instance of one.

Beware, by the way, that &v[0] or &v.front() is undefined behaviour if
v is an empty vector, so you should check for this and probably return
a NULL pointer when that is the case.
 
H

Henrik Goldman

If mytype is a C struct that needs to be used with C headers too (thus
you can't give it any constructors), then you can still do it using a
0-initialised element to constructor your vector or call resize. You
just have to create one such struct and use it to initialise all the
other members.

Yes in my case it's a C struct.
I don't really like your suggestion about using a preinitialized value
though. It requires even more code then other proposed solutions.
Note that you could use a "static" instance of one.

That is really not a good idea in my case since the code is used among
different projects and modules. It only makes it harder to understand.
Beware, by the way, that &v[0] or &v.front() is undefined behaviour if
v is an empty vector, so you should check for this and probably return
a NULL pointer when that is the case.

This is not the case for me fortunatly. In those places where I need it it
has been resize()'d in the constructor prior to any use.

Thanks.

-- Henrik
 
C

Clark S. Cox III

Henrik said:
Thank you Gianni, Jim, Peter, for your answers.

It seems to be a good choice. It certainly saved me from a day of work
trying to get a smart pointer class working to do the same job.

The way I use it is:

vector <mytype> v;

v.resize(nElements);
and then
memset(&v[0], 0, sizeof(mytype) * v.size());

It's too bad I cannot do all the initialization at once. However the data
needs to be zero initialized and have a certain size.

static const mytype t;
vector<mytype> v;
v.resize(nElements, t);
 
E

Earl Purple

Henrik said:
That is really not a good idea in my case since the code is used among
different projects and modules. It only makes it harder to understand.

It doesn't really need to be static, a const global will do. It should
be initialised in one module though. Although globals are normally
evil, if it's const it is less evil.

const mytype zero_my_type = { 0, 0, 0, 0 }; // etc

then my_type_vec.resize( 50, zero_my_type );

If you prefer you can scope the instance and expose the function but
you really haven't gained a lot.
Beware, by the way, that &v[0] or &v.front() is undefined behaviour if
v is an empty vector, so you should check for this and probably return
a NULL pointer when that is the case.

This is not the case for me fortunatly. In those places where I need it it
has been resize()'d in the constructor prior to any use.

I have wrapper "buffer" classes that will wrap arrays or vectors and it
also has a begin() and end() which are guaranteed to be pointers. (I
have two such classes, one for const and one for non-const). buffer is
fairly trivial to write and looks something like this:

template < typename T >
class buffer
{
T * itsData;
size_t itsSize;
public:
buffer( T* d, size_t sz ) : itsData( d ), itsSize( sz ) {}

buffer( vector< T > & vec )
: itsData( vec.empty() ? 0 : &vec[0] ),
itsSize( vec.size() )
{
}

buffer() : itsData( 0 ), itsSize( 0 )
{
}

buffer( T* first, T* last ) : itsData( first ), itsSize( last -
first )
{
}

T* begin() const { return itsData; }
T* end() const { return itsData + itsSize; }
bool empty() const { return itsSize == 0; }
size_t size() const { return itsSize; }
};

Define const_buffer exactly the same but with const T* pointers and
const std::vector<T> & and add this constructor

const_buffer( const buffer<T> & nc_buf ) : itsData( nc_buf.begin()
), itsSize( nc_buf.size() )
{
}

Note that these classes do not take ownership of the buffer so there is
no memory handling issue. You might be surprised that begin() and end()
are const methods even in buffer. They are because you cannot modify
the pointers themselves, only what they point to. The class is
non-mutable (cannot be changed) other than assigning it to another
instance. Note that its constructors that take one parameter are
purposely non-explicit.

Now with a vector that might be empty and passing it to a functino that
takes a pointer and size that might be empty you can always pass in

buffer( theVec ).begin() (or const_buffer( theVec ).begin() if
appropriate)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,432
Messages
2,571,680
Members
48,796
Latest member
Greg L.

Latest Threads

Top