qpdf/include/qpdf/PointerHolder.hh

213 lines
5.6 KiB
C++

// Copyright (c) 2005-2022 Jay Berkenbilt
//
// This file is part of qpdf.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Versions of qpdf prior to version 7 were released under the terms
// of version 2.0 of the Artistic License. At your option, you may
// continue to consider qpdf to be licensed under those terms. Please
// see the manual for additional information.
#ifndef POINTERHOLDER_HH
#define POINTERHOLDER_HH
// In qpdf 11, PointerHolder will be derived from std::shared_ptr and
// will also include a fix to incorrect semantics of const
// PointerHolder objects. PointerHolder only allows a const
// PointerHolder to return a const pointer. This is wrong. Use a
// PointerHolder<const T> for that. A const PointerHolder should just
// not allow you to change what it points to. This is consistent with
// how regular pointers and standard library shared pointers work.
// This class is basically std::shared_ptr but predates that by
// several years.
// This class expects to be initialized with a dynamically allocated
// object pointer. It keeps a reference count and deletes this once
// the reference count goes to zero. PointerHolder objects are
// explicitly safe for use in STL containers.
// It is very important that a client who pulls the pointer out of
// this holder does not let the holder go out of scope until it is
// finished with the pointer. It is also important that exactly one
// instance of this object ever gets initialized with a given pointer.
// Otherwise, the pointer will be deleted twice, and before that, some
// objects will be left with a pointer to a deleted object. In other
// words, the only legitimate way for two PointerHolder objects to
// contain the same pointer is for one to be a copy of the other.
// Copy and assignment semantics are well-defined and essentially
// allow you to use PointerHolder as a means to get pass-by-reference
// semantics in a pass-by-value environment without having to worry
// about memory management details.
// Comparison (== and <) are defined and operate on the internally
// stored pointers, not on the data. This makes it possible to store
// PointerHolder objects in sorted lists or to find them in STL
// containers just as one would be able to store pointers. Comparing
// the underlying pointers provides a well-defined, if not
// particularly meaningful, ordering.
template <class T>
class PointerHolder
{
private:
class Data
{
public:
Data(T* pointer, bool array) :
pointer(pointer),
array(array),
refcount(0)
{
}
~Data()
{
if (array)
{
delete [] this->pointer;
}
else
{
delete this->pointer;
}
}
T* pointer;
bool array;
int refcount;
private:
Data(Data const&) = delete;
Data& operator=(Data const&) = delete;
};
public:
PointerHolder(T* pointer = 0)
{
this->init(new Data(pointer, false));
}
// Special constructor indicating to free memory with delete []
// instead of delete
PointerHolder(bool, T* pointer)
{
this->init(new Data(pointer, true));
}
PointerHolder(PointerHolder const& rhs)
{
this->copy(rhs);
}
PointerHolder& operator=(PointerHolder const& rhs)
{
if (this != &rhs)
{
this->destroy();
this->copy(rhs);
}
return *this;
}
~PointerHolder()
{
this->destroy();
}
bool operator==(PointerHolder const& rhs) const
{
return this->data->pointer == rhs.data->pointer;
}
bool operator<(PointerHolder const& rhs) const
{
return this->data->pointer < rhs.data->pointer;
}
// get() is for interface compatibility with std::shared_ptr
T* get() const
{
return this->data->pointer;
}
// NOTE: The pointer returned by getPointer turns into a pumpkin
// when the last PointerHolder that contains it disappears.
#ifndef NO_POINTERHOLDER_DEPRECATION
[[deprecated("use PointerHolder<T>::get() instead of getPointer()")]]
#endif
T* getPointer()
{
return this->data->pointer;
}
#ifndef NO_POINTERHOLDER_DEPRECATION
[[deprecated("use PointerHolder<T>::get() instead of getPointer()")]]
#endif
T const* getPointer() const
{
return this->data->pointer;
}
#ifndef NO_POINTERHOLDER_DEPRECATION
[[deprecated("use use_count() instead of getRefcount()")]]
#endif
int getRefcount() const
{
return this->data->refcount;
}
// use_count() is for compatibility with std::shared_ptr
long use_count()
{
return static_cast<long>(this->data->refcount);
}
T const& operator*() const
{
return *this->data->pointer;
}
T& operator*()
{
return *this->data->pointer;
}
T const* operator->() const
{
return this->data->pointer;
}
T* operator->()
{
return this->data->pointer;
}
private:
void init(Data* data)
{
this->data = data;
++this->data->refcount;
}
void copy(PointerHolder const& rhs)
{
this->init(rhs.data);
}
void destroy()
{
bool gone = false;
{
if (--this->data->refcount == 0)
{
gone = true;
}
}
if (gone)
{
delete this->data;
}
}
Data* data;
};
#endif // POINTERHOLDER_HH