1

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
Mark Cohen
  • 13
  • 2

2 Answers2

0

You can use boost::scoped_array or just std::vector.

young
  • 2,163
  • 12
  • 19
0

You are correct that this:

boost::scoped_ptr<IppsSHA256State> ctx(
    reinterpret_cast<IppsSHA256State *>(new uint8_t[byteSize])
);

Is a bad idea, for two reasons: delete will be called instead of delete[], and it will be deleting the wrong type.

boost::scoped_array Should work fine:

boost::scoped_array<uint8_t> temparray (new uint8_t[byteSize]);
IppsSHA256State * shaCtx = (IppsSHA256State *)(temparray.get());

But that gives you two local variables, and you have to remember not to keep shaCtx around after you're done with temparray. So, rolling your own wrapper is a very appealing option, because it gives you that safety.

Your current IppsScopedState is fine, but I'd suggest tweaking it slightly, to make use of boost::scoped_array internally, add the operator-> accessor, and const access:

template <typename T>
class IppsScopedState
{
public:
    explicit IppsScopedState(size_t byteSize)
        : _ptr(new uint8_t[byteSize])
    {}

    const T* operator->() const { return get(); }
    T* operator->() { return get(); }
    const T* get() const  { return reinterpret_cast<const T*> (_ptr.get()); }
    T* get()   { return reinterpret_cast<T*>(_ptr.get()); }

private:
    boost::scoped_array<uint8_t> _ptr;
    //NUKE_COPYASSIGN_CONSTRUCTORS
};

Then you can use this wrapper easily:

IppsScopedState<IppsSHA256State> shaCtx (byteSize);
shaCtx->member;  // access any member of IppsSHA256State
some_function(shaCtx.get());  // pass the IppsSHA256State* to a method

Depending on your needs the operator-> and const get() 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.

Tim
  • 8,912
  • 3
  • 39
  • 57
  • Thanks for addressing the points in my post. Since these Intel crypto structs are opaque, none of them have any accessible members and thus I won't need the `operator->`. I can't upvote your answer since this is my first question, but I'll accept it after doing some brief testing. – Mark Cohen Aug 09 '11 at 00:50