add weak_set and wake_value_map
Containers for weak_ptr which automatically remove dead references.
This commit is contained in:
254
p2pApp/weakmap.h
Normal file
254
p2pApp/weakmap.h
Normal file
@ -0,0 +1,254 @@
|
||||
#ifndef WEAKMAP_H
|
||||
#define WEAKMAP_H
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <pv/sharedPtr.h>
|
||||
#include <epicsMutex.h>
|
||||
#include <epicsGuard.h>
|
||||
|
||||
/** @brief An associative map where the value is a weak_ptr to the value is stored.
|
||||
*
|
||||
* Acts like std::map<K, weak_ptr<V> > where entries are automatically
|
||||
* removed when no longer referenced.
|
||||
*
|
||||
* Meant to be used in situations where an object must hold some weak references
|
||||
* of entries which it can iterate.
|
||||
*
|
||||
* Note that insert() and operator[] w/ assignment replaces the reference pass in
|
||||
* with a "wrapped" reference which removes from the set then releases the original ref.
|
||||
* The reference passed in *must* be unique() or std::invalid_argument is thrown.
|
||||
* While this can't be enforced, no weak_ptr to this object should exist.
|
||||
*
|
||||
* A reference loop will exist if the object owning the weak_set also
|
||||
* holds strong references to entries in this set.
|
||||
*
|
||||
* @note With the exception of swap() all methods are thread-safe
|
||||
*
|
||||
* @warning Use caution when storing types deriving from enabled_shared_from_this<>
|
||||
* As the implict weak reference they contain will not be wrapped.
|
||||
@code
|
||||
struct Owner;
|
||||
struct Entry {
|
||||
shared_ptr<Owner> O;
|
||||
};
|
||||
struct Owner {
|
||||
weak_map<string, Entry> M;
|
||||
};
|
||||
shared_ptr<Entry> build(const shared_ptr<Owner>& own, const std::string& k) {
|
||||
shared_ptr<Owner> N(new Entry);
|
||||
N.O = own;
|
||||
own.M[k] = N; // modifies 'N'
|
||||
return N;
|
||||
}
|
||||
void example()
|
||||
{
|
||||
shared_ptr<Owner> O(new Owner);
|
||||
shared_ptr<Entry> E(build(O, "test"));
|
||||
assert(!O.M.empty());
|
||||
assert(O.M["test"]==E);
|
||||
E.reset(); // Entry is removed from the set and free'd
|
||||
assert(O.M.empty());
|
||||
}
|
||||
@endcode
|
||||
*/
|
||||
template<typename K, typename V, typename C = std::less<K> >
|
||||
class weak_value_map
|
||||
{
|
||||
public:
|
||||
typedef K key_type;
|
||||
typedef V value_type;
|
||||
typedef std::tr1::shared_ptr<V> value_pointer;
|
||||
typedef std::tr1::weak_ptr<V> value_weak_pointer;
|
||||
typedef std::set<value_pointer> set_type;
|
||||
|
||||
typedef epicsMutex mutex_type;
|
||||
typedef epicsGuard<epicsMutex> guard_type;
|
||||
typedef epicsGuardRelease<epicsMutex> release_type;
|
||||
private:
|
||||
typedef std::map<K, value_weak_pointer, C> store_t;
|
||||
|
||||
struct data {
|
||||
mutex_type mutex;
|
||||
store_t store;
|
||||
};
|
||||
std::tr1::shared_ptr<data> m_data;
|
||||
|
||||
struct dtor {
|
||||
std::tr1::weak_ptr<data> container;
|
||||
K key;
|
||||
value_pointer realself;
|
||||
dtor(const std::tr1::weak_ptr<data>& d,
|
||||
const K& k,
|
||||
const value_pointer& w)
|
||||
:container(d), key(k), realself(w)
|
||||
{}
|
||||
void operator()(value_type *)
|
||||
{
|
||||
value_pointer R;
|
||||
R.swap(realself);
|
||||
std::tr1::shared_ptr<data> cont(container.lock());
|
||||
if(cont) {
|
||||
guard_type G(cont->mutex);
|
||||
cont->store.erase(key);
|
||||
}
|
||||
|
||||
/* A subtle gotcha may exist since this struct
|
||||
* may not be destructed until the *weak*
|
||||
* count of the enclosing shared_ptr goes
|
||||
* to zero. Which won't happen
|
||||
* as long as we hold a weak ref to the
|
||||
* container holding a weak ref to us.
|
||||
* It is *essential* that we break this
|
||||
* "weak ref. loop" explicitly
|
||||
*/
|
||||
container.reset();
|
||||
}
|
||||
};
|
||||
public:
|
||||
//! Construct a new empty set
|
||||
weak_value_map() :m_data(new data) {}
|
||||
//! Not copyable
|
||||
weak_value_map(const weak_value_map& O);
|
||||
//! Not copyable
|
||||
weak_value_map& operator=(const weak_value_map& O);
|
||||
|
||||
//! exchange the two sets.
|
||||
//! @warning Not thread safe (exchanges mutexes as well)
|
||||
void swap(weak_value_map& O) {
|
||||
m_data.swap(O.m_data);
|
||||
}
|
||||
|
||||
//! Remove all (weak) entries from the set
|
||||
//! @note Thread safe
|
||||
void clear() {
|
||||
guard_type G(m_data->mutex);
|
||||
return m_data->store.clear();
|
||||
}
|
||||
|
||||
//! Test if set is empty at this moment
|
||||
//! @note Thread safe
|
||||
bool empty() const {
|
||||
guard_type G(m_data->mutex);
|
||||
return m_data->store.empty();
|
||||
}
|
||||
|
||||
//! number of entries in the set at this moment
|
||||
//! @note Thread safe
|
||||
size_t size() const {
|
||||
guard_type G(m_data->mutex);
|
||||
return m_data->store.size();
|
||||
}
|
||||
|
||||
//! proxy class for lookup of non-const
|
||||
//! Supports assignment and deref.
|
||||
//! implicitly castable to value_pointer (aka shared_ptr<V>)
|
||||
class element_proxy {
|
||||
weak_value_map& M;
|
||||
const key_type& k;
|
||||
friend class weak_value_map;
|
||||
element_proxy(weak_value_map& m, const key_type& k)
|
||||
:M(m), k(k) {}
|
||||
public:
|
||||
~element_proxy() {}
|
||||
//! Support: map[k] = v
|
||||
//! The value_pointer passed in will be replaced with a wrapped reference
|
||||
//! @returns the argument
|
||||
value_pointer& operator=(value_pointer& v)
|
||||
{
|
||||
if(!v.unique())
|
||||
throw std::invalid_argument("Only unique() references may be inserted");
|
||||
value_pointer chainptr(v.get(), dtor(M.m_data, k, v));
|
||||
M.m_data->store[k] = chainptr;
|
||||
v.swap(chainptr);
|
||||
return v;
|
||||
}
|
||||
//! Support: *map[k]
|
||||
inline V& operator*() const {
|
||||
return *value_pointer(*this);
|
||||
}
|
||||
//! Support: map[k]->mem
|
||||
inline V* operator->() const {
|
||||
return value_pointer(*this).get();
|
||||
}
|
||||
//! Support: value_pointer V(map[k])
|
||||
operator value_pointer() const
|
||||
{
|
||||
value_pointer ret = M.find(k);
|
||||
if(!ret)
|
||||
throw std::runtime_error("Bad key");
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
inline element_proxy operator[](const K& k)
|
||||
{
|
||||
return element_proxy(*this, k);
|
||||
}
|
||||
|
||||
value_pointer operator[](const K& k) const
|
||||
{
|
||||
value_pointer ret = find(k);
|
||||
if(!ret)
|
||||
throw std::runtime_error("Bad key");
|
||||
}
|
||||
|
||||
//! Lookup key 'k'
|
||||
//! @returns a strong reference or nullptr if 'k' is not present
|
||||
value_pointer find(const K& k) const
|
||||
{
|
||||
value_pointer ret;
|
||||
guard_type G(m_data->mutex);
|
||||
typename store_t::const_iterator it(m_data->store.find(k));
|
||||
if(it!=m_data->store.end())
|
||||
ret = it->second.lock(); // may be nullptr if we race destruction
|
||||
return ret;
|
||||
}
|
||||
|
||||
//! Insert or replace
|
||||
//! @returns previous value of key k (may be nullptr).
|
||||
value_pointer insert(const K& k, value_pointer& v)
|
||||
{
|
||||
value_pointer ret;
|
||||
guard_type G(m_data->mutex);
|
||||
typename store_t::const_iterator it = m_data->store.find(k);
|
||||
if(it!=m_data->store.end())
|
||||
ret = it->second.lock();
|
||||
(*this)[k] = v;
|
||||
return ret;
|
||||
}
|
||||
|
||||
typedef std::map<K, value_pointer, C> lock_map_type;
|
||||
//! Return an equivalent map with strong value references
|
||||
lock_map_type lock_map() const
|
||||
{
|
||||
std::map<K, value_pointer, C> ret;
|
||||
guard_type G(m_data->mutex);
|
||||
for(typename store_t::const_iterator it = m_data->store.begin(),
|
||||
end = m_data->store.end(); it!=end; ++it)
|
||||
{
|
||||
ret[it->first] = it->second.lock();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
typedef std::vector<std::pair<K, value_pointer> > lock_vector_type;
|
||||
//! Return a vector of pairs of keys and strong value references.
|
||||
//! useful for iteration
|
||||
lock_vector_type lock_vector() const
|
||||
{
|
||||
std::vector<std::pair<K, value_pointer> > ret;
|
||||
guard_type G(m_data->mutex);
|
||||
ret.reserve(m_data->store.size());
|
||||
for(typename store_t::const_iterator it = m_data->store.begin(),
|
||||
end = m_data->store.end(); it!=end; ++it)
|
||||
{
|
||||
ret.push_back(std::make_pair(it->first, it->second.lock()));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // WEAKMAP_H
|
251
p2pApp/weakset.h
Normal file
251
p2pApp/weakset.h
Normal file
@ -0,0 +1,251 @@
|
||||
#ifndef WEAKSET_H
|
||||
#define WEAKSET_H
|
||||
|
||||
#include <set>
|
||||
#include <vector>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <pv/sharedPtr.h>
|
||||
#include <epicsMutex.h>
|
||||
#include <epicsGuard.h>
|
||||
|
||||
/** @brief a std::set-ish container where entries are removed when ref. counts fall to zero
|
||||
*
|
||||
* A container of ref. counted (by shared_ptr) entries
|
||||
* where an entry may be present zero or one times in the set.
|
||||
*
|
||||
* Meant to be used in situations where an object must hold some weak references
|
||||
* of entries which it can iterate.
|
||||
*
|
||||
* Note that the insert() method replaces the reference pass to it
|
||||
* with a "wrapped" reference which removes from the set then releases the original ref.
|
||||
* The reference passed in *must* be unique() or std::invalid_argument is thrown.
|
||||
* While this can't be enforced, no weak_ptr to this object should exist.
|
||||
*
|
||||
* A reference loop will exist if the object owning the weak_set also
|
||||
* holds strong references to entries in this set.
|
||||
*
|
||||
* @note With the exception of swap() all methods are thread-safe
|
||||
*
|
||||
* @warning Use caution when storing types deriving from enabled_shared_from_this<>
|
||||
* As the implict weak reference they contain will not be wrapped.
|
||||
@code
|
||||
struct Owner;
|
||||
struct Entry {
|
||||
shared_ptr<Owner> O;
|
||||
};
|
||||
struct Owner {
|
||||
weak_set<Entry> S;
|
||||
};
|
||||
shared_ptr<Entry> build(const shared_ptr<Owner>& own) {
|
||||
shared_ptr<Owner> N(new Entry);
|
||||
N.O = own;
|
||||
own.S.insert(N); // modifies 'N'
|
||||
return N;
|
||||
}
|
||||
void example()
|
||||
{
|
||||
shared_ptr<Owner> O(new Owner);
|
||||
shared_ptr<Entry> E(build(O));
|
||||
assert(!O.S.empty());
|
||||
E.reset(); // Entry is removed from the set and free'd
|
||||
assert(O.S.empty());
|
||||
}
|
||||
@endcode
|
||||
*/
|
||||
template<typename T>
|
||||
class weak_set
|
||||
{
|
||||
public:
|
||||
typedef T value_type;
|
||||
typedef std::tr1::shared_ptr<T> value_pointer;
|
||||
typedef std::tr1::weak_ptr<T> value_weak_pointer;
|
||||
typedef std::set<value_pointer> set_type;
|
||||
typedef std::vector<value_pointer> vector_type;
|
||||
|
||||
typedef epicsMutex mutex_type;
|
||||
typedef epicsGuard<epicsMutex> guard_type;
|
||||
typedef epicsGuardRelease<epicsMutex> release_type;
|
||||
private:
|
||||
struct weak_less {
|
||||
bool operator()(const value_weak_pointer& lhs,
|
||||
const value_weak_pointer& rhs) const
|
||||
{
|
||||
value_pointer LHS(lhs.lock()), RHS(rhs.lock());
|
||||
return LHS && RHS && LHS.get() < RHS.get();
|
||||
}
|
||||
bool operator()(const value_pointer& lhs,
|
||||
const value_weak_pointer& rhs) const
|
||||
{
|
||||
value_pointer RHS(rhs.lock());
|
||||
return RHS && lhs.get() < RHS.get();
|
||||
}
|
||||
bool operator()(const value_weak_pointer& lhs,
|
||||
const value_pointer& rhs) const
|
||||
{
|
||||
value_pointer LHS(lhs.lock());
|
||||
return LHS && LHS.get() < rhs.get();
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::set<value_weak_pointer, weak_less> store_t;
|
||||
|
||||
struct data {
|
||||
mutex_type mutex;
|
||||
store_t store;
|
||||
};
|
||||
std::tr1::shared_ptr<data> m_data;
|
||||
|
||||
//! Destroyer for a chained shared_ptr
|
||||
//! which holds the unique() real strong
|
||||
//! refrence to the object
|
||||
struct dtor {
|
||||
std::tr1::weak_ptr<data> container;
|
||||
value_pointer realself;
|
||||
dtor(const std::tr1::weak_ptr<data>& d,
|
||||
const value_pointer& w)
|
||||
:container(d), realself(w)
|
||||
{}
|
||||
void operator()(value_type *)
|
||||
{
|
||||
value_pointer R;
|
||||
R.swap(realself);
|
||||
assert(R.unique());
|
||||
std::tr1::shared_ptr<data> C(container.lock());
|
||||
if(C) {
|
||||
guard_type G(C->mutex);
|
||||
C->store.erase(R);
|
||||
}
|
||||
|
||||
/* A subtle gotcha may exist since this struct
|
||||
* may not be destructed until the *weak*
|
||||
* count of the enclosing shared_ptr goes
|
||||
* to zero. Which won't happen
|
||||
* as long as we hold a weak ref to the
|
||||
* container holding a weak ref to us.
|
||||
* It is *essential* that we break this
|
||||
* "weak ref. loop" explicitly
|
||||
*/
|
||||
container.reset();
|
||||
}
|
||||
};
|
||||
public:
|
||||
//! Construct a new empty set
|
||||
weak_set() :m_data(new data) {}
|
||||
//! Not copyable
|
||||
weak_set(const weak_set& O);
|
||||
//! Not copyable
|
||||
weak_set& operator=(const weak_set& O);
|
||||
|
||||
//! exchange the two sets.
|
||||
//! @warning Not thread safe (exchanges mutexes as well)
|
||||
void swap(weak_set& O) {
|
||||
m_data.swap(O.m_data);
|
||||
}
|
||||
|
||||
//! Remove all (weak) entries from the set
|
||||
//! @note Thread safe
|
||||
void clear() {
|
||||
guard_type G(m_data->mutex);
|
||||
return m_data->store.clear();
|
||||
}
|
||||
|
||||
//! Test if set is empty at this moment
|
||||
//! @note Thread safe
|
||||
bool empty() const {
|
||||
guard_type G(m_data->mutex);
|
||||
return m_data->store.empty();
|
||||
}
|
||||
|
||||
//! number of entries in the set at this moment
|
||||
//! @note Thread safe
|
||||
size_t size() const {
|
||||
guard_type G(m_data->mutex);
|
||||
return m_data->store.size();
|
||||
}
|
||||
|
||||
//! Insert a new entry into the set
|
||||
//! The callers shared_ptr must be unique()
|
||||
//! and is (transparently) replaced with another
|
||||
void insert(value_pointer&);
|
||||
|
||||
//! Remove any (weak) ref to this object from the set
|
||||
//! @returns the number of objects removed (0 or 1)
|
||||
size_t erase(value_pointer& v) {
|
||||
guard_type G(m_data->mutex);
|
||||
return m_data->store.erase(v);
|
||||
}
|
||||
|
||||
//! Return a set of strong references to all entries
|
||||
//! @note that this allocates a new std::set and copies all entries
|
||||
set_type lock_set() const;
|
||||
|
||||
//! Return a vector of strong references to all entries
|
||||
//! Useful for iteration
|
||||
//! @note that this allocates a new std::set and copies all entries
|
||||
vector_type lock_vector() const;
|
||||
|
||||
//! Access to the weak_set internal lock
|
||||
//! for use with batch operations.
|
||||
//! @warning Use caution when swap()ing while holding this lock!
|
||||
inline guard_type lock() const {
|
||||
return guard_type(m_data->mutex);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
void weak_set<T>::insert(value_pointer &v)
|
||||
{
|
||||
if(!v.unique())
|
||||
throw std::invalid_argument("Only unique() references may be inserted");
|
||||
|
||||
guard_type G(m_data->mutex);
|
||||
typename store_t::const_iterator it = m_data->store.find(v);
|
||||
if(it==m_data->store.end()) { // new object
|
||||
|
||||
// wrapped strong ref. which removes from our map
|
||||
value_pointer chainptr(v.get(), dtor(m_data, v));
|
||||
|
||||
m_data->store.insert(chainptr);
|
||||
|
||||
v.swap(chainptr); // we only keep the chained pointer
|
||||
} else {
|
||||
// already stored, no-op
|
||||
|
||||
// paranoia, if already inserted then this should be a wrapped ref.
|
||||
// but not sure how to check this so update arg. with known wrapped ref.
|
||||
v = value_pointer(*it); // could throw bad_weak_ptr, but really never should
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
typename weak_set<T>::set_type
|
||||
weak_set<T>::lock_set() const
|
||||
{
|
||||
set_type ret;
|
||||
guard_type G(m_data->mutex);
|
||||
for(typename store_t::const_iterator it=m_data->store.begin(),
|
||||
end=m_data->store.end(); it!=end; ++it)
|
||||
{
|
||||
ret.insert(value_pointer(*it));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
typename weak_set<T>::vector_type
|
||||
weak_set<T>::lock_vector() const
|
||||
{
|
||||
vector_type ret;
|
||||
guard_type G(m_data->mutex);
|
||||
ret.reserve(m_data->store.size());
|
||||
for(typename store_t::const_iterator it=m_data->store.begin(),
|
||||
end=m_data->store.end(); it!=end; ++it)
|
||||
{
|
||||
ret.push_back(value_pointer(*it));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif // WEAKSET_H
|
22
testApp/Makefile
Normal file
22
testApp/Makefile
Normal file
@ -0,0 +1,22 @@
|
||||
TOP=..
|
||||
|
||||
include $(TOP)/configure/CONFIG
|
||||
#----------------------------------------
|
||||
# ADD MACRO DEFINITIONS AFTER THIS LINE
|
||||
#=============================
|
||||
|
||||
USR_CPPFLAGS = -I$(TOP)/p2pApp
|
||||
|
||||
TESTPROD_HOST += testweak
|
||||
testweak_SRCS = testweak.cpp
|
||||
testweak_LIBS = Com
|
||||
TESTS += testweak
|
||||
|
||||
TESTSCRIPTS_HOST += $(TESTS:%=%.t)
|
||||
|
||||
#===========================
|
||||
|
||||
include $(TOP)/configure/RULES
|
||||
#----------------------------------------
|
||||
# ADD RULES AFTER THIS LINE
|
||||
|
160
testApp/testweak.cpp
Normal file
160
testApp/testweak.cpp
Normal file
@ -0,0 +1,160 @@
|
||||
|
||||
#include "weakset.h"
|
||||
#include "weakmap.h"
|
||||
|
||||
#include <epicsUnitTest.h>
|
||||
#include <testMain.h>
|
||||
|
||||
namespace {
|
||||
|
||||
static
|
||||
void testWeakSet1()
|
||||
{
|
||||
testDiag("Test1 weak_set");
|
||||
|
||||
weak_set<int>::value_pointer ptr;
|
||||
weak_set<int> set;
|
||||
|
||||
testOk1(set.empty());
|
||||
|
||||
ptr.reset(new int(5));
|
||||
set.insert(ptr);
|
||||
|
||||
set.insert(ptr); // second insert is a no-op
|
||||
|
||||
testOk1(ptr.unique()); // we hold the only "fake" strong ref.
|
||||
|
||||
{
|
||||
weak_set<int>::set_type S(set.lock_set());
|
||||
testOk1(!S.empty());
|
||||
testOk1(S.size()==1);
|
||||
testOk1(S.find(ptr)!=S.end());
|
||||
}
|
||||
{
|
||||
weak_set<int>::vector_type S(set.lock_vector());
|
||||
testOk1(!S.empty());
|
||||
testOk1(S.size()==1);
|
||||
testOk1(*S[0]==5);
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void testWeakSet2()
|
||||
{
|
||||
testDiag("Test2 weak_set");
|
||||
|
||||
weak_set<int> set;
|
||||
weak_set<int>::value_pointer ptr;
|
||||
|
||||
testOk1(set.empty());
|
||||
|
||||
ptr.reset(new int(5));
|
||||
set.insert(ptr);
|
||||
|
||||
testOk1(!set.empty());
|
||||
|
||||
testOk1(ptr.unique());
|
||||
ptr.reset(); // implicitly removes from set
|
||||
|
||||
testOk1(set.empty());
|
||||
|
||||
ptr.reset(new int(5));
|
||||
set.insert(ptr);
|
||||
|
||||
set.clear();
|
||||
testOk1(set.empty());
|
||||
testOk1(!!ptr);
|
||||
}
|
||||
|
||||
static
|
||||
void testWeakSetInvalid()
|
||||
{
|
||||
testDiag("Test adding non-unique");
|
||||
weak_set<int> set;
|
||||
weak_set<int>::value_pointer ptr(new int(5)),
|
||||
other(ptr);
|
||||
|
||||
testOk1(!ptr.unique());
|
||||
|
||||
try{
|
||||
set.insert(ptr);
|
||||
testFail("Missed expected exception");
|
||||
} catch(std::invalid_argument& e) {
|
||||
testPass("Got expected exception: %s", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void testWeakMap1()
|
||||
{
|
||||
testDiag("Test weak_value_map1");
|
||||
|
||||
weak_set<int>::value_pointer ptr;
|
||||
weak_value_map<int,int> map;
|
||||
|
||||
testOk1(map.empty());
|
||||
|
||||
ptr.reset(new int(5));
|
||||
map[4] = ptr;
|
||||
|
||||
testOk1(!map.empty());
|
||||
{
|
||||
weak_value_map<int,int>::lock_vector_type V(map.lock_vector());
|
||||
testOk1(V.size()==1);
|
||||
testOk1(V[0].first==4);
|
||||
testOk1(*V[0].second==5);
|
||||
}
|
||||
|
||||
testOk1(map[4]==ptr);
|
||||
testOk1(*map[4]==5);
|
||||
}
|
||||
|
||||
static
|
||||
void testWeakMap2()
|
||||
{
|
||||
testDiag("Test weak_value_map2");
|
||||
|
||||
weak_set<int>::value_pointer ptr;
|
||||
weak_value_map<int,int> map;
|
||||
|
||||
testOk1(map.empty());
|
||||
|
||||
ptr.reset(new int(5));
|
||||
map[4] = ptr;
|
||||
|
||||
testOk1(!map.empty());
|
||||
{
|
||||
weak_value_map<int,int>::lock_vector_type V(map.lock_vector());
|
||||
testOk1(V.size()==1);
|
||||
testOk1(V[0].first==4);
|
||||
testOk1(*V[0].second==5);
|
||||
}
|
||||
|
||||
ptr.reset();
|
||||
testOk1(map.empty());
|
||||
|
||||
ptr.reset(new int(5));
|
||||
map[4] = ptr;
|
||||
{
|
||||
weak_set<int>::value_pointer O(map[4]);
|
||||
testOk1(O==ptr);
|
||||
}
|
||||
|
||||
testOk1(map.size()==1);
|
||||
map.clear();
|
||||
testOk1(map.empty());
|
||||
testOk1(!!ptr);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
MAIN(testweak)
|
||||
{
|
||||
testPlan(33);
|
||||
testWeakSet1();
|
||||
testWeakSet2();
|
||||
testWeakSetInvalid();
|
||||
testWeakMap1();
|
||||
testWeakMap2();
|
||||
return testDone();
|
||||
}
|
Reference in New Issue
Block a user