Background
I’m working with the Intel IPP Cryptographic Libraries for testing.
They define several opaque structs used for shared contexts over things like hashing and encryption which, of course, cannot be directly instantiated.
To initialize one of these opaque structs, you query for a byte size and then dynamically allocate some bytes and cast to the struct pointer.
Their examples work something like this:
int byteSize = 0;
ippsSHA256GetSize(&byteSize);
// IppsSHA256State shaCtx; // Error: incomplete type is not allowed
IppsSHA256State * shaCtx = (IppsSHA256State *)(new uint8_t[byteSize]);
// use shaCtx
delete [] (uint8_t *)shaCtx;
Question
What’s the proper way to wrap this in a scoped pointer class so that I don’t have to worry about deallocation?
Things I’ve Tried
I would think the following would not be safe since the call to delete in the destructor would be on the T type rather than a delete [] on the array that was actually allocated:
boost::scoped_ptr<IppsSHA256State> ctx(
reinterpret_cast<IppsSHA256State *>(new uint8_t[byteSize])
);
Another (simplified) option I’ve considered is a simple scoped pointer class of my own, but the casting there makes me unsure if this is correct, though my understanding of reinterpret_cast is that when cast back to the original type, there shouldn’t be any ambiguity:
template <typename T>
class IppsScopedState
{
public:
explicit IppsScopedState(size_t byteSize)
: _ptr(reinterpret_cast<T *>(new uint8_t[byteSize]))
{}
T * get() const { return _ptr; }
~IppsScopedState(void) {
if (_ptr) delete [] reinterpret_cast<uint8_t *>(_ptr);
}
private:
T * _ptr;
//NUKE_COPYASSIGN_CONSTRUCTORS
};
Finally, I’ve considered a slight variation on the above:
template <typename T>
class IppsScopedState
{
public:
explicit IppsScopedState(size_t byteSize)
: _ptr(new uint8_t[byteSize])
{}
T * get() const { return reinterpret_cast<T *>(_ptr); }
~IppsScopedState(void) {
if (_ptr) delete [] _ptr;
}
private:
uint8_t * _ptr;
//NUKE_COPYASSIGN_CONSTRUCTORS
};
In either case, the usage would be like this:
IppsScopedState<IppsSHA256State> ctx(byteSize); // after querying for the byteSize, of course
You are correct that this:
Is a bad idea, for two reasons:
deletewill be called instead ofdelete[], and it will be deleting the wrong type.boost::scoped_arrayShould work fine:But that gives you two local variables, and you have to remember not to keep
shaCtxaround after you’re done withtemparray. So, rolling your own wrapper is a very appealing option, because it gives you that safety.Your current
IppsScopedStateis fine, but I’d suggest tweaking it slightly, to make use of boost::scoped_array internally, add theoperator->accessor, and const access:Then you can use this wrapper easily:
Depending on your needs the
operator->and constget()may be overkill, and unnecessary.So, in the end, I’d recommend your custom wrapper, and using reinterpret_cast over the old c-style cast syntax.