Improved reference-counted pointer

This commit is contained in:
Tsuda Kageyu 2013-04-29 20:15:05 +09:00
parent 82e616101a
commit 36ceaadfaa
17 changed files with 409 additions and 156 deletions

View File

@ -6,60 +6,103 @@ include(CheckLibraryExists)
include(CheckTypeSize)
include(CheckCXXSourceCompiles)
# check for libz using the cmake supplied FindZLIB.cmake
find_package(ZLIB)
if(ZLIB_FOUND)
set(HAVE_ZLIB 1)
else()
set(HAVE_ZLIB 0)
endif()
# Determine whether or not your compiler supports move semantics.
check_cxx_source_compiles("
#ifdef __clang__
# pragma clang diagnostic error \"-Wc++11-extensions\"
#endif
#include <utility>
int func(int &&x) { return x - 1; }
int main() { return func(std::move(1)); }
#ifdef __clang__
# pragma clang diagnostic error \"-Wc++11-extensions\"
#endif
#include <utility>
int func(int &&x) { return x - 1; }
int main() { return func(std::move(1)); }
" SUPPORT_MOVE_SEMANTICS)
# Determine whether or not your compiler supports template alias.
check_cxx_source_compiles("
#ifdef __clang__
# pragma clang diagnostic error \"-Wc++11-extensions\"
#endif
#include <vector>
template <typename T> using myVector = std::vector<T>;
int main() { return 0; }
" SUPPORT_TEMPLATE_ALIAS)
# Determine where shared_ptr<T> is defined regardless of C++11 support.
check_cxx_source_compiles("
#include <memory>
int main() { std::tr1::shared_ptr<int> x; return 0; }
#include <memory>
int main() { std::tr1::shared_ptr<int> x; return 0; }
" HAVE_STD_SHARED_PTR)
check_cxx_source_compiles("
if(NOT HAVE_STD_SHARED_PTR)
check_cxx_source_compiles("
#include <tr1/memory>
int main() { std::tr1::shared_ptr<int> x; return 0; }
" HAVE_TR1_SHARED_PTR)
" HAVE_TR1_SHARED_PTR)
if(NOT HAVE_TR1_SHARED_PTR)
check_cxx_source_compiles("
#include <boost/shared_ptr.hpp>
int main() { boost::shared_ptr<int> x; return 0; }
" HAVE_BOOST_SHARED_PTR)
endif()
endif()
# Determine which kind of atomic operations your compiler supports.
if(NOT HAVE_STD_SHARED_PTR AND NOT HAVE_TR1_SHARED_PTR AND NOT HAVE_BOOST_SHARED_PTR)
check_cxx_source_compiles("
int main() {
volatile int x;
__sync_add_and_fetch(&x, 1);
int y = __sync_sub_and_fetch(&x, 1);
return 0;
}
" HAVE_GCC_ATOMIC)
if(NOT HAVE_GCC_ATOMIC)
check_cxx_source_compiles("
#include <libkern/OSAtomic.h>
int main() {
volatile int32_t x;
OSAtomicIncrement32Barrier(&x);
int32_t y = OSAtomicDecrement32Barrier(&x);
return 0;
}
" HAVE_MAC_ATOMIC)
if(NOT HAVE_MAC_ATOMIC)
check_cxx_source_compiles("
#include <windows.h>
int main() {
volatile LONG x;
InterlockedIncrement(&x);
LONG y = InterlockedDecrement(&x);
return 0;
}
" HAVE_WIN_ATOMIC)
if(NOT HAVE_WIN_ATOMIC)
check_cxx_source_compiles("
#include <ia64intrin.h>
int main() {
volatile int x;
__sync_add_and_fetch(&x, 1);
int y = __sync_sub_and_fetch(&x, 1);
return 0;
}
" HAVE_IA64_ATOMIC)
endif()
endif()
endif()
endif()
check_cxx_source_compiles("
#include <boost/shared_ptr.hpp>
int main() { boost::shared_ptr<int> x; return 0; }
" HAVE_BOOST_SHARED_PTR)
# Determine whether your compiler supports codecvt header.
check_cxx_source_compiles("
#include <codecvt>
int main() { std::codecvt_utf8_utf16<wchar_t> x; return 0; }
#include <codecvt>
int main() { std::codecvt_utf8_utf16<wchar_t> x; return 0; }
" HAVE_CODECVT)
# check for libz using the cmake supplied FindZLIB.cmake
find_package(ZLIB)
if(ZLIB_FOUND)
set(HAVE_ZLIB 1)
else()
set(HAVE_ZLIB 0)
endif()
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules)
find_package(CppUnit)
if(NOT CppUnit_FOUND AND BUILD_TESTS)
message(STATUS "CppUnit not found, disabling tests.")
set(BUILD_TESTS OFF)
message(STATUS "CppUnit not found, disabling tests.")
set(BUILD_TESTS OFF)
endif()

View File

@ -1,22 +1,25 @@
/* config-taglib.h. Generated by cmake from config-taglib.h.cmake */
/* Define if you have libz */
#cmakedefine HAVE_ZLIB 1
/* Defined if your compiler supports the move semantics */
#cmakedefine SUPPORT_MOVE_SEMANTICS 1
/* Defined if your compiler supports the template alias */
#cmakedefine SUPPORT_TEMPLATE_ALIAS 1
/* Defined if your compiler supports shared_ptr */
#cmakedefine HAVE_STD_SHARED_PTR 1
#cmakedefine HAVE_TR1_SHARED_PTR 1
#cmakedefine HAVE_BOOST_SHARED_PTR 1
/* Defined if your compiler supports some atomic operations */
#cmakedefine HAVE_GCC_ATOMIC 1
#cmakedefine HAVE_MAC_ATOMIC 1
#cmakedefine HAVE_WIN_ATOMIC 1
#cmakedefine HAVE_IA64_ATOMIC 1
/* Defined if your compiler has <codecvt> header */
#cmakedefine HAVE_CODECVT 1
/* Define if you have libz */
#cmakedefine HAVE_ZLIB 1
#cmakedefine NO_ITUNES_HACKS 1
#cmakedefine WITH_ASF 1
#cmakedefine WITH_MP4 1

View File

@ -205,7 +205,7 @@ namespace TagLib
ByteVector render(const String &name, int kind = 0) const;
class AttributePrivate;
RefCountPtr<AttributePrivate> d;
TAGLIB_SHARED_PTR<AttributePrivate> d;
};
}

View File

@ -231,7 +231,7 @@ namespace TagLib
#endif
private:
class PicturePrivate;
RefCountPtr<PicturePrivate> d;
TAGLIB_SHARED_PTR<PicturePrivate> d;
};
}
}

View File

@ -66,7 +66,7 @@ class FileRef::FileRefPrivate
public:
FileRefPrivate(File *f) : file(f) {}
RefCountPtr<File> file;
TAGLIB_SHARED_PTR<File> file;
static List<const FileTypeResolver *> fileTypeResolvers;
};

View File

@ -278,7 +278,7 @@ namespace TagLib {
private:
class FileRefPrivate;
RefCountPtr<FileRefPrivate> d;
TAGLIB_SHARED_PTR<FileRefPrivate> d;
};
} // namespace TagLib

View File

@ -66,7 +66,7 @@ namespace TagLib {
private:
class CoverArtPrivate;
RefCountPtr<CoverArtPrivate> d;
TAGLIB_SHARED_PTR<CoverArtPrivate> d;
};
typedef List<CoverArt> CoverArtList;

View File

@ -97,7 +97,7 @@ namespace TagLib {
private:
class ItemPrivate;
RefCountPtr<ItemPrivate> d;
TAGLIB_SHARED_PTR<ItemPrivate> d;
};
}

View File

@ -181,7 +181,7 @@ namespace TagLib {
void parse(const ByteVector &data);
class HeaderPrivate;
RefCountPtr<HeaderPrivate> d;
TAGLIB_SHARED_PTR<HeaderPrivate> d;
};
}
}

View File

@ -272,7 +272,7 @@ public:
{
}
ByteVectorPrivate(RefCountPtr<ByteVectorPrivate> d, size_t o, size_t l)
ByteVectorPrivate(TAGLIB_SHARED_PTR<ByteVectorPrivate> d, size_t o, size_t l)
: data(d->data)
, offset(d->offset + o)
, length(l)
@ -320,7 +320,7 @@ public:
return *this;
}
RefCountPtr<DataPrivate> data;
TAGLIB_SHARED_PTR<DataPrivate> data;
size_t offset;
size_t length;
};

View File

@ -478,7 +478,7 @@ namespace TagLib {
private:
class ByteVectorPrivate;
RefCountPtr<ByteVectorPrivate> d;
TAGLIB_SHARED_PTR<ByteVectorPrivate> d;
};
/*!

View File

@ -305,7 +305,7 @@ namespace TagLib {
private:
#ifndef DO_NOT_DOCUMENT
template <class TP> class ListPrivate;
RefCountPtr<ListPrivate<T> > d;
TAGLIB_SHARED_PTR<ListPrivate<T> > d;
#endif
};

View File

@ -219,7 +219,7 @@ namespace TagLib {
private:
#ifndef DO_NOT_DOCUMENT
template <class KeyP, class TP> class MapPrivate;
RefCountPtr<MapPrivate<Key, T> > d;
TAGLIB_SHARED_PTR<MapPrivate<Key, T> > d;
#endif
};

View File

@ -37,19 +37,33 @@
#elif defined(HAVE_BOOST_SHARED_PTR)
# include <boost/shared_ptr.hpp>
#else
# ifdef __APPLE__
# include <libkern/OSAtomic.h>
# define TAGLIB_ATOMIC_MAC
# elif defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__)
# define TAGLIB_ATOMIC_WIN
# elif defined (__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ >= 401) \
&& (defined(__i386__) || defined(__i486__) || defined(__i586__) || \
defined(__i686__) || defined(__x86_64) || defined(__ia64)) \
&& !defined(__INTEL_COMPILER)
# define TAGLIB_ATOMIC_GCC
# elif defined(__ia64) && defined(__INTEL_COMPILER)
# include <ia64intrin.h>
# define TAGLIB_ATOMIC_GCC
# include <algorithm>
# if defined(HAVE_GCC_ATOMIC)
# define TAGLIB_ATOMIC_INT int
# define TAGLIB_ATOMIC_INC(x) __sync_add_and_fetch(&x, 1)
# define TAGLIB_ATOMIC_DEC(x) __sync_sub_and_fetch(&x, 1)
# elif defined(HAVE_WIN_ATOMIC)
# if !defined(NOMINMAX)
# define NOMINMAX
# endif
# include <windows.h>
# define TAGLIB_ATOMIC_INT long
# define TAGLIB_ATOMIC_INC(x) InterlockedIncrement(&x)
# define TAGLIB_ATOMIC_DEC(x) InterlockedDecrement(&x)
# elif defined(HAVE_MAC_ATOMIC)
# include <libkern/OSAtomic.h>
# define TAGLIB_ATOMIC_INT int32_t
# define TAGLIB_ATOMIC_INC(x) OSAtomicIncrement32Barrier(&x)
# define TAGLIB_ATOMIC_DEC(x) OSAtomicDecrement32Barrier(&x)
# elif defined(HAVE_IA64_ATOMIC)
# include <ia64intrin.h>
# define TAGLIB_ATOMIC_INT int
# define TAGLIB_ATOMIC_INC(x) __sync_add_and_fetch(&x, 1)
# define TAGLIB_ATOMIC_DEC(x) __sync_sub_and_fetch(&x, 1)
# else
# define TAGLIB_ATOMIC_INT int
# define TAGLIB_ATOMIC_INC(x) (++x)
# define TAGLIB_ATOMIC_DEC(x) (--x)
# endif
#endif
@ -63,55 +77,18 @@
namespace TagLib {
#if defined(HAVE_STD_SHARED_PTR) || defined(HAVE_TR1_SHARED_PTR) || defined(HAVE_BOOST_SHARED_PTR)
#if defined(HAVE_STD_SHARED_PTR) || defined(HAVE_TR1_SHARED_PTR)
#define TAGLIB_SHARED_PTR std::tr1::shared_ptr
# if defined(SUPPORT_TEMPLATE_ALIAS)
#elif defined(HAVE_BOOST_SHARED_PTR)
// Defines RefCountPtr<T> as an alias of shared_ptr<T>
// if shared_ptr<T> and the template alias are both available.
# if defined(HAVE_STD_SHARED_PTR) || defined(HAVE_TR1_SHARED_PTR)
template <typename T>
using RefCountPtr = std::tr1::shared_ptr<T>;
# else
template <typename T>
using RefCountPtr = boost::shared_ptr<T>;
# endif
# else
// Defines RefCountPtr<T> as a derived class of shared_ptr<T>.
// if shared_ptr<T> is available but the template alias is not.
# if defined(HAVE_STD_SHARED_PTR) || defined(HAVE_TR1_SHARED_PTR)
template <typename T>
class RefCountPtr : public std::tr1::shared_ptr<T>
{
public:
explicit RefCountPtr(T *p) : std::tr1::shared_ptr<T>(p) {}
};
# else
template <typename T>
class RefCountPtr : public boost::shared_ptr<T>
{
public:
explicit RefCountPtr(T *p) : boost::shared_ptr<T>(p) {}
};
# endif
# endif
#define TAGLIB_SHARED_PTR boost::shared_ptr
#else // HAVE_*_SHARED_PTR
// Implements RefCountPtr<T> if shared_ptr<T> is not available.
// Self-implements RefCountPtr<T> if shared_ptr<T> is not available.
// I STRONGLY RECOMMEND using standard shared_ptr<T> rather than this class.
template<typename T>
class RefCountPtr
@ -134,12 +111,12 @@ namespace TagLib {
void addref()
{
increment(&count);
TAGLIB_ATOMIC_INC(count);
}
void release()
{
if(decrement(&count) == 0) {
if(TAGLIB_ATOMIC_DEC(count) == 0) {
dispose();
delete this;
}
@ -153,33 +130,7 @@ namespace TagLib {
virtual void dispose() = 0;
private:
# if defined(TAGLIB_ATOMIC_MAC)
typedef volatile int32_t counter_t;
inline static void increment(counter_t *c) { OSAtomicIncrement32Barrier(c); }
inline static counter_t decrement(counter_t *c) { return OSAtomicDecrement32Barrier(c); }
# elif defined(TAGLIB_ATOMIC_WIN)
typedef volatile long counter_t;
inline static void increment(counter_t *c) { InterlockedIncrement(c); }
inline static counter_t decrement(counter_t *c) { return InterlockedDecrement(c); }
# elif defined(TAGLIB_ATOMIC_GCC)
typedef volatile int counter_t;
inline static void increment(counter_t *c) { __sync_add_and_fetch(c, 1); }
inline static counter_t decrement(counter_t *c) { return __sync_sub_and_fetch(c, 1); }
# else
typedef uint counter_t;
inline static void increment(counter_t *c) { ++(*c) }
inline static counter_t decrement(counter_t *c) { return --(*c); }
# endif
counter_t count;
volatile TAGLIB_ATOMIC_INT count;
};
// Counter impl class. Provides a dynamic deleter.
@ -208,31 +159,52 @@ namespace TagLib {
};
public:
explicit RefCountPtr()
: counter(0)
{
}
template <typename U>
explicit RefCountPtr(U *p)
: counter(new CounterImpl<U>(p))
{
}
RefCountPtr(const RefCountPtr &x)
RefCountPtr(const RefCountPtr<T> &x)
: counter(x.counter)
{
counter = x.counter;
counter->addref();
if(counter)
counter->addref();
}
template <typename U>
RefCountPtr(const RefCountPtr<U> &x)
: counter(reinterpret_cast<CounterBase*>(x.counter))
{
if(counter)
counter->addref();
}
~RefCountPtr()
{
counter->release();
if(counter)
counter->release();
}
T *get() const
{
return static_cast<CounterImpl<T>*>(counter)->get();
if(counter)
return static_cast<CounterImpl<T>*>(counter)->get();
else
return 0;
}
long use_count() const
{
return counter->use_count();
if(counter)
return counter->use_count();
else
return 0;
}
bool unique() const
@ -244,20 +216,44 @@ namespace TagLib {
void reset(U *p)
{
if(get() != p)
{
counter->release();
counter = new CounterImpl<U>(p);
}
RefCountPtr<T>(p).swap(*this);
}
void reset()
{
RefCountPtr<T>().swap(*this);
}
void swap(RefCountPtr<T> &x)
{
std::swap(counter, x.counter);
}
RefCountPtr<T> &operator=(const RefCountPtr<T> &x)
{
if(get() != x.get())
{
counter->release();
if(get() != x.get()) {
if(counter)
counter->release();
counter = x.counter;
counter->addref();
if(counter)
counter->addref();
}
return *this;
}
template <typename U>
RefCountPtr<T> &operator=(const RefCountPtr<U> &x)
{
if(get() != x.get()) {
if(counter)
counter->release();
counter = reinterpret_cast<CounterBase*>(x.counter);
if(counter)
counter->addref();
}
return *this;
}
@ -288,9 +284,19 @@ namespace TagLib {
}
private:
CounterBase *counter;
mutable CounterBase *counter;
template <typename U> friend class RefCountPtr;
};
# define TAGLIB_SHARED_PTR TagLib::RefCountPtr
template <typename T>
void swap(RefCountPtr<T> &a, RefCountPtr<T> &b)
{
a.swap(b);
}
#endif // HAVE_*_SHARED_PTR
}
#endif // DO_NOT_DOCUMENT

View File

@ -523,7 +523,7 @@ namespace TagLib {
static const Type WCharByteOrder;
class StringPrivate;
RefCountPtr<StringPrivate> d;
TAGLIB_SHARED_PTR<StringPrivate> d;
};
/*!

View File

@ -30,6 +30,7 @@ SET(test_runner_SRCS
main.cpp
test_list.cpp
test_map.cpp
test_smartptr.cpp
test_mpeg.cpp
test_synchdata.cpp
test_trueaudio.cpp

200
tests/test_smartptr.cpp Normal file
View File

@ -0,0 +1,200 @@
#include <trefcountptr.h>
#include <cppunit/extensions/HelperMacros.h>
#include "utils.h"
using namespace std;
using namespace TagLib;
bool baseDestructorCalled;
bool derivedDestructorCalled;
bool incompleteDestructorCalled;
class TestSmartptr : public CppUnit::TestFixture
{
CPPUNIT_TEST_SUITE(TestSmartptr);
CPPUNIT_TEST(testSharedptrBasic);
CPPUNIT_TEST(testDerivedClass);
CPPUNIT_TEST(testIncompleteClass);
CPPUNIT_TEST_SUITE_END();
private:
template<class T>
void ck( const T* v1, T v2 ) { CPPUNIT_ASSERT( *v1 == v2 ); }
public:
void testSharedptrBasic()
{
int * ip = new int;
TAGLIB_SHARED_PTR<int> cp ( ip );
CPPUNIT_ASSERT( ip == cp.get() );
CPPUNIT_ASSERT( cp.use_count() == 1 );
*cp = 54321;
CPPUNIT_ASSERT( *cp == 54321 );
CPPUNIT_ASSERT( *ip == 54321 );
ck( static_cast<int*>(cp.get()), 54321 );
ck( static_cast<int*>(ip), *cp );
TAGLIB_SHARED_PTR<int> cp2 ( cp );
CPPUNIT_ASSERT( ip == cp2.get() );
CPPUNIT_ASSERT( cp.use_count() == 2 );
CPPUNIT_ASSERT( cp2.use_count() == 2 );
CPPUNIT_ASSERT( *cp == 54321 );
CPPUNIT_ASSERT( *cp2 == 54321 );
ck( static_cast<int*>(cp2.get()), 54321 );
ck( static_cast<int*>(ip), *cp2 );
TAGLIB_SHARED_PTR<int> cp3 ( cp );
CPPUNIT_ASSERT( cp.use_count() == 3 );
CPPUNIT_ASSERT( cp2.use_count() == 3 );
CPPUNIT_ASSERT( cp3.use_count() == 3 );
cp.reset();
CPPUNIT_ASSERT( cp2.use_count() == 2 );
CPPUNIT_ASSERT( cp3.use_count() == 2 );
cp.reset( new int );
*cp = 98765;
CPPUNIT_ASSERT( *cp == 98765 );
*cp3 = 87654;
CPPUNIT_ASSERT( *cp3 == 87654 );
CPPUNIT_ASSERT( *cp2 == 87654 );
cp.swap( cp3 );
CPPUNIT_ASSERT( *cp == 87654 );
CPPUNIT_ASSERT( *cp2 == 87654 );
CPPUNIT_ASSERT( *cp3 == 98765 );
cp.swap( cp3 );
CPPUNIT_ASSERT( *cp == 98765 );
CPPUNIT_ASSERT( *cp2 == 87654 );
CPPUNIT_ASSERT( *cp3 == 87654 );
cp2 = cp2;
CPPUNIT_ASSERT( cp2.use_count() == 2 );
CPPUNIT_ASSERT( *cp2 == 87654 );
cp = cp2;
CPPUNIT_ASSERT( cp2.use_count() == 3 );
CPPUNIT_ASSERT( *cp2 == 87654 );
CPPUNIT_ASSERT( cp.use_count() == 3 );
CPPUNIT_ASSERT( *cp == 87654 );
TAGLIB_SHARED_PTR<int> cp4;
swap( cp2, cp4 );
CPPUNIT_ASSERT( cp4.use_count() == 3 );
CPPUNIT_ASSERT( *cp4 == 87654 );
CPPUNIT_ASSERT( cp2.get() == 0 );
std::set< TAGLIB_SHARED_PTR<int> > scp;
scp.insert(cp4);
CPPUNIT_ASSERT( scp.find(cp4) != scp.end() );
CPPUNIT_ASSERT( scp.find(cp4) == scp.find( TAGLIB_SHARED_PTR<int>(cp4) ) );
}
private:
class DummyBase
{
public:
DummyBase(int x) : value(x)
{
}
virtual ~DummyBase()
{
baseDestructorCalled = true;
}
int getValue() const
{
return value;
}
private:
int value;
};
class DummyDerived : public DummyBase
{
public:
DummyDerived(int x) : DummyBase(x)
{
}
virtual ~DummyDerived()
{
derivedDestructorCalled = true;
}
};
public:
void testDerivedClass()
{
baseDestructorCalled = false;
derivedDestructorCalled = false;
{
TAGLIB_SHARED_PTR<DummyBase> p1(new DummyDerived(100));
CPPUNIT_ASSERT(p1->getValue() == 100);
}
CPPUNIT_ASSERT(baseDestructorCalled);
CPPUNIT_ASSERT(derivedDestructorCalled);
baseDestructorCalled = false;
derivedDestructorCalled = false;
{
TAGLIB_SHARED_PTR<DummyDerived> p1(new DummyDerived(100));
TAGLIB_SHARED_PTR<DummyBase> p2 = p1;
CPPUNIT_ASSERT(p1->getValue() == 100);
CPPUNIT_ASSERT(p2->getValue() == 100);
}
CPPUNIT_ASSERT(baseDestructorCalled);
CPPUNIT_ASSERT(derivedDestructorCalled);
baseDestructorCalled = false;
derivedDestructorCalled = false;
{
TAGLIB_SHARED_PTR<DummyDerived> p1;
TAGLIB_SHARED_PTR<DummyBase> p2;
p1.reset(new DummyDerived(100));
p2 = p1;
CPPUNIT_ASSERT(p1->getValue() == 100);
CPPUNIT_ASSERT(p2->getValue() == 100);
}
CPPUNIT_ASSERT(baseDestructorCalled);
CPPUNIT_ASSERT(derivedDestructorCalled);
}
private:
class DummyIncomplete;
TAGLIB_SHARED_PTR<DummyIncomplete> pincomplete;
class DummyIncomplete
{
public:
~DummyIncomplete()
{
incompleteDestructorCalled = true;
}
int getValue() const { return 100; }
};
public:
void testIncompleteClass()
{
incompleteDestructorCalled = false;
pincomplete.reset(new DummyIncomplete());
CPPUNIT_ASSERT(pincomplete->getValue() == 100);
pincomplete.reset();
CPPUNIT_ASSERT(incompleteDestructorCalled);
}
};
CPPUNIT_TEST_SUITE_REGISTRATION(TestSmartptr);