// $Id$
// Version: $Name$
//
-// Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004 by the deal authors
+// Copyright (C) 1998 - 2005 by the deal authors
//
// This file is subject to QPL and may not be distributed
// without copyright and license information. Please refer
* SmartPointer does NOT implement any memory handling! Especially,
* deleting a SmartPointer does not delete the object. Writing
* @code
- * SmartPointer<T> t = new T;
+ * SmartPointer<T> dont_do_this = new T;
* @endcode
* is a sure way to program a memory leak! The secure version is
* @code
* T* p = new T;
* {
- * SmartPointer<T> t = p;
+ * SmartPointer<T> t(p, "mypointer");
* ...
* }
* delete p;
* a constant object (disallowing write access when dereferenced), while
* <tt>SmartPointer<ABC></tt> is a mutable pointer.
*
- * @author Guido Kanschat, Wolfgang Bangerth, 1998, 1999, 2000
+ * @author Guido Kanschat, Wolfgang Bangerth, 1998 - 2005
*/
template<typename T>
class SmartPointer
{
public:
/**
- * Standard constructor for null pointer.
+ * Standard constructor for null
+ * pointer. @deprecated Since
+ * this constructor will leave
+ * #id empty, it should be
+ * avoided wherever possible and
+ * replaced by SmartPointer(0,id).
*/
SmartPointer ();
* copy the object subscribed to
* from <tt>tt</tt>, but subscribe
* ourselves to it again.
+ *
+ * The <tt>id</tt> is used in the
+ * call to
+ * Subscriptor::subscribe(). The #id of
+ * the object copied is used here.
*/
SmartPointer (const SmartPointer<T> &tt);
* to lock it, i.e. to prevent
* its destruction before the end
* of its use.
+ *
+ * The <tt>id</tt> is used in the
+ * call to
+ * Subscriptor::subscribe() and
+ * by ~SmartPointer() in the call
+ * to Subscriptor::unsubscribe().
*/
- SmartPointer (T *t);
+ SmartPointer (T *t, const char* id=0);
/**
* short name.
*/
T * t;
+ /**
+ * The identification for the
+ * subscriptor.
+ */
+ const char* const id;
};
template <typename T>
SmartPointer<T>::SmartPointer ()
:
- t (0)
+ t (0), id(0)
{}
template <typename T>
-SmartPointer<T>::SmartPointer (T *t)
+SmartPointer<T>::SmartPointer (T *t, const char* id)
:
- t (t)
+ t (t), id(id)
{
if (t != 0)
- t->subscribe();
+ t->subscribe(id);
}
template <typename T>
SmartPointer<T>::SmartPointer (const SmartPointer<T> &tt)
:
- t (tt.t)
+ t (tt.t), id(tt.id)
{
if (t != 0)
t->subscribe();
SmartPointer<T>::~SmartPointer ()
{
if (t != 0)
- t->unsubscribe();
+ t->unsubscribe(id);
}
return *this;
if (t != 0)
- t->unsubscribe();
+ t->unsubscribe(id);
t = tt;
if (tt != 0)
- tt->subscribe();
+ tt->subscribe(id);
return *this;
}
return *this;
if (t != 0)
- t->unsubscribe();
+ t->unsubscribe(id);
t = static_cast<T*>(tt);
if (tt != 0)
- tt->subscribe();
+ tt->subscribe(id);
return *this;
}
// $Id$
// Version: $Name$
//
-// Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004 by the deal.II authors
+// Copyright (C) 1998 - 2005 by the deal.II authors
//
// This file is subject to QPL and may not be distributed
// without copyright and license information. Please refer
#include <base/config.h>
#include <base/exceptions.h>
-#include <typeinfo>
-
-
-namespace internal
-{
- /**
- * @internal
- * A namespace in which we
- * implement helper classes for the
- * subscriptor class.
- */
- namespace Subscriptor
- {
- /**
- * @internal
- * Template class that declares
- * in inner typedef with the
- * following semantics: if the
- * first template parameter is
- * <tt>true</tt>, then the inner
- * typedef is <tt>volatile T</tt>,
- * otherwise <tt>T</tt>. We achieve
- * this behavior by partial
- * specialization of the general
- * template for both values of
- * the boolean argument.
- *
- * This trick is used to declare
- * the type of the counter
- * variable to be eiter volatile
- * or not, depending on whether
- * we are in multithreaded mode
- * or not. (If we are in MT mode,
- * then we need the <tt>volatile</tt>
- * specifier since more than one
- * thread my modify the counter
- * at the same time.
- *
- * Since we only partially
- * specialize the case that the
- * boolean template argument is
- * <tt>false</tt>, this general
- * template catches the case that
- * it is <tt>true</tt>, i.e. in a
- * sense it is also a
- * specialization since a
- * <tt>bool</tt> can only have two
- * states.
- *
- * @author Wolfgang Bangerth, 2003
- */
- template <bool, typename T> struct PossiblyVolatile
- {
- /**
- * @internal
- * @brief The actual typedef.
- */
- typedef volatile T type;
- };
-
- /**
- * @internal
- * Specialization of the template
- * for the case that the first
- * template argument is
- * <tt>false</tt>, i.e. the
- * non-volatile case.
- */
- template <typename T> struct PossiblyVolatile<false,T>
- {
- /**
- * @internal
- * @brief The actual typedef.
- */
- typedef T type;
- };
- }
-}
-
+#include <typeinfo>
+#include <map>
+#include <string>
/**
* Handling of subscriptions.
* constructor by reference, is stored. Then, the original object may
* not be deleted before the dependent object is deleted. You can assert
* this constraint by letting the object passed be derived from this class
- * and let the user <tt>subscribe</tt> to this object. The destructor the used
- * object inherits from the <tt>Subscriptor</tt> class then will lead to an error
+ * and let the user subscribe() to this object. The destructor the used
+ * object inherits from the Subscriptor class then will lead to an error
* when destruction is attempted while there are still subscriptions.
+ *
+ * The utility of this class is even enhanced by providing identifying
+ * strings to the functions subscribe() and unsubscribe(). In case of
+ * a hanging subscription during destruction, this string will be
+ * listed in the exception's message. For reasons of efficiency, these
+ * strings are handled as <tt>const char*</tt>. Therefore, the
+ * pointers provided to subscribe() and to unsubscribe() must be the
+ * same. Strings with equal contents will not be recognized to be the
+ * same. The handling in SmartPointer will take care of this.
+ *
+ * @note Due to a problem with <tt>volatile</tt> declarations, this
+ * additional feature is switched of if multithreading is used.
+ *
+ * @author Guido Kanschat, 1998 - 2005
*/
class Subscriptor
{
Subscriptor& operator = (const Subscriptor&);
/**
- * Subscribes a user of the object.
+ * Subscribes a user of the
+ * object. The subscriber may be
+ * identified by text supplied as
+ * <tt>identifier</tt>.
*/
- void subscribe () const;
+ void subscribe (const char* identifier = 0) const;
/**
- * Unsubscribes a user from the object.
+ * Unsubscribes a user from the
+ * object.
+ *
+ * @note The <tt>identifier</tt>
+ * must be the <b>same
+ * pointer</b> as the one
+ * supplied to subscribe(), not
+ * just the same text.
*/
- void unsubscribe () const;
+ void unsubscribe (const char* identifier = 0) const;
/**
* Return the present number of
* Object may not be deleted, since
* it is used.
*/
- DeclException2(ExcInUse,
- int, char *,
+ DeclException3(ExcInUse,
+ int, char*, std::string&,
<< "Object of class " << arg2
- << " is still used by " << arg1 << " other objects.");
+ << " is still used by " << arg1 << " other objects."
+ << arg3);
/**
- * Exception: object should be used
- * when <tt>unsubscribe</tt> is called.
+ * A subscriber with the
+ * identification string given to
+ * Subscriptor::unsubscribe() did
+ * not subscribe to the object.
+ */
+ DeclException2(ExcNoSubscriber, char*, char*,
+ << "No subscriber with identifier \"" << arg2
+ << "\" did subscribe to this object of class " << arg1);
+
+ /**
+ * Exception: object should be
+ * used when
+ * Subscriptor::unsubscribe() is
+ * called.
*/
DeclException0(ExcNotUsed);
//@}
private:
+ /**
+ * The data type used in
+ * #counter_map.
+ */
+ typedef std::map<const char*, unsigned int>::value_type
+ map_value_type;
+
+ /**
+ * The iterator type used in
+ * #counter_map.
+ */
+ typedef std::map<const char*, unsigned int>::iterator
+ map_iterator;
+
/**
* Store the number of objects
* which subscribed to this
* unsubscribed again).
*
* The creator (and owner) of an
- * object is not counted.
+ * object is counted in the map
+ * below if HE manages to supply
+ * identification.
*
* We use the <tt>mutable</tt> keyword
* in order to allow subscription
* <tt>volatile</tt>. However, this is
* counter-productive in non-MT
* mode since it may pessimize
- * code. So use the above
- * template class to selectively
+ * code. So use the macro
+ * defined above to selectively
* add volatility.
*/
- mutable internal::Subscriptor::PossiblyVolatile<DEAL_II_USE_MT,unsigned int>::type counter;
-
+ mutable DEAL_VOLATILE unsigned int counter;
+
+ /**
+ * In this map, we count
+ * subscriptions for each
+ * different identification strig
+ * supplied to subscribe().
+ */
+ mutable std::map<const char*, unsigned int> counter_map;
+
/**
* Pointer to the typeinfo object
* of this object, from which we
mutable const std::type_info * object_info;
};
+//----------------------------------------------------------------------//
+
+// If we are in optimized mode, the subscription checking is turned
+// off. Therefore, we provide inline definitions of subscribe and
+// unsubscribe here. The definitions for debug mode are in
+// subscriptor.cc.
+
+#ifndef DEBUG
+
+inline void
+Subscriptor::subscribe(const char*) const
+{}
+
+
+inline void
+Subscriptor::unsubscribe(const char*) const
+{}
+
+#endif
#endif
// $Id$
// Version: $Name$
//
-// Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 by the deal authors
+// Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2005 by the deal authors
//
// This file is subject to QPL and may not be distributed
// without copyright and license information. Please refer
#include <base/thread_management.h>
#include <base/subscriptor.h>
+
#include <typeinfo>
+#include <string>
+#include <iostream>
namespace
{
}
-
-/*
-#include <set>
-
-template <class Class>
-class ActiveObjectMonitor
-{
- public:
- ~ActiveObjectMonitor ();
-
- void register_object (const Class *p);
- void deregister_object (const Class *p);
- private:
- std::set<const Class*> registered_objects;
-};
-
-ActiveObjectMonitor<Subscriptor> active_object_monitor;
-
-
-ActiveObjectMonitor::~ActiveObjectMonitor ()
-{
- if (registered_objects.size() > 0)
- {
- for (typename std::set<const Subscriptor*>::const_iterator i=registered_objects.begin();
- i!=registered_objects.end(); ++i)
- std::cout << "Object still exists of type "
- << typeid(**i).name()
- << std::endl;
- Assert (false, ExcInternalError());
- };
-};
-
-
-void ActiveObjectMonitor::register_object (const Subscriptor *p)
-{
- Assert (registered_objects.find(p) == registered_objects.end(),
- ExcInternalError());
- registered_objects.insert (p);
-};
-
-
-void
-ActiveObjectMonitor::deregister_object (const Subscriptor *p)
-{
- Assert (registered_objects.find(p) != registered_objects.end(),
- ExcInternalError());
- registered_objects.erase (registered_objects.find(p));
-};
-*/
-
-
+static const char* unknown_subscriber = "unknown subscriber";
Subscriptor::Subscriptor ()
// you can obtain the latter by
// running the c++filt program over
// the output.
- Assert (counter == 0, ExcInUse(counter, object_info->name()));
+#ifdef DEBUG
+ std::string infostring;
+ for (map_iterator it = counter_map.begin(); it != counter_map.end(); ++it)
+ {
+ if (it->second > 0)
+ infostring += std::string("\n from Subscriber ")
+ + std::string(it->first);
+ }
+
+ Assert (counter == 0, ExcInUse(counter,
+ object_info->name(),
+ infostring));
+#endif
}
return *this;
}
+// These are the implementations for debug mode. The optimized
+// versions are inlined in the header file.
-
-void Subscriptor::subscribe () const
-{
#ifdef DEBUG
+void Subscriptor::subscribe (const char* id) const
+{
if (object_info == 0)
object_info = &typeid(*this);
-#endif
-
Threads::ThreadMutex::ScopedLock lock (subscription_lock);
++counter;
+
+#if DEAL_USE_MT == 0
+ const char* const name = (id != 0) ? id : unknown_subscriber;
+
+ map_iterator it = counter_map.find(name);
+ if (it == counter_map.end())
+ counter_map.insert(map_value_type(name, 1U));
+
+ else
+ it->second++;
+#endif
}
-void Subscriptor::unsubscribe () const
+void Subscriptor::unsubscribe (const char* id) const
{
Assert (counter>0, ExcNotUsed());
Threads::ThreadMutex::ScopedLock lock (subscription_lock);
--counter;
+
+#if DEAL_USE_MT == 0
+ const char* name = (id != 0) ? id : unknown_subscriber;
+
+ map_iterator it = counter_map.find(name);
+ Assert (it != counter_map.end(), ExcNoSubscriber(object_info->name(), name));
+ Assert (it->second > 0, ExcNoSubscriber(object_info->name(), name));
+
+ it->second--;
+#endif
}
+#endif
unsigned int Subscriptor::n_subscriptions () const
// $Id$
// Version: $Name$
//
-// Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004 by the deal.II authors
+// Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 by the deal.II authors
//
// This file is subject to QPL and may not be distributed
// without copyright and license information. Please refer
streambuf *old_cerr_buf = std::cerr.rdbuf();
#endif
std::cerr.rdbuf(logfile.rdbuf());
-
- Test a("A");
- const Test b("B");
- SmartPointer<Test> r=&a;
- SmartPointer<const Test> s=&a;
-// SmartPointer<Test> t=&b; // this one should not work
- SmartPointer<Test> t=const_cast<Test*>(&b);
- SmartPointer<const Test> u=&b;
-
-
- deallog << "a ";
- a.f(); // should print "mutable", since #a# is not const
- deallog << "b ";
- b.f(); // should print "const", since #b# is const
- deallog << "r ";
- r->f(); // should print "mutable", since it points to the non-const #a#
- deallog << "s ";
- s->f(); // should print "const", since it points to the const #b#
- // but we made it const
- deallog << "t ";
- t->f(); // should print "mutable", since #b# is const, but
- // we casted the constness away
- deallog << "u ";
- u->f(); // should print "const" since #b# is const
- // Now try if subscriptor works
- {
- Test c("C");
- r = &c;
- Test d("D");
- r = &d;
- }
+ if (true)
+ {
+ Test a("A");
+ const Test b("B");
+ SmartPointer<Test> r(&a, "Test R");
+ SmartPointer<const Test> s(&a, "const Test S");
+// SmartPointer<Test> t=&b; // this one should not work
+ SmartPointer<Test> t(const_cast<Test*>(&b), "Test T");
+ SmartPointer<const Test> u(&b, "const Test");
+
+
+ deallog << "a ";
+ a.f(); // should print "mutable", since #a# is not const
+ deallog << "b ";
+ b.f(); // should print "const", since #b# is const
+ deallog << "r ";
+ r->f(); // should print "mutable", since it points to the non-const #a#
+ deallog << "s ";
+ s->f(); // should print "const", since it points to the const #b#
+ // but we made it const
+ deallog << "t ";
+ t->f(); // should print "mutable", since #b# is const, but
+ // we casted the constness away
+ deallog << "u ";
+ u->f(); // should print "const" since #b# is const
+ // Now try if subscriptor works
+ Test c("C");
+ r = &c;
+ Test d("D");
+ r = &d;
+ }
std::cerr.rdbuf(old_cerr_buf);
}
The violated condition was:
counter == 0
The name and call sequence of the exception was:
- ExcInUse(counter, object_info->name())
+ ExcInUse(counter, object_info->name(), infostring)
Additional Information:
Object of class 4Test is still used by 1 other objects.
+ from Subscriber Test R
--------------------------------------------------------
DEAL::Abort!!!
DEAL::Destruct C
+--------------------------------------------------------
+An error occurred in file <subscriptor.cc> in function
+ void Subscriptor::unsubscribe(const char*) const
+The violated condition was:
+ it != counter_map.end()
+The name and call sequence of the exception was:
+ ExcNoSubscriber(object_info->name(), name)
+Additional Information:
+No subscriber with identifier "Test R" did subscribe to this object of class 4Test
+--------------------------------------------------------
+DEAL::Abort!!!
+--------------------------------------------------------
+An error occurred in file <subscriptor.cc> in function
+ void Subscriptor::unsubscribe(const char*) const
+The violated condition was:
+ it->second > 0
+The name and call sequence of the exception was:
+ ExcNoSubscriber(object_info->name(), name)
+Additional Information:
+No subscriber with identifier "Test R" did subscribe to this object of class 4Test
+--------------------------------------------------------
+DEAL::Abort!!!
DEAL::Destruct B
DEAL::Destruct A