-#ifndef STLPLUS_CONTAINERS
-#define STLPLUS_CONTAINERS
-////////////////////////////////////////////////////////////////////////////////
-
-// Author: Andy Rushton
-// Copyright: (c) Southampton University 1999-2004
-// (c) Andy Rushton 2004-2009
-// License: BSD License, see ../docs/license.html
-
-// Allows all the STLplus containers to be included in one go
-
-////////////////////////////////////////////////////////////////////////////////
-
-#include "digraph.hpp"
-#include "foursome.hpp"
-#include "hash.hpp"
-#include "matrix.hpp"
-#include "ntree.hpp"
-#include "smart_ptr.hpp"
-#include "triple.hpp"
-
-////////////////////////////////////////////////////////////////////////////////
-#endif
+#ifndef STLPLUS_CONTAINERS\r
+#define STLPLUS_CONTAINERS\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+// Author: Andy Rushton\r
+// Copyright: (c) Southampton University 1999-2004\r
+// (c) Andy Rushton 2004 onwards\r
+// License: BSD License, see ../docs/license.html\r
+\r
+// Allows all the STLplus containers to be included in one go\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+#include "digraph.hpp"\r
+#include "foursome.hpp"\r
+#include "hash.hpp"\r
+#include "matrix.hpp"\r
+#include "ntree.hpp"\r
+#include "smart_ptr.hpp"\r
+#include "triple.hpp"\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#endif\r
-#ifndef STLPLUS_CONTAINERS_FIXES
-#define STLPLUS_CONTAINERS_FIXES
-////////////////////////////////////////////////////////////////////////////////
-
-// Author: Andy Rushton
-// Copyright: (c) Southampton University 1999-2004
-// (c) Andy Rushton 2004-2009
-// License: BSD License, see ../docs/license.html
-
-// Contains work arounds for OS or Compiler specific problems with container
-// templates
-
-////////////////////////////////////////////////////////////////////////////////
-
-////////////////////////////////////////////////////////////////////////////////
-// Unnecessary compiler warnings
-////////////////////////////////////////////////////////////////////////////////
-
-#ifdef _MSC_VER
-// Microsoft Visual Studio
-// shut up the following irritating warnings
-// 4786 - VC6, identifier string exceeded maximum allowable length and was truncated (only affects debugger)
-// 4305 - VC6, identifier type was converted to a smaller type
-// 4503 - VC6, decorated name was longer than the maximum the compiler allows (only affects debugger)
-// 4309 - VC6, type conversion operation caused a constant to exceeded the space allocated for it
-// 4290 - VC6, C++ exception specification ignored
-// 4800 - VC6, forcing value to bool 'true' or 'false' (performance warning)
-// 4355 - VC6, 'this' : used in base member initializer list
-// 4675 - VC7.1, "change" in function overload resolution _might_ have altered program
-// 4996 - VC8, 'xxxx' was declared deprecated
-#pragma warning(disable: 4786 4305 4503 4309 4290 4800 4355 4675 4996)
-#endif
-
-#ifdef __BORLANDC__
-// Borland
-// Shut up the following irritating warnings
-// 8026 - Functions with exception specifications are not expanded inline
-// 8027 - Functions with xxx are not expanded inline
-#pragma warn -8026
-#pragma warn -8027
-#endif
-
-////////////////////////////////////////////////////////////////////////////////
-// Problems with the typename keyword
-////////////////////////////////////////////////////////////////////////////////
-
-// There are problems with using the 'typename' keyword. Technically, if you
-// use a type member of a template class (i.e. a type declared within the
-// template class by a local typedef), you need to tell the compiler that it
-// is a type name. This is because the compiler cannot work out whether a
-// member is a type, a method or a data field at compile time. However,
-// support for the typename keyword has traditionally been incomplete in both
-// gcc and Visual Studio. I have used macros to try to resolve this issue. The
-// macros add the keyword for compiler versions that require it and omit it
-// for compiler versions that do not support it
-
-// There are five places where typename keywords cause problems:
-//
-// 1) in a typedef where a template class's member type is being mapped onto
-// a type definition within another template class or function
-// e.g. template<typename T> fn () {
-// typedef typename someclass<T>::member_type local_type;
-// ^^^^^^^^
-// 2) in a function parameter declaration, with similar rules to the above
-// e.g. template<typename T> fn (typename someclass<T>::member_type)
-// ^^^^^^^^
-// 3) in instantiating a template, the parameter to the template, with similar rules to the above
-// e.g. template_class<typename someclass<T>::member_type>
-// ^^^^^^^^
-// 4) Return expressions
-// e.g. return typename ntree<T>::const_iterator(this,m_root);
-// ^^^^^^^^
-// 5) Creating temporary objects when passing arguments to a function or constructor
-// e.g. return typename ntree<T>::const_prefix_iterator(typename ntree<T>::const_iterator(this,m_root));
-// ^^^^^^^^
-// Note that the typename keyword is only required when the type being referred to is a member of a template class
-//
-// So far it *seems* as if all compilers either require all of them or none of
-// them, so this set of situations can be handled by a single macro
-
-// default values, overridden for individual problem cases below
-#define TYPENAME typename
-
-// GCC
-// - pre-version 3 didn't handle typename in any of these cases
-// - version 3 onwards, typename is required for all three cases as per default
-#ifdef __GNUC__
-#if __GNUC__ < 3
-#undef TYPENAME
-#define TYPENAME
-#endif
-#endif
-
-// Visual Studio
-// - version 6 (compiler v.12) cannot handle typename in any of these cases
-// - version 7 (.NET) (compiler v.13) requires a typename in a parameter specification but supports all
-// - version 8 (2005) (compiler v.14) requires parameters and templates, supports all
-#ifdef _MSC_VER
-#if _MSC_VER <= 1200
-#undef TYPENAME
-#define TYPENAME
-#endif
-#endif
-
-// Borland
-// - doesn't handle typename in 5.5, does in 5.82, not sure about other cases
-#ifdef __BORLANDC__
-#if __BORLANDC__ <= 0x550
-#undef TYPENAME
-#define TYPENAME
-#endif
-#endif
-
-////////////////////////////////////////////////////////////////////////////////
-// Member templates
-// e.g. a template function in a template class
-
-// Not all compilers support them - this fix can be used to disable member
-// templates for compilers that don't. Unfortunately that means that some
-// functionality will be missing for those compilers.
-
-#define STLPLUS_MEMBER_TEMPLATES
-
-// Visual Studio v6 (compiler version 12) does not support them
-#ifdef _MSC_VER
-#if _MSC_VER <= 1200
-#undef STLPLUS_MEMBER_TEMPLATES
-#endif
-#endif
-
-////////////////////////////////////////////////////////////////////////////////
-#endif
+#ifndef STLPLUS_CONTAINERS_FIXES\r
+#define STLPLUS_CONTAINERS_FIXES\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+// Author: Andy Rushton\r
+// Copyright: (c) Southampton University 1999-2004\r
+// (c) Andy Rushton 2004 onwards\r
+// License: BSD License, see ../docs/license.html\r
+\r
+// Contains work arounds for OS or Compiler specific problems with container\r
+// templates\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// Unnecessary compiler warnings\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+#ifdef _MSC_VER\r
+// Microsoft Visual Studio\r
+// shut up the following irritating warnings\r
+// 4786 - VC6, identifier string exceeded maximum allowable length and was truncated (only affects debugger)\r
+// 4305 - VC6, identifier type was converted to a smaller type\r
+// 4503 - VC6, decorated name was longer than the maximum the compiler allows (only affects debugger)\r
+// 4309 - VC6, type conversion operation caused a constant to exceeded the space allocated for it\r
+// 4290 - VC6, C++ exception specification ignored\r
+// 4800 - VC6, forcing value to bool 'true' or 'false' (performance warning)\r
+// 4355 - VC6, 'this' : used in base member initializer list\r
+// 4675 - VC7.1, "change" in function overload resolution _might_ have altered program\r
+// 4996 - VC8, 'xxxx' was declared deprecated\r
+#pragma warning(disable: 4786 4305 4503 4309 4290 4800 4355 4675 4996)\r
+#endif\r
+\r
+#ifdef __BORLANDC__\r
+// Borland\r
+// Shut up the following irritating warnings\r
+// 8026 - Functions with exception specifications are not expanded inline\r
+// 8027 - Functions with xxx are not expanded inline\r
+#pragma warn -8026\r
+#pragma warn -8027\r
+#endif\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// Problems with the typename keyword\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+// There are problems with using the 'typename' keyword. Technically, if you\r
+// use a type member of a template class (i.e. a type declared within the\r
+// template class by a local typedef), you need to tell the compiler that it\r
+// is a type name. This is because the compiler cannot work out whether a\r
+// member is a type, a method or a data field at compile time. However,\r
+// support for the typename keyword has traditionally been incomplete in both\r
+// gcc and Visual Studio. I have used macros to try to resolve this issue. The\r
+// macros add the keyword for compiler versions that require it and omit it\r
+// for compiler versions that do not support it\r
+\r
+// There are five places where typename keywords cause problems:\r
+//\r
+// 1) in a typedef where a template class's member type is being mapped onto\r
+// a type definition within another template class or function \r
+// e.g. template<typename T> fn () {\r
+// typedef typename someclass<T>::member_type local_type;\r
+// ^^^^^^^^\r
+// 2) in a function parameter declaration, with similar rules to the above\r
+// e.g. template<typename T> fn (typename someclass<T>::member_type)\r
+// ^^^^^^^^\r
+// 3) in instantiating a template, the parameter to the template, with similar rules to the above\r
+// e.g. template_class<typename someclass<T>::member_type>\r
+// ^^^^^^^^\r
+// 4) Return expressions\r
+// e.g. return typename ntree<T>::const_iterator(this,m_root);\r
+// ^^^^^^^^\r
+// 5) Creating temporary objects when passing arguments to a function or constructor\r
+// e.g. return typename ntree<T>::const_prefix_iterator(typename ntree<T>::const_iterator(this,m_root));\r
+// ^^^^^^^^\r
+// Note that the typename keyword is only required when the type being referred to is a member of a template class\r
+//\r
+// So far it *seems* as if all compilers either require all of them or none of\r
+// them, so this set of situations can be handled by a single macro\r
+\r
+// default values, overridden for individual problem cases below\r
+#define TYPENAME typename\r
+\r
+// GCC \r
+// - pre-version 3 didn't handle typename in any of these cases\r
+// - version 3 onwards, typename is required for all three cases as per default\r
+#ifdef __GNUC__\r
+#if __GNUC__ < 3\r
+#undef TYPENAME\r
+#define TYPENAME\r
+#endif\r
+#endif\r
+\r
+// Visual Studio\r
+// - version 6 (compiler v.12) cannot handle typename in any of these cases\r
+// - version 7 (.NET) (compiler v.13) requires a typename in a parameter specification but supports all\r
+// - version 8 (2005) (compiler v.14) requires parameters and templates, supports all\r
+#ifdef _MSC_VER\r
+#if _MSC_VER <= 1200\r
+#undef TYPENAME\r
+#define TYPENAME\r
+#endif\r
+#endif\r
+\r
+// Borland \r
+// - doesn't handle typename in 5.5, does in 5.82, not sure about other cases\r
+#ifdef __BORLANDC__\r
+#if __BORLANDC__ <= 0x550\r
+#undef TYPENAME\r
+#define TYPENAME\r
+#endif\r
+#endif\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// Member templates\r
+// e.g. a template function in a template class\r
+\r
+// Not all compilers support them - this fix can be used to disable member\r
+// templates for compilers that don't. Unfortunately that means that some\r
+// functionality will be missing for those compilers.\r
+\r
+#define STLPLUS_MEMBER_TEMPLATES\r
+\r
+// Visual Studio v6 (compiler version 12) does not support them\r
+#ifdef _MSC_VER\r
+#if _MSC_VER <= 1200\r
+#undef STLPLUS_MEMBER_TEMPLATES\r
+#endif\r
+#endif\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#endif\r
-#ifndef STLPLUS_COPY_FUNCTORS
-#define STLPLUS_COPY_FUNCTORS
-////////////////////////////////////////////////////////////////////////////////
-
-// Author: Andy Rushton
-// Copyright: (c) Southampton University 1999-2004
-// (c) Andy Rushton 2004-2009
-// License: BSD License, see ../docs/license.html
-
-// The function constructor classes below are used by the smart_ptr and the
-// simple_ptr classes. They provide three (well ok, two) copying mechanisms.
-// These classes have been separated from the smart_ptr header by DJDM, as
-// the simple_ptr classes now also use them.
-
-////////////////////////////////////////////////////////////////////////////////
-#include "containers_fixes.hpp"
-#include "exceptions.hpp"
-
-namespace stlplus
-{
-
- ////////////////////////////////////////////////////////////////////////////////
- // copy functors implementing the three possible copy semantics
-
- // constructor_copy uses the copy constructor of the object - used for simple types
-
- template <typename T>
- class constructor_copy
- {
- public:
- T* operator() (const T& from) throw()
- {
- return new T(from);
- }
- };
-
- // clone_copy uses the clone method of the object - used for polymorphic types
-
- template <typename T>
- class clone_copy
- {
- public:
- T* operator() (const T& from) throw()
- {
- return from.clone();
- }
- };
-
- // no_copy throws an exception - used for types that cannot be copied
-
- template <typename T>
- class no_copy
- {
- public:
- T* operator() (const T& from) throw(illegal_copy)
- {
- throw illegal_copy("no_copy functor called");
- return 0;
- }
- };
-
- ////////////////////////////////////////////////////////////////////////////////
-
-} // end namespace stlplus
-
-#endif
+#ifndef STLPLUS_COPY_FUNCTORS\r
+#define STLPLUS_COPY_FUNCTORS\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+// Author: Andy Rushton\r
+// Copyright: (c) Southampton University 1999-2004\r
+// (c) Andy Rushton 2004 onwards\r
+// License: BSD License, see ../docs/license.html\r
+\r
+// The function constructor classes below are used by the smart_ptr and the\r
+// simple_ptr classes. They provide three (well ok, two) copying mechanisms.\r
+// These classes have been separated from the smart_ptr header by DJDM, as\r
+// the simple_ptr classes now also use them.\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "containers_fixes.hpp"\r
+#include "exceptions.hpp"\r
+\r
+namespace stlplus\r
+{\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // copy functors implementing the three possible copy semantics\r
+\r
+ // constructor_copy uses the copy constructor of the object - used for simple types\r
+\r
+ template <typename T>\r
+ class constructor_copy\r
+ {\r
+ public:\r
+ T* operator() (const T& from) throw()\r
+ {\r
+ return new T(from);\r
+ }\r
+ };\r
+\r
+ // clone_copy uses the clone method of the object - used for polymorphic types\r
+\r
+ template <typename T>\r
+ class clone_copy\r
+ {\r
+ public:\r
+ T* operator() (const T& from) throw()\r
+ {\r
+ return from.clone();\r
+ }\r
+ };\r
+\r
+ // no_copy throws an exception - used for types that cannot be copied\r
+\r
+ template <typename T>\r
+ class no_copy\r
+ {\r
+ public:\r
+ T* operator() (const T& from) throw(illegal_copy)\r
+ {\r
+ throw illegal_copy("no_copy functor called");\r
+ return 0;\r
+ }\r
+ };\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
+\r
+#endif\r
-#ifndef STLPLUS_DIGRAPH
-#define STLPLUS_DIGRAPH
-////////////////////////////////////////////////////////////////////////////////
-
-// Author: Andy Rushton
-// Copyright: (c) Southampton University 1999-2004
-// (c) Andy Rushton 2004-2009
-// License: BSD License, see ../docs/license.html
-
-// STL-style Directed graph template component
-// Digraph stands for directed-graph, i.e. all arcs have a direction
-
-////////////////////////////////////////////////////////////////////////////////
-#include "containers_fixes.hpp"
-#include "safe_iterator.hpp"
-#include "exceptions.hpp"
-#include <vector>
-#include <map>
-#include <set>
-
-namespace stlplus
-{
-
- ////////////////////////////////////////////////////////////////////////////////
- // Internals
-
- template<typename NT, typename AT> class digraph_node;
- template<typename NT, typename AT> class digraph_arc;
- template<typename NT, typename AT> class digraph;
-
- ////////////////////////////////////////////////////////////////////////////////
- // The Digraph iterator classes
- // a digraph_iterator points to a node whilst a digraph_arc_iterator points to an arc
- // Note that these are redefined as:
- // digraph<NT,AT>::iterator - points to a non-const node
- // digraph<NT,AT>::const_iterator - points to a const node
- // digraph<NT,AT>::arc_iterator - points to a non-const arc
- // digraph<NT,AT>::const_arc_iterator - points to a const arc
- // and this is the form in which they should be used
-
- template<typename NT, typename AT, typename NRef, typename NPtr>
- class digraph_iterator : public safe_iterator<digraph<NT,AT>, digraph_node<NT,AT> >
- {
- public:
- friend class digraph<NT,AT>;
-
- // local type definitions
- // an iterator points to an object whilst a const_iterator points to a const object
- typedef digraph_iterator<NT,AT,NT&,NT*> iterator;
- typedef digraph_iterator<NT,AT,const NT&,const NT*> const_iterator;
- typedef digraph_iterator<NT,AT,NRef,NPtr> this_iterator;
- typedef NRef reference;
- typedef NPtr pointer;
-
- // constructor to create a null iterator - you must assign a valid value to this iterator before using it
- digraph_iterator(void);
- ~digraph_iterator(void);
-
- // Type conversion methods allow const_iterator and iterator to be converted
- // convert an iterator/const_iterator to a const_iterator
- const_iterator constify(void) const;
- // convert an iterator/const_iterator to an iterator
- iterator deconstify(void) const;
-
- // increment/decrement operators used to step through the set of all nodes in a graph
- // it is only legal to increment a valid iterator
- // pre-increment
- this_iterator& operator ++ (void)
- throw(null_dereference,end_dereference);
- // post-increment
- this_iterator operator ++ (int)
- throw(null_dereference,end_dereference);
- // pre-decrement
- this_iterator& operator -- (void)
- throw(null_dereference,end_dereference);
- // post-decrement
- this_iterator operator -- (int)
- throw(null_dereference,end_dereference);
-
- // test useful for testing whether iteration has completed and for inclusion in other containers
- // Note: this class also inherits the safe_iterator methods: valid(), null(), end()
- bool operator == (const this_iterator& r) const;
- bool operator != (const this_iterator& r) const;
- bool operator < (const this_iterator& r) const;
-
- // access the node data - a const_iterator gives you a const element, an iterator a non-const element
- // it is illegal to dereference an invalid (i.e. null or end) iterator
- reference operator*(void) const
- throw(null_dereference,end_dereference);
- pointer operator->(void) const
- throw(null_dereference,end_dereference);
-
- public:
- // constructor used by digraph to create a non-null iterator
- explicit digraph_iterator(digraph_node<NT,AT>* node);
- // constructor used by digraph to create an end iterator
- explicit digraph_iterator(const digraph<NT,AT>* owner);
- // used to create an alias of an iterator
- explicit digraph_iterator(const safe_iterator<digraph<NT,AT>, digraph_node<NT,AT> >& iterator);
- };
-
- ////////////////////////////////////////////////////////////////////////////////
-
- template<typename NT, typename AT, typename ARef, typename APtr>
- class digraph_arc_iterator : public safe_iterator<digraph<NT,AT>, digraph_arc<NT,AT> >
- {
- public:
- friend class digraph<NT,AT>;
-
- // local type definitions
- // an iterator points to an object whilst a const_iterator points to a const object
- typedef digraph_arc_iterator<NT,AT,AT&,AT*> iterator;
- typedef digraph_arc_iterator<NT,AT,const AT&,const AT*> const_iterator;
- typedef digraph_arc_iterator<NT,AT,ARef,APtr> this_iterator;
- typedef ARef reference;
- typedef APtr pointer;
-
- // constructor to create a null iterator - you must assign a valid value to this iterator before using it
- digraph_arc_iterator(void);
- ~digraph_arc_iterator(void);
-
- // Type conversion methods allow const_iterator and iterator to be converted
- // convert an iterator/const_iterator to a const_iterator
- const_iterator constify(void) const;
- // convert an iterator/const_iterator to an iterator
- iterator deconstify(void) const;
-
- // increment/decrement operators used to step through the set of all nodes in a graph
- // it is only legal to increment a valid iterator
- // pre-increment
- this_iterator& operator ++ (void)
- throw(null_dereference,end_dereference);
- // post-increment
- this_iterator operator ++ (int)
- throw(null_dereference,end_dereference);
- // pre-decrement
- this_iterator& operator -- (void)
- throw(null_dereference,end_dereference);
- // post-decrement
- this_iterator operator -- (int)
- throw(null_dereference,end_dereference);
-
- // test useful for testing whether iteration has completed and for inclusion in other containers
- // Note: this class also inherits the safe_iterator methods: valid(), null(), end()
- bool operator == (const this_iterator&) const;
- bool operator != (const this_iterator&) const;
- bool operator < (const this_iterator&) const;
-
- // access the node data - a const_iterator gives you a const element, an iterator a non-const element
- // it is illegal to dereference an invalid (i.e. null or end) iterator
- reference operator*(void) const
- throw(null_dereference,end_dereference);
- pointer operator->(void) const
- throw(null_dereference,end_dereference);
-
- public:
- // constructor used by digraph to create a non-null iterator
- explicit digraph_arc_iterator(digraph_arc<NT,AT>* arc);
- // constructor used by digraph to create an end iterator
- explicit digraph_arc_iterator(const digraph<NT,AT>* owner);
- // used to create an alias of an iterator
- explicit digraph_arc_iterator(const safe_iterator<digraph<NT,AT>, digraph_arc<NT,AT> >& iterator);
- };
-
- ////////////////////////////////////////////////////////////////////////////////
- // The Graph class
- // NT is the Node type and AT is the Arc type
- ////////////////////////////////////////////////////////////////////////////////
-
- template<typename NT, typename AT>
- class digraph
- {
- public:
- // STL-like typedefs for the types and iterators
- typedef NT node_type;
- typedef AT arc_type;
- typedef digraph_iterator<NT,AT,NT&,NT*> iterator;
- typedef digraph_iterator<NT,AT,const NT&,const NT*> const_iterator;
- typedef digraph_arc_iterator<NT,AT,AT&,AT*> arc_iterator;
- typedef digraph_arc_iterator<NT,AT,const AT&,const AT*> const_arc_iterator;
-
- // supplementary types used throughout
-
- // a path is represented as a vector of arcs so the forward traversal is
- // done by going from begin() to end() or 0 to size-1 - of course a backward
- // traversal can be done by traversing the vector backwards
- typedef std::vector<arc_iterator> arc_vector;
- typedef std::vector<const_arc_iterator> const_arc_vector;
- const_arc_vector constify_arcs(const arc_vector&) const
- throw(wrong_object,null_dereference,end_dereference);
- arc_vector deconstify_arcs(const const_arc_vector&) const
- throw(wrong_object,null_dereference,end_dereference);
-
- // a path vector is a vector of paths used to represent all the paths from one node to another
- // there is no particular ordering to the paths in the vector
- typedef std::vector<arc_vector> path_vector;
- typedef std::vector<const_arc_vector> const_path_vector;
- const_path_vector constify_paths(const path_vector&) const
- throw(wrong_object,null_dereference,end_dereference);
- path_vector deconstify_paths(const const_path_vector&) const
- throw(wrong_object,null_dereference,end_dereference);
-
- // a node vector is a simple vector of nodes used to represent the reachable sets
- // there is no particular ordering to the nodes in the vector
- typedef std::vector<iterator> node_vector;
- typedef std::vector<const_iterator> const_node_vector;
- const_node_vector constify_nodes(const node_vector&) const
- throw(wrong_object,null_dereference,end_dereference);
- node_vector deconstify_nodes(const const_node_vector&) const
- throw(wrong_object,null_dereference,end_dereference);
-
- // callback used in the path algorithms to select which arcs to consider
- typedef bool (*arc_select_fn) (const digraph<NT,AT>&, const_arc_iterator);
-
- // a value representing an unknown offset
- // Note that it's static so use in the form digraph<NT,AT>::npos()
- static unsigned npos(void);
-
- //////////////////////////////////////////////////////////////////////////
- // Constructors, destructors and copies
-
- digraph(void);
- ~digraph(void);
-
- // copy constructor and assignment both copy the graph
- digraph(const digraph<NT,AT>&);
- digraph<NT,AT>& operator=(const digraph<NT,AT>&);
-
- //////////////////////////////////////////////////////////////////////////
- // Basic Node functions
- // Nodes are referred to by iterators created when the node is inserted.
- // Iterators remain valid unless the node is erased (they are list iterators, so no resize problems)
- // It is also possible to walk through all the nodes using a list-like start() to end() loop
- // Each node has a set of input arcs and output arcs. These are indexed by an unsigned i.e. they form a vector.
- // The total number of inputs is the fanin and the total number of outputs is the fanout.
- // The contents of the node (type NT) are accessed, of course, by dereferencing the node iterator.
-
- // tests for the number of nodes and the special test for zero nodes
- bool empty(void) const;
- unsigned size(void) const;
-
- // add a new node and return its iterator
- iterator insert(const NT& node_data);
-
- // remove a node and return the iterator to the next node
- // erasing a node erases its arcs
- iterator erase(iterator)
- throw(wrong_object,null_dereference,end_dereference);
- // remove all nodes
- void clear(void);
-
- // traverse all the nodes in no particular order using STL-style iteration
- const_iterator begin(void) const;
- iterator begin(void);
- const_iterator end(void) const;
- iterator end(void);
-
- // access the inputs of this node
- // the fanin is the number of inputs and the inputs are accessed using an index from 0..fanin-1
- unsigned fanin(const_iterator) const
- throw(wrong_object,null_dereference,end_dereference);
- unsigned fanin(iterator)
- throw(wrong_object,null_dereference,end_dereference);
- const_arc_iterator input(const_iterator, unsigned) const
- throw(wrong_object,null_dereference,end_dereference,std::out_of_range);
- arc_iterator input(iterator, unsigned)
- throw(wrong_object,null_dereference,end_dereference,std::out_of_range);
-
- // access the outputs of this node
- // the fanout is the number of outputs and the outputs are accessed using an index from 0..fanout-1
- unsigned fanout(const_iterator) const
- throw(wrong_object,null_dereference,end_dereference);
- unsigned fanout(iterator)
- throw(wrong_object,null_dereference,end_dereference);
- const_arc_iterator output(const_iterator, unsigned) const
- throw(wrong_object,null_dereference,end_dereference,std::out_of_range);
- arc_iterator output(iterator, unsigned)
- throw(wrong_object,null_dereference,end_dereference,std::out_of_range);
-
- // convenience routines for getting the set of all inputs or all outputs as vectors
- const_arc_vector inputs(const_iterator) const
- throw(wrong_object,null_dereference,end_dereference);
- arc_vector inputs(iterator)
- throw(wrong_object,null_dereference,end_dereference);
- const_arc_vector outputs(const_iterator) const
- throw(wrong_object,null_dereference,end_dereference);
- arc_vector outputs(iterator)
- throw(wrong_object,null_dereference,end_dereference);
-
- // find the output index of an arc which goes from this node
- // returns digraph<NT,AT>::npos if the arc is not an output of from
- unsigned output_offset(const_iterator from, const_arc_iterator arc) const
- throw(wrong_object,null_dereference,end_dereference);
- unsigned output_offset(iterator from, arc_iterator arc)
- throw(wrong_object,null_dereference,end_dereference);
- // ditto for an input arc
- unsigned input_offset(const_iterator to, const_arc_iterator arc) const
- throw(wrong_object,null_dereference,end_dereference);
- unsigned input_offset(iterator to, arc_iterator arc)
- throw(wrong_object,null_dereference,end_dereference);
-
- //////////////////////////////////////////////////////////////////////////
- // Basic Arc functions
- // to avoid name conflicts, arc functions have the arc_ prefix
- // Arcs, like nodes, are referred to by a list iterator which is returned by the arc_insert function
- // They may also be visited from arc_begin() to arc_end()
- // Each arc has a from field and a to field which contain the node iterators of the endpoints of the arc
- // Of course, the arc data can be accessed by simply dereferencing the iterator
-
- // tests for the number of arcs and the special test for zero arcs
- bool arc_empty (void) const;
- unsigned arc_size(void) const;
-
- // add a new arc and return its iterator
- arc_iterator arc_insert(iterator from, iterator to, const AT& arc_data = AT())
- throw(wrong_object,null_dereference,end_dereference);
-
- // remove an arc and return the iterator to the next arc
- arc_iterator arc_erase(arc_iterator)
- throw(wrong_object,null_dereference,end_dereference);
- // remove all arcs
- void arc_clear(void);
-
- // traverse all the arcs in no particular order using STL-style iteration
- const_arc_iterator arc_begin(void) const;
- arc_iterator arc_begin(void);
- const_arc_iterator arc_end(void) const;
- arc_iterator arc_end(void);
-
- // find the node that an arc points from or to
- const_iterator arc_from(const_arc_iterator) const
- throw(wrong_object,null_dereference,end_dereference);
- iterator arc_from(arc_iterator)
- throw(wrong_object,null_dereference,end_dereference);
- const_iterator arc_to(const_arc_iterator) const
- throw(wrong_object,null_dereference,end_dereference);
- iterator arc_to(arc_iterator)
- throw(wrong_object,null_dereference,end_dereference);
-
- // reconnect an arc to a different from and to node
- void arc_move(arc_iterator arc, iterator from, iterator to)
- throw(wrong_object,null_dereference,end_dereference);
- // reconnect just the from node
- void arc_move_from(arc_iterator arc, iterator from)
- throw(wrong_object,null_dereference,end_dereference);
- // reconnect just the to node
- void arc_move_to(arc_iterator arc, iterator to)
- throw(wrong_object,null_dereference,end_dereference);
- // reverse the arc direction so that to becomes from and vice-versa
- void arc_flip(arc_iterator arc)
- throw(wrong_object,null_dereference,end_dereference);
-
- ////////////////////////////////////////////////////////////////////////////////
- // Adjacency algorithms
-
- // test whether the nodes are adjacent i.e. whether there is an arc going from from to to
- bool adjacent(const_iterator from, const_iterator to) const
- throw(wrong_object,null_dereference,end_dereference);
- bool adjacent(iterator from, iterator to)
- throw(wrong_object,null_dereference,end_dereference);
-
- // as above, but returns the arc that makes the nodes adjacent
- // returns the first arc if there's more than one, returns arc_end() if there are none
- const_arc_iterator adjacent_arc(const_iterator from, const_iterator to) const
- throw(wrong_object,null_dereference,end_dereference);
- arc_iterator adjacent_arc(iterator from, iterator to)
- throw(wrong_object,null_dereference,end_dereference);
-
- // as above, but returns the set of all arcs that make two nodes adjacent (there may be more than one)
- // returns an empty vector if there are none
- const_arc_vector adjacent_arcs(const_iterator from, const_iterator to) const
- throw(wrong_object,null_dereference,end_dereference);
- arc_vector adjacent_arcs(iterator from, iterator to)
- throw(wrong_object,null_dereference,end_dereference);
-
- // return the adjacency sets for the node inputs or outputs, i.e. the set of nodes adjacent to this node
- // each adjacent node will only be entered once even if there are multiple arcs between the nodes
- const_node_vector input_adjacencies(const_iterator to) const
- throw(wrong_object,null_dereference,end_dereference);
- node_vector input_adjacencies(iterator to)
- throw(wrong_object,null_dereference,end_dereference);
- const_node_vector output_adjacencies(const_iterator from) const
- throw(wrong_object,null_dereference,end_dereference);
- node_vector output_adjacencies(iterator from)
- throw(wrong_object,null_dereference,end_dereference);
-
- ////////////////////////////////////////////////////////////////////////////////
- // Topographical Sort Algorithm
- // This generates a node ordering such that each node is visited after its fanin nodes.
-
- // This only generates a valid ordering for a DAG.
-
- // The return value is a pair :
- // - the node vector which is a set of iterators to the nodes in sorted order
- // - the arc vector is the set of backward ards that were broken to achieve the sort
- // If the arc vector is empty then the graph formed a DAG.
-
- // The arc selection callback can be used to ignore arcs that are not part
- // of the ordering, i.e. arcs that are meant to be backwards arcs
-
- std::pair<const_node_vector,const_arc_vector> sort(arc_select_fn = 0) const;
- std::pair<node_vector,arc_vector> sort(arc_select_fn = 0);
-
- // Simplified variant of above for graphs that are known to be DAGs.
- // If the sort fails due to backward arcs, the
- // return vector is empty. Note that this will also be empty if the graph
- // has no nodes in it, so use the empty() method to differentiate.
-
- const_node_vector dag_sort(arc_select_fn = 0) const;
- node_vector dag_sort(arc_select_fn = 0);
-
- ////////////////////////////////////////////////////////////////////////////////
- // Basic Path Algorithms
- // A path is a series of arcs - you can use arc_from and arc_to to convert
- // that into a series of nodes. All the path algorithms take an arc_select
- // which allows arcs to be selected or rejected for consideration in a path.
-
- // A selection callback function is applied to each arc in the traversal and
- // returns true if the arc is to be selected and false if the arc is to be
- // rejected. If no function is provided the arc is selected. If you want to
- // use arc selection you should create a function with the type profile given
- // by the arc_select_fn type. The select function is passed both the graph and
- // the arc iterator so that it is possible to select an arc on the basis of
- // the nodes it is connected to.
-
- // Note: I used a callback because the STL-like predicate idea wasn't working for me...
-
- // test for the existence of a path from from to to
- bool path_exists(const_iterator from, const_iterator to, arc_select_fn = 0) const
- throw(wrong_object,null_dereference,end_dereference);
- bool path_exists(iterator from, iterator to, arc_select_fn = 0)
- throw(wrong_object,null_dereference,end_dereference);
-
- // get the set of all paths from from to to
- const_path_vector all_paths(const_iterator from, const_iterator to, arc_select_fn = 0) const
- throw(wrong_object,null_dereference,end_dereference);
- path_vector all_paths(iterator from, iterator to, arc_select_fn = 0)
- throw(wrong_object,null_dereference,end_dereference);
-
- // get the set of all nodes that can be reached by any path from from
- const_node_vector reachable_nodes(const_iterator from, arc_select_fn = 0) const
- throw(wrong_object,null_dereference,end_dereference);
- node_vector reachable_nodes(iterator from, arc_select_fn = 0)
- throw(wrong_object,null_dereference,end_dereference);
-
- // get the set of all nodes that can reach to to by any path
- const_node_vector reaching_nodes(const_iterator to, arc_select_fn = 0) const
- throw(wrong_object,null_dereference,end_dereference);
- node_vector reaching_nodes(iterator to, arc_select_fn = 0)
- throw(wrong_object,null_dereference,end_dereference);
-
- ////////////////////////////////////////////////////////////////////////////////
- // Unweighted Shortest path algorithms
-
- // find the shortest path from from to to
- // This is an unweighted shortest path algorithm, i.e. the weight of each
- // arc is assumed to be 1, so just counts the number of arcs
- // if there is more than one shortest path it returns the first one
- // If there are no paths, returns an empty path
- const_arc_vector shortest_path(const_iterator from, const_iterator to, arc_select_fn = 0) const
- throw(wrong_object,null_dereference,end_dereference);
- arc_vector shortest_path(iterator from, iterator to, arc_select_fn = 0)
- throw(wrong_object,null_dereference,end_dereference);
-
- // find the set of shortest paths from from to any other node in the graph
- // that is reachable (i.e. for which path_exists() is true)
- // This is an unweighted shortest path, so just counts the number of arcs
- // if there is more than one shortest path to a node it returns the first one
- // If there are no paths, returns an empty list
- const_path_vector shortest_paths(const_iterator from, arc_select_fn = 0) const
- throw(wrong_object,null_dereference,end_dereference);
- path_vector shortest_paths(iterator from, arc_select_fn = 0)
- throw(wrong_object,null_dereference,end_dereference);
-
- private:
- friend class digraph_iterator<NT,AT,NT&,NT*>;
- friend class digraph_iterator<NT,AT,const NT&,const NT*>;
- friend class digraph_arc_iterator<NT,AT,AT&,AT*>;
- friend class digraph_arc_iterator<NT,AT,const AT&, const AT*>;
-
- typedef std::set<const_iterator> const_iterator_set;
- typedef TYPENAME const_iterator_set::iterator const_iterator_set_iterator;
-
- bool path_exists_r(const_iterator from, const_iterator to, const_iterator_set& visited, arc_select_fn) const
- throw(wrong_object,null_dereference,end_dereference);
-
- void all_paths_r(const_iterator from, const_iterator to, const_arc_vector& so_far, const_path_vector& result, arc_select_fn) const
- throw(wrong_object,null_dereference,end_dereference);
-
- void reachable_nodes_r(const_iterator from, const_iterator_set& visited, arc_select_fn) const
- throw(wrong_object,null_dereference,end_dereference);
-
- void reaching_nodes_r(const_iterator to, const_iterator_set& visited, arc_select_fn) const
- throw(wrong_object,null_dereference,end_dereference);
-
- digraph_node<NT,AT>* m_nodes_begin;
- digraph_node<NT,AT>* m_nodes_end;
- digraph_arc<NT,AT>* m_arcs_begin;
- digraph_arc<NT,AT>* m_arcs_end;
- };
-
- ////////////////////////////////////////////////////////////////////////////////
-
-} // end namespace stlplus
-
-#include "digraph.tpp"
-#endif
+#ifndef STLPLUS_DIGRAPH\r
+#define STLPLUS_DIGRAPH\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+// Author: Andy Rushton\r
+// Copyright: (c) Southampton University 1999-2004\r
+// (c) Andy Rushton 2004 onwards\r
+// License: BSD License, see ../docs/license.html\r
+\r
+// STL-style Directed graph template component\r
+// Digraph stands for directed-graph, i.e. all arcs have a direction\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "containers_fixes.hpp"\r
+#include "safe_iterator.hpp"\r
+#include "exceptions.hpp"\r
+#include <vector>\r
+#include <map>\r
+#include <set>\r
+\r
+namespace stlplus\r
+{\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // Internals\r
+\r
+ template<typename NT, typename AT> class digraph_node;\r
+ template<typename NT, typename AT> class digraph_arc;\r
+ template<typename NT, typename AT> class digraph;\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // The Digraph iterator classes\r
+ // a digraph_iterator points to a node whilst a digraph_arc_iterator points to an arc\r
+ // Note that these are redefined as:\r
+ // digraph<NT,AT>::iterator - points to a non-const node\r
+ // digraph<NT,AT>::const_iterator - points to a const node\r
+ // digraph<NT,AT>::arc_iterator - points to a non-const arc\r
+ // digraph<NT,AT>::const_arc_iterator - points to a const arc\r
+ // and this is the form in which they should be used\r
+\r
+ template<typename NT, typename AT, typename NRef, typename NPtr>\r
+ class digraph_iterator : public safe_iterator<digraph<NT,AT>, digraph_node<NT,AT> >\r
+ {\r
+ public:\r
+ friend class digraph<NT,AT>;\r
+\r
+ // local type definitions\r
+ // an iterator points to an object whilst a const_iterator points to a const object\r
+ typedef digraph_iterator<NT,AT,NT&,NT*> iterator;\r
+ typedef digraph_iterator<NT,AT,const NT&,const NT*> const_iterator;\r
+ typedef digraph_iterator<NT,AT,NRef,NPtr> this_iterator;\r
+ typedef NRef reference;\r
+ typedef NPtr pointer;\r
+\r
+ // constructor to create a null iterator - you must assign a valid value to this iterator before using it\r
+ digraph_iterator(void);\r
+ ~digraph_iterator(void);\r
+\r
+ // Type conversion methods allow const_iterator and iterator to be converted\r
+ // convert an iterator/const_iterator to a const_iterator\r
+ const_iterator constify(void) const;\r
+ // convert an iterator/const_iterator to an iterator\r
+ iterator deconstify(void) const;\r
+\r
+ // increment/decrement operators used to step through the set of all nodes in a graph\r
+ // it is only legal to increment a valid iterator\r
+ // pre-increment\r
+ this_iterator& operator ++ (void)\r
+ throw(null_dereference,end_dereference);\r
+ // post-increment\r
+ this_iterator operator ++ (int)\r
+ throw(null_dereference,end_dereference);\r
+ // pre-decrement\r
+ this_iterator& operator -- (void)\r
+ throw(null_dereference,end_dereference);\r
+ // post-decrement\r
+ this_iterator operator -- (int)\r
+ throw(null_dereference,end_dereference);\r
+\r
+ // test useful for testing whether iteration has completed and for inclusion in other containers\r
+ // Note: this class also inherits the safe_iterator methods: valid(), null(), end()\r
+ bool operator == (const this_iterator& r) const;\r
+ bool operator != (const this_iterator& r) const;\r
+ bool operator < (const this_iterator& r) const;\r
+\r
+ // access the node data - a const_iterator gives you a const element, an iterator a non-const element\r
+ // it is illegal to dereference an invalid (i.e. null or end) iterator\r
+ reference operator*(void) const\r
+ throw(null_dereference,end_dereference);\r
+ pointer operator->(void) const\r
+ throw(null_dereference,end_dereference);\r
+\r
+ public:\r
+ // constructor used by digraph to create a non-null iterator\r
+ explicit digraph_iterator(digraph_node<NT,AT>* node);\r
+ // constructor used by digraph to create an end iterator\r
+ explicit digraph_iterator(const digraph<NT,AT>* owner);\r
+ // used to create an alias of an iterator\r
+ explicit digraph_iterator(const safe_iterator<digraph<NT,AT>, digraph_node<NT,AT> >& iterator);\r
+ };\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+\r
+ template<typename NT, typename AT, typename ARef, typename APtr>\r
+ class digraph_arc_iterator : public safe_iterator<digraph<NT,AT>, digraph_arc<NT,AT> >\r
+ {\r
+ public:\r
+ friend class digraph<NT,AT>;\r
+\r
+ // local type definitions\r
+ // an iterator points to an object whilst a const_iterator points to a const object\r
+ typedef digraph_arc_iterator<NT,AT,AT&,AT*> iterator;\r
+ typedef digraph_arc_iterator<NT,AT,const AT&,const AT*> const_iterator;\r
+ typedef digraph_arc_iterator<NT,AT,ARef,APtr> this_iterator;\r
+ typedef ARef reference;\r
+ typedef APtr pointer;\r
+\r
+ // constructor to create a null iterator - you must assign a valid value to this iterator before using it\r
+ digraph_arc_iterator(void);\r
+ ~digraph_arc_iterator(void);\r
+\r
+ // Type conversion methods allow const_iterator and iterator to be converted\r
+ // convert an iterator/const_iterator to a const_iterator\r
+ const_iterator constify(void) const;\r
+ // convert an iterator/const_iterator to an iterator\r
+ iterator deconstify(void) const;\r
+\r
+ // increment/decrement operators used to step through the set of all nodes in a graph\r
+ // it is only legal to increment a valid iterator\r
+ // pre-increment\r
+ this_iterator& operator ++ (void)\r
+ throw(null_dereference,end_dereference);\r
+ // post-increment\r
+ this_iterator operator ++ (int)\r
+ throw(null_dereference,end_dereference);\r
+ // pre-decrement\r
+ this_iterator& operator -- (void)\r
+ throw(null_dereference,end_dereference);\r
+ // post-decrement\r
+ this_iterator operator -- (int)\r
+ throw(null_dereference,end_dereference);\r
+\r
+ // test useful for testing whether iteration has completed and for inclusion in other containers\r
+ // Note: this class also inherits the safe_iterator methods: valid(), null(), end()\r
+ bool operator == (const this_iterator&) const;\r
+ bool operator != (const this_iterator&) const;\r
+ bool operator < (const this_iterator&) const;\r
+\r
+ // access the node data - a const_iterator gives you a const element, an iterator a non-const element\r
+ // it is illegal to dereference an invalid (i.e. null or end) iterator\r
+ reference operator*(void) const\r
+ throw(null_dereference,end_dereference);\r
+ pointer operator->(void) const\r
+ throw(null_dereference,end_dereference);\r
+\r
+ public:\r
+ // constructor used by digraph to create a non-null iterator\r
+ explicit digraph_arc_iterator(digraph_arc<NT,AT>* arc);\r
+ // constructor used by digraph to create an end iterator\r
+ explicit digraph_arc_iterator(const digraph<NT,AT>* owner);\r
+ // used to create an alias of an iterator\r
+ explicit digraph_arc_iterator(const safe_iterator<digraph<NT,AT>, digraph_arc<NT,AT> >& iterator);\r
+ };\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // The Graph class\r
+ // NT is the Node type and AT is the Arc type\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+\r
+ template<typename NT, typename AT>\r
+ class digraph\r
+ {\r
+ public:\r
+ // STL-like typedefs for the types and iterators\r
+ typedef NT node_type;\r
+ typedef AT arc_type;\r
+ typedef digraph_iterator<NT,AT,NT&,NT*> iterator;\r
+ typedef digraph_iterator<NT,AT,const NT&,const NT*> const_iterator;\r
+ typedef digraph_arc_iterator<NT,AT,AT&,AT*> arc_iterator;\r
+ typedef digraph_arc_iterator<NT,AT,const AT&,const AT*> const_arc_iterator;\r
+\r
+ // supplementary types used throughout\r
+\r
+ // a path is represented as a vector of arcs so the forward traversal is\r
+ // done by going from begin() to end() or 0 to size-1 - of course a backward\r
+ // traversal can be done by traversing the vector backwards\r
+ typedef std::vector<arc_iterator> arc_vector;\r
+ typedef std::vector<const_arc_iterator> const_arc_vector;\r
+ const_arc_vector constify_arcs(const arc_vector&) const\r
+ throw(wrong_object,null_dereference,end_dereference);\r
+ arc_vector deconstify_arcs(const const_arc_vector&) const\r
+ throw(wrong_object,null_dereference,end_dereference);\r
+\r
+ // a path vector is a vector of paths used to represent all the paths from one node to another\r
+ // there is no particular ordering to the paths in the vector\r
+ typedef std::vector<arc_vector> path_vector;\r
+ typedef std::vector<const_arc_vector> const_path_vector;\r
+ const_path_vector constify_paths(const path_vector&) const\r
+ throw(wrong_object,null_dereference,end_dereference);\r
+ path_vector deconstify_paths(const const_path_vector&) const\r
+ throw(wrong_object,null_dereference,end_dereference);\r
+\r
+ // a node vector is a simple vector of nodes used to represent the reachable sets\r
+ // there is no particular ordering to the nodes in the vector\r
+ typedef std::vector<iterator> node_vector;\r
+ typedef std::vector<const_iterator> const_node_vector;\r
+ const_node_vector constify_nodes(const node_vector&) const\r
+ throw(wrong_object,null_dereference,end_dereference);\r
+ node_vector deconstify_nodes(const const_node_vector&) const\r
+ throw(wrong_object,null_dereference,end_dereference);\r
+\r
+ // callback used in the path algorithms to select which arcs to consider\r
+ typedef bool (*arc_select_fn) (const digraph<NT,AT>&, const_arc_iterator);\r
+\r
+ // a value representing an unknown offset\r
+ // Note that it's static so use in the form digraph<NT,AT>::npos()\r
+ static unsigned npos(void);\r
+\r
+ //////////////////////////////////////////////////////////////////////////\r
+ // Constructors, destructors and copies\r
+\r
+ digraph(void);\r
+ ~digraph(void);\r
+\r
+ // copy constructor and assignment both copy the graph\r
+ digraph(const digraph<NT,AT>&);\r
+ digraph<NT,AT>& operator=(const digraph<NT,AT>&);\r
+\r
+ //////////////////////////////////////////////////////////////////////////\r
+ // Basic Node functions\r
+ // Nodes are referred to by iterators created when the node is inserted.\r
+ // Iterators remain valid unless the node is erased (they are list iterators, so no resize problems)\r
+ // It is also possible to walk through all the nodes using a list-like start() to end() loop\r
+ // Each node has a set of input arcs and output arcs. These are indexed by an unsigned i.e. they form a vector.\r
+ // The total number of inputs is the fanin and the total number of outputs is the fanout.\r
+ // The contents of the node (type NT) are accessed, of course, by dereferencing the node iterator.\r
+\r
+ // tests for the number of nodes and the special test for zero nodes\r
+ bool empty(void) const;\r
+ unsigned size(void) const;\r
+\r
+ // add a new node and return its iterator\r
+ iterator insert(const NT& node_data);\r
+\r
+ // remove a node and return the iterator to the next node\r
+ // erasing a node erases its arcs\r
+ iterator erase(iterator)\r
+ throw(wrong_object,null_dereference,end_dereference);\r
+ // remove all nodes\r
+ void clear(void);\r
+\r
+ // traverse all the nodes in no particular order using STL-style iteration\r
+ const_iterator begin(void) const;\r
+ iterator begin(void);\r
+ const_iterator end(void) const;\r
+ iterator end(void);\r
+\r
+ // access the inputs of this node\r
+ // the fanin is the number of inputs and the inputs are accessed using an index from 0..fanin-1\r
+ unsigned fanin(const_iterator) const\r
+ throw(wrong_object,null_dereference,end_dereference);\r
+ unsigned fanin(iterator)\r
+ throw(wrong_object,null_dereference,end_dereference);\r
+ const_arc_iterator input(const_iterator, unsigned) const\r
+ throw(wrong_object,null_dereference,end_dereference,std::out_of_range);\r
+ arc_iterator input(iterator, unsigned)\r
+ throw(wrong_object,null_dereference,end_dereference,std::out_of_range);\r
+\r
+ // access the outputs of this node\r
+ // the fanout is the number of outputs and the outputs are accessed using an index from 0..fanout-1\r
+ unsigned fanout(const_iterator) const\r
+ throw(wrong_object,null_dereference,end_dereference);\r
+ unsigned fanout(iterator)\r
+ throw(wrong_object,null_dereference,end_dereference);\r
+ const_arc_iterator output(const_iterator, unsigned) const\r
+ throw(wrong_object,null_dereference,end_dereference,std::out_of_range);\r
+ arc_iterator output(iterator, unsigned)\r
+ throw(wrong_object,null_dereference,end_dereference,std::out_of_range);\r
+\r
+ // convenience routines for getting the set of all inputs or all outputs as vectors\r
+ const_arc_vector inputs(const_iterator) const\r
+ throw(wrong_object,null_dereference,end_dereference);\r
+ arc_vector inputs(iterator)\r
+ throw(wrong_object,null_dereference,end_dereference);\r
+ const_arc_vector outputs(const_iterator) const\r
+ throw(wrong_object,null_dereference,end_dereference);\r
+ arc_vector outputs(iterator)\r
+ throw(wrong_object,null_dereference,end_dereference);\r
+\r
+ // find the output index of an arc which goes from this node\r
+ // returns digraph<NT,AT>::npos if the arc is not an output of from\r
+ unsigned output_offset(const_iterator from, const_arc_iterator arc) const\r
+ throw(wrong_object,null_dereference,end_dereference);\r
+ unsigned output_offset(iterator from, arc_iterator arc)\r
+ throw(wrong_object,null_dereference,end_dereference);\r
+ // ditto for an input arc\r
+ unsigned input_offset(const_iterator to, const_arc_iterator arc) const\r
+ throw(wrong_object,null_dereference,end_dereference);\r
+ unsigned input_offset(iterator to, arc_iterator arc)\r
+ throw(wrong_object,null_dereference,end_dereference);\r
+\r
+ //////////////////////////////////////////////////////////////////////////\r
+ // Basic Arc functions\r
+ // to avoid name conflicts, arc functions have the arc_ prefix \r
+ // Arcs, like nodes, are referred to by a list iterator which is returned by the arc_insert function\r
+ // They may also be visited from arc_begin() to arc_end()\r
+ // Each arc has a from field and a to field which contain the node iterators of the endpoints of the arc\r
+ // Of course, the arc data can be accessed by simply dereferencing the iterator\r
+\r
+ // tests for the number of arcs and the special test for zero arcs\r
+ bool arc_empty (void) const;\r
+ unsigned arc_size(void) const;\r
+\r
+ // add a new arc and return its iterator\r
+ arc_iterator arc_insert(iterator from, iterator to, const AT& arc_data = AT())\r
+ throw(wrong_object,null_dereference,end_dereference);\r
+\r
+ // remove an arc and return the iterator to the next arc\r
+ arc_iterator arc_erase(arc_iterator)\r
+ throw(wrong_object,null_dereference,end_dereference);\r
+ // remove all arcs\r
+ void arc_clear(void);\r
+\r
+ // traverse all the arcs in no particular order using STL-style iteration\r
+ const_arc_iterator arc_begin(void) const;\r
+ arc_iterator arc_begin(void);\r
+ const_arc_iterator arc_end(void) const;\r
+ arc_iterator arc_end(void);\r
+\r
+ // find the node that an arc points from or to\r
+ const_iterator arc_from(const_arc_iterator) const\r
+ throw(wrong_object,null_dereference,end_dereference);\r
+ iterator arc_from(arc_iterator)\r
+ throw(wrong_object,null_dereference,end_dereference);\r
+ const_iterator arc_to(const_arc_iterator) const\r
+ throw(wrong_object,null_dereference,end_dereference);\r
+ iterator arc_to(arc_iterator)\r
+ throw(wrong_object,null_dereference,end_dereference);\r
+\r
+ // reconnect an arc to a different from and to node\r
+ void arc_move(arc_iterator arc, iterator from, iterator to)\r
+ throw(wrong_object,null_dereference,end_dereference);\r
+ // reconnect just the from node\r
+ void arc_move_from(arc_iterator arc, iterator from)\r
+ throw(wrong_object,null_dereference,end_dereference);\r
+ // reconnect just the to node\r
+ void arc_move_to(arc_iterator arc, iterator to)\r
+ throw(wrong_object,null_dereference,end_dereference);\r
+ // reverse the arc direction so that to becomes from and vice-versa\r
+ void arc_flip(arc_iterator arc)\r
+ throw(wrong_object,null_dereference,end_dereference);\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // Adjacency algorithms\r
+\r
+ // test whether the nodes are adjacent i.e. whether there is an arc going from from to to\r
+ bool adjacent(const_iterator from, const_iterator to) const\r
+ throw(wrong_object,null_dereference,end_dereference);\r
+ bool adjacent(iterator from, iterator to)\r
+ throw(wrong_object,null_dereference,end_dereference);\r
+\r
+ // as above, but returns the arc that makes the nodes adjacent\r
+ // returns the first arc if there's more than one, returns arc_end() if there are none\r
+ const_arc_iterator adjacent_arc(const_iterator from, const_iterator to) const\r
+ throw(wrong_object,null_dereference,end_dereference);\r
+ arc_iterator adjacent_arc(iterator from, iterator to)\r
+ throw(wrong_object,null_dereference,end_dereference);\r
+\r
+ // as above, but returns the set of all arcs that make two nodes adjacent (there may be more than one)\r
+ // returns an empty vector if there are none\r
+ const_arc_vector adjacent_arcs(const_iterator from, const_iterator to) const\r
+ throw(wrong_object,null_dereference,end_dereference);\r
+ arc_vector adjacent_arcs(iterator from, iterator to)\r
+ throw(wrong_object,null_dereference,end_dereference);\r
+\r
+ // return the adjacency sets for the node inputs or outputs, i.e. the set of nodes adjacent to this node\r
+ // each adjacent node will only be entered once even if there are multiple arcs between the nodes\r
+ const_node_vector input_adjacencies(const_iterator to) const\r
+ throw(wrong_object,null_dereference,end_dereference);\r
+ node_vector input_adjacencies(iterator to)\r
+ throw(wrong_object,null_dereference,end_dereference);\r
+ const_node_vector output_adjacencies(const_iterator from) const\r
+ throw(wrong_object,null_dereference,end_dereference);\r
+ node_vector output_adjacencies(iterator from)\r
+ throw(wrong_object,null_dereference,end_dereference);\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // Topographical Sort Algorithm\r
+ // This generates a node ordering such that each node is visited after its fanin nodes.\r
+\r
+ // This only generates a valid ordering for a DAG. \r
+\r
+ // The return value is a pair : \r
+ // - the node vector which is a set of iterators to the nodes in sorted order\r
+ // - the arc vector is the set of backward ards that were broken to achieve the sort\r
+ // If the arc vector is empty then the graph formed a DAG.\r
+\r
+ // The arc selection callback can be used to ignore arcs that are not part\r
+ // of the ordering, i.e. arcs that are meant to be backwards arcs\r
+\r
+ std::pair<const_node_vector,const_arc_vector> sort(arc_select_fn = 0) const;\r
+ std::pair<node_vector,arc_vector> sort(arc_select_fn = 0);\r
+\r
+ // Simplified variant of above for graphs that are known to be DAGs.\r
+ // If the sort fails due to backward arcs, the\r
+ // return vector is empty. Note that this will also be empty if the graph\r
+ // has no nodes in it, so use the empty() method to differentiate.\r
+\r
+ const_node_vector dag_sort(arc_select_fn = 0) const;\r
+ node_vector dag_sort(arc_select_fn = 0);\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // Basic Path Algorithms\r
+ // A path is a series of arcs - you can use arc_from and arc_to to convert\r
+ // that into a series of nodes. All the path algorithms take an arc_select\r
+ // which allows arcs to be selected or rejected for consideration in a path.\r
+\r
+ // A selection callback function is applied to each arc in the traversal and\r
+ // returns true if the arc is to be selected and false if the arc is to be\r
+ // rejected. If no function is provided the arc is selected. If you want to\r
+ // use arc selection you should create a function with the type profile given\r
+ // by the arc_select_fn type. The select function is passed both the graph and\r
+ // the arc iterator so that it is possible to select an arc on the basis of\r
+ // the nodes it is connected to.\r
+\r
+ // Note: I used a callback because the STL-like predicate idea wasn't working for me...\r
+\r
+ // test for the existence of a path from from to to\r
+ bool path_exists(const_iterator from, const_iterator to, arc_select_fn = 0) const\r
+ throw(wrong_object,null_dereference,end_dereference);\r
+ bool path_exists(iterator from, iterator to, arc_select_fn = 0)\r
+ throw(wrong_object,null_dereference,end_dereference);\r
+\r
+ // get the set of all paths from from to to\r
+ const_path_vector all_paths(const_iterator from, const_iterator to, arc_select_fn = 0) const\r
+ throw(wrong_object,null_dereference,end_dereference);\r
+ path_vector all_paths(iterator from, iterator to, arc_select_fn = 0)\r
+ throw(wrong_object,null_dereference,end_dereference);\r
+\r
+ // get the set of all nodes that can be reached by any path from from\r
+ const_node_vector reachable_nodes(const_iterator from, arc_select_fn = 0) const\r
+ throw(wrong_object,null_dereference,end_dereference);\r
+ node_vector reachable_nodes(iterator from, arc_select_fn = 0)\r
+ throw(wrong_object,null_dereference,end_dereference);\r
+\r
+ // get the set of all nodes that can reach to to by any path\r
+ const_node_vector reaching_nodes(const_iterator to, arc_select_fn = 0) const\r
+ throw(wrong_object,null_dereference,end_dereference);\r
+ node_vector reaching_nodes(iterator to, arc_select_fn = 0)\r
+ throw(wrong_object,null_dereference,end_dereference);\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // Unweighted Shortest path algorithms\r
+\r
+ // find the shortest path from from to to\r
+ // This is an unweighted shortest path algorithm, i.e. the weight of each\r
+ // arc is assumed to be 1, so just counts the number of arcs\r
+ // if there is more than one shortest path it returns the first one\r
+ // If there are no paths, returns an empty path\r
+ const_arc_vector shortest_path(const_iterator from, const_iterator to, arc_select_fn = 0) const\r
+ throw(wrong_object,null_dereference,end_dereference);\r
+ arc_vector shortest_path(iterator from, iterator to, arc_select_fn = 0)\r
+ throw(wrong_object,null_dereference,end_dereference);\r
+\r
+ // find the set of shortest paths from from to any other node in the graph\r
+ // that is reachable (i.e. for which path_exists() is true)\r
+ // This is an unweighted shortest path, so just counts the number of arcs\r
+ // if there is more than one shortest path to a node it returns the first one\r
+ // If there are no paths, returns an empty list\r
+ const_path_vector shortest_paths(const_iterator from, arc_select_fn = 0) const\r
+ throw(wrong_object,null_dereference,end_dereference);\r
+ path_vector shortest_paths(iterator from, arc_select_fn = 0)\r
+ throw(wrong_object,null_dereference,end_dereference);\r
+\r
+ private:\r
+ friend class digraph_iterator<NT,AT,NT&,NT*>;\r
+ friend class digraph_iterator<NT,AT,const NT&,const NT*>;\r
+ friend class digraph_arc_iterator<NT,AT,AT&,AT*>;\r
+ friend class digraph_arc_iterator<NT,AT,const AT&, const AT*>;\r
+\r
+ typedef std::set<const_iterator> const_iterator_set;\r
+ typedef TYPENAME const_iterator_set::iterator const_iterator_set_iterator;\r
+\r
+ bool path_exists_r(const_iterator from, const_iterator to, const_iterator_set& visited, arc_select_fn) const\r
+ throw(wrong_object,null_dereference,end_dereference);\r
+\r
+ void all_paths_r(const_iterator from, const_iterator to, const_arc_vector& so_far, const_path_vector& result, arc_select_fn) const\r
+ throw(wrong_object,null_dereference,end_dereference);\r
+\r
+ void reachable_nodes_r(const_iterator from, const_iterator_set& visited, arc_select_fn) const\r
+ throw(wrong_object,null_dereference,end_dereference);\r
+\r
+ void reaching_nodes_r(const_iterator to, const_iterator_set& visited, arc_select_fn) const\r
+ throw(wrong_object,null_dereference,end_dereference);\r
+\r
+ digraph_node<NT,AT>* m_nodes_begin;\r
+ digraph_node<NT,AT>* m_nodes_end;\r
+ digraph_arc<NT,AT>* m_arcs_begin;\r
+ digraph_arc<NT,AT>* m_arcs_end;\r
+ };\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
+\r
+#include "digraph.tpp"\r
+#endif\r
-////////////////////////////////////////////////////////////////////////////////
-
-// Author: Andy Rushton
-// Copyright: (c) Southampton University 1999-2004
-// (c) Andy Rushton 2004-2009
-// License: BSD License, see ../docs/license.html
-
-// Note: I tried to write this using STL lists for the node and arc lists, but
-// it got far too hairy. The specific problem is that I wanted a digraph
-// iterator to contain a list::iterator so I needed to be able to generate a
-// list::iterator from a node or arc and STL list iterators don't give you that
-// functionality. I tried burgling the data structures, but that was
-// non-portable between different STL implementations so needed lots of #ifdefs
-// and so was mind-bogglingly awful and unreadable - in other words a
-// maintenance nightmare. I gave up and impemented my own lists - not difficult.
-
-// I use circular double-linked lists. The circular design means that both
-// ends of the list are equally accessible in unit time. An empty list
-// contains no objects. There is no end node in the list - unlike the STL
-// lists which have a dummy node for end iterators to point to -
-// conceptually the end iterator points one element beyond the end of the
-// list. However, I implement the end iterator concept in the iterator
-// itself, so do not need the dummy end node.
-
-////////////////////////////////////////////////////////////////////////////////
-#include <algorithm>
-#include <deque>
-
-////////////////////////////////////////////////////////////////////////////////
-// Internals
-
-namespace stlplus
-{
-
- template<typename NT, typename AT>
- class digraph_node
- {
- public:
- master_iterator<digraph<NT,AT>, digraph_node<NT,AT> > m_master;
- NT m_data;
- digraph_node<NT,AT>* m_prev;
- digraph_node<NT,AT>* m_next;
- std::vector<digraph_arc<NT,AT>*> m_inputs;
- std::vector<digraph_arc<NT,AT>*> m_outputs;
- public:
- digraph_node(const digraph<NT,AT>* owner, const NT& d = NT()) :
- m_master(owner,this), m_data(d), m_prev(0), m_next(0)
- {
- }
- ~digraph_node(void)
- {
- }
- };
-
- template<typename NT, typename AT>
- class digraph_arc
- {
- public:
- master_iterator<digraph<NT,AT>, digraph_arc<NT,AT> > m_master;
- AT m_data;
- digraph_arc<NT,AT>* m_prev;
- digraph_arc<NT,AT>* m_next;
- digraph_node<NT,AT>* m_from;
- digraph_node<NT,AT>* m_to;
- digraph_arc(const digraph<NT,AT>* owner, digraph_node<NT,AT>* from = 0, digraph_node<NT,AT>* to = 0, const AT& d = AT()) :
- m_master(owner,this), m_data(d), m_prev(0), m_next(0), m_from(from), m_to(to)
- {
- }
- };
-
- ////////////////////////////////////////////////////////////////////////////////
- // Iterators
- ////////////////////////////////////////////////////////////////////////////////
-
- ////////////////////////////////////////////////////////////////////////////////
- // Node iterator
-
- // construct a null iterator
- template<typename NT, typename AT, typename NRef, typename NPtr>
- digraph_iterator<NT,AT,NRef,NPtr>::digraph_iterator(void)
- {
- }
-
- // valid iterator
- template<typename NT, typename AT, typename NRef, typename NPtr>
- digraph_iterator<NT,AT,NRef,NPtr>::digraph_iterator(digraph_node<NT,AT>* node) :
- safe_iterator<digraph<NT,AT>,digraph_node<NT,AT> >(node->m_master)
- {
- }
-
- // end iterator
- template<typename NT, typename AT, typename NRef, typename NPtr>
- digraph_iterator<NT,AT,NRef,NPtr>::digraph_iterator(const digraph<NT,AT>* owner) :
- safe_iterator<digraph<NT,AT>,digraph_node<NT,AT> >(owner)
- {
- }
-
- // alias an iterator
- template<typename NT, typename AT, typename NRef, typename NPtr>
- digraph_iterator<NT,AT,NRef,NPtr>::digraph_iterator(const safe_iterator<digraph<NT,AT>, digraph_node<NT,AT> >& iterator) :
- safe_iterator<digraph<NT,AT>,digraph_node<NT,AT> >(iterator)
- {
- }
-
- // destructor
- template<typename NT, typename AT, typename NRef, typename NPtr>
- digraph_iterator<NT,AT,NRef,NPtr>::~digraph_iterator(void)
- {
- }
-
- template<typename NT, typename AT, typename NRef, typename NPtr>
- TYPENAME digraph_iterator<NT,AT,NRef,NPtr>::const_iterator digraph_iterator<NT,AT,NRef,NPtr>::constify (void) const
- {
- return digraph_iterator<NT,AT,const NT&,const NT*>(*this);
- }
-
- template<typename NT, typename AT, typename NRef, typename NPtr>
- TYPENAME digraph_iterator<NT,AT,NRef,NPtr>::iterator digraph_iterator<NT,AT,NRef,NPtr>::deconstify (void) const
- {
- return digraph_iterator<NT,AT,NT&,NT*>(*this);
- }
-
- template<typename NT, typename AT, typename NRef, typename NPtr>
- TYPENAME digraph_iterator<NT,AT,NRef,NPtr>::this_iterator& digraph_iterator<NT,AT,NRef,NPtr>::operator ++ (void)
- throw(null_dereference,end_dereference)
- {
- this->assert_valid();
- if (this->node()->m_next)
- this->set(this->node()->m_next->m_master);
- else
- this->set_end();
- return *this;
- }
-
- template<typename NT, typename AT, typename NRef, typename NPtr>
- TYPENAME digraph_iterator<NT,AT,NRef,NPtr>::this_iterator digraph_iterator<NT,AT,NRef,NPtr>::operator ++ (int)
- throw(null_dereference,end_dereference)
- {
- // post-increment is defined in terms of the pre-increment
- digraph_iterator<NT,AT,NRef,NPtr> result(*this);
- ++(*this);
- return result;
- }
-
- template<typename NT, typename AT, typename NRef, typename NPtr>
- TYPENAME digraph_iterator<NT,AT,NRef,NPtr>::this_iterator& digraph_iterator<NT,AT,NRef,NPtr>::operator -- (void)
- throw(null_dereference,end_dereference)
- {
- this->assert_valid();
- if (this->node()->m_prev)
- this->set(this->node()->m_prev->m_master);
- else
- this->set_end();
- return *this;
- }
-
- template<typename NT, typename AT, typename NRef, typename NPtr>
- TYPENAME digraph_iterator<NT,AT,NRef,NPtr>::this_iterator digraph_iterator<NT,AT,NRef,NPtr>::operator -- (int)
- throw(null_dereference,end_dereference)
- {
- // post-decrement is defined in terms of the pre-decrement
- digraph_iterator<NT,AT,NRef,NPtr> result(*this);
- --(*this);
- return result;
- }
-
- template<typename NT, typename AT, typename NRef, typename NPtr>
- bool digraph_iterator<NT,AT,NRef,NPtr>::operator == (const TYPENAME digraph_iterator<NT,AT,NRef,NPtr>::this_iterator& r) const
- {
- return equal(r);
- }
-
- template<typename NT, typename AT, typename NRef, typename NPtr>
- bool digraph_iterator<NT,AT,NRef,NPtr>::operator != (const TYPENAME digraph_iterator<NT,AT,NRef,NPtr>::this_iterator& r) const
- {
- return !operator==(r);
- }
-
- template<typename NT, typename AT, typename NRef, typename NPtr>
- bool digraph_iterator<NT,AT,NRef,NPtr>::operator < (const TYPENAME digraph_iterator<NT,AT,NRef,NPtr>::this_iterator& r) const
- {
- return compare(r) < 0;
- }
-
- template<typename NT, typename AT, typename NRef, typename NPtr>
- TYPENAME digraph_iterator<NT,AT,NRef,NPtr>::reference digraph_iterator<NT,AT,NRef,NPtr>::operator*(void) const
- throw(null_dereference,end_dereference)
- {
- this->assert_valid();
- return this->node()->m_data;
- }
-
- template<typename NT, typename AT, typename NRef, typename NPtr>
- TYPENAME digraph_iterator<NT,AT,NRef,NPtr>::pointer digraph_iterator<NT,AT,NRef,NPtr>::operator->(void) const
- throw(null_dereference,end_dereference)
- {
- return &(operator*());
- }
-
- ////////////////////////////////////////////////////////////////////////////////
- // Arc Iterator
-
- template<typename NT, typename AT, typename ARef, typename APtr>
- digraph_arc_iterator<NT,AT,ARef,APtr>::digraph_arc_iterator(void)
- {
- }
-
- // valid iterator
- template<typename NT, typename AT, typename NRef, typename NPtr>
- digraph_arc_iterator<NT,AT,NRef,NPtr>::digraph_arc_iterator(digraph_arc<NT,AT>* arc) :
- safe_iterator<digraph<NT,AT>,digraph_arc<NT,AT> >(arc->m_master)
- {
- }
-
- // end iterator
- template<typename NT, typename AT, typename NRef, typename NPtr>
- digraph_arc_iterator<NT,AT,NRef,NPtr>::digraph_arc_iterator(const digraph<NT,AT>* owner) :
- safe_iterator<digraph<NT,AT>,digraph_arc<NT,AT> >(owner)
- {
- }
-
- // alias an iterator
- template<typename NT, typename AT, typename NRef, typename NPtr>
- digraph_arc_iterator<NT,AT,NRef,NPtr>::digraph_arc_iterator(const safe_iterator<digraph<NT,AT>, digraph_arc<NT,AT> >& iterator) :
- safe_iterator<digraph<NT,AT>,digraph_arc<NT,AT> >(iterator)
- {
- }
-
- template<typename NT, typename AT, typename ARef, typename APtr>
- digraph_arc_iterator<NT,AT,ARef,APtr>::~digraph_arc_iterator(void)
- {
- }
-
- template<typename NT, typename AT, typename NRef, typename NPtr>
- TYPENAME digraph_arc_iterator<NT,AT,NRef,NPtr>::const_iterator digraph_arc_iterator<NT,AT,NRef,NPtr>::constify (void) const
- {
- return digraph_arc_iterator<NT,AT,const AT&,const AT*>(*this);
- }
-
- template<typename NT, typename AT, typename NRef, typename NPtr>
- TYPENAME digraph_arc_iterator<NT,AT,NRef,NPtr>::iterator digraph_arc_iterator<NT,AT,NRef,NPtr>::deconstify (void) const
- {
- return digraph_arc_iterator<NT,AT,AT&,AT*>(*this);
- }
-
- template<typename NT, typename AT, typename ARef, typename APtr>
- TYPENAME digraph_arc_iterator<NT,AT,ARef,APtr>::this_iterator& digraph_arc_iterator<NT,AT,ARef,APtr>::operator ++ (void)
- throw(null_dereference,end_dereference)
- {
- this->assert_valid();
- if (this->node()->m_next)
- this->set(this->node()->m_next->m_master);
- else
- this->set_end();
- return *this;
- }
-
- template<typename NT, typename AT, typename ARef, typename APtr>
- TYPENAME digraph_arc_iterator<NT,AT,ARef,APtr>::this_iterator digraph_arc_iterator<NT,AT,ARef,APtr>::operator ++ (int)
- throw(null_dereference,end_dereference)
- {
- // post-increment is defined in terms of the pre-increment
- digraph_arc_iterator<NT,AT,ARef,APtr> result(*this);
- ++(*this);
- return result;
- }
-
- template<typename NT, typename AT, typename ARef, typename APtr>
- TYPENAME digraph_arc_iterator<NT,AT,ARef,APtr>::this_iterator& digraph_arc_iterator<NT,AT,ARef,APtr>::operator -- (void)
- throw(null_dereference,end_dereference)
- {
- this->assert_valid();
- if (this->node()->m_prev)
- this->set(this->node()->m_prev->m_master);
- else
- this->set_end();
- return *this;
- }
-
- template<typename NT, typename AT, typename ARef, typename APtr>
- TYPENAME digraph_arc_iterator<NT,AT,ARef,APtr>::this_iterator digraph_arc_iterator<NT,AT,ARef,APtr>::operator -- (int)
- throw(null_dereference,end_dereference)
- {
- // post-decrement is defined in terms of the pre-decrement
- digraph_arc_iterator<NT,AT,ARef,APtr> result(*this);
- --(*this);
- return result;
- }
-
- template<typename NT, typename AT, typename ARef, typename APtr>
- bool digraph_arc_iterator<NT,AT,ARef,APtr>::operator == (const TYPENAME digraph_arc_iterator<NT,AT,ARef,APtr>::this_iterator& r) const
- {
- return equal(r);
- }
-
- template<typename NT, typename AT, typename ARef, typename APtr>
- bool digraph_arc_iterator<NT,AT,ARef,APtr>::operator != (const TYPENAME digraph_arc_iterator<NT,AT,ARef,APtr>::this_iterator& r) const
- {
- return !operator==(r);
- }
-
- template<typename NT, typename AT, typename ARef, typename APtr>
- bool digraph_arc_iterator<NT,AT,ARef,APtr>::operator < (const TYPENAME digraph_arc_iterator<NT,AT,ARef,APtr>::this_iterator& r) const
- {
- return compare(r) < 0;
- }
-
- template<typename NT, typename AT, typename ARef, typename APtr>
- TYPENAME digraph_arc_iterator<NT,AT,ARef,APtr>::reference digraph_arc_iterator<NT,AT,ARef,APtr>::operator*(void) const
- throw(null_dereference,end_dereference)
- {
- this->assert_valid();
- return this->node()->m_data;
- }
-
- template<typename NT, typename AT, typename ARef, typename APtr>
- TYPENAME digraph_arc_iterator<NT,AT,ARef,APtr>::pointer digraph_arc_iterator<NT,AT,ARef,APtr>::operator->(void) const
- throw(null_dereference,end_dereference)
- {
- return &(operator*());
- }
-
- ////////////////////////////////////////////////////////////////////////////////
- // subtype utilities
-
- template<typename NT, typename AT>
- TYPENAME digraph<NT,AT>::const_arc_vector digraph<NT,AT>::constify_arcs(const TYPENAME digraph<NT,AT>::arc_vector& arcs) const
- throw(wrong_object,null_dereference,end_dereference)
- {
- std::vector<digraph_arc_iterator<NT,AT,const AT&,const AT*> > result;
- for (unsigned i = 0; i < arcs.size(); i++)
- {
- arcs[i].assert_valid(this);
- result.push_back(arcs[i].constify());
- }
- return result;
- }
-
- template<typename NT, typename AT>
- TYPENAME digraph<NT,AT>::arc_vector digraph<NT,AT>::deconstify_arcs(const TYPENAME digraph<NT,AT>::const_arc_vector& arcs) const
- throw(wrong_object,null_dereference,end_dereference)
- {
- std::vector<digraph_arc_iterator<NT,AT,AT&,AT*> > result;
- for (unsigned i = 0; i < arcs.size(); i++)
- {
- arcs[i].assert_valid(this);
- result.push_back(arcs[i].deconstify());
- }
- return result;
- }
-
- template<typename NT, typename AT>
- TYPENAME digraph<NT,AT>::const_path_vector digraph<NT,AT>::constify_paths(const TYPENAME digraph<NT,AT>::path_vector& paths) const
- throw(wrong_object,null_dereference,end_dereference)
- {
- std::vector<std::vector<digraph_arc_iterator<NT,AT,const AT&,const AT*> > > result;
- for (unsigned i = 0; i < paths.size(); i++)
- result.push_back(constify_arcs(paths[i]));
- return result;
- }
-
- template<typename NT, typename AT>
- TYPENAME digraph<NT,AT>::path_vector digraph<NT,AT>::deconstify_paths(const TYPENAME digraph<NT,AT>::const_path_vector& paths) const
- throw(wrong_object,null_dereference,end_dereference)
- {
- std::vector<std::vector<digraph_arc_iterator<NT,AT,AT&,AT*> > > result;
- for (unsigned i = 0; i < paths.size(); i++)
- result.push_back(deconstify_arcs(paths[i]));
- return result;
- }
-
- template<typename NT, typename AT>
- TYPENAME digraph<NT,AT>::const_node_vector digraph<NT,AT>::constify_nodes(const TYPENAME digraph<NT,AT>::node_vector& nodes) const
- throw(wrong_object,null_dereference,end_dereference)
- {
- std::vector<digraph_iterator<NT,AT,const NT&,const NT*> > result;
- for (unsigned i = 0; i < nodes.size(); i++)
- {
- nodes[i].assert_valid(this);
- result.push_back(nodes[i].constify());
- }
- return result;
- }
-
- template<typename NT, typename AT>
- TYPENAME digraph<NT,AT>::node_vector digraph<NT,AT>::deconstify_nodes(const TYPENAME digraph<NT,AT>::const_node_vector& nodes) const
- throw(wrong_object,null_dereference,end_dereference)
- {
- std::vector<digraph_iterator<NT,AT,NT&,NT*> > result;
- for (unsigned i = 0; i < nodes.size(); i++)
- {
- nodes[i].assert_valid(this);
- result.push_back(nodes[i].deconstify());
- }
- return result;
- }
-
- template<typename NT, typename AT>
- unsigned digraph<NT,AT>::npos(void)
- {
- return(unsigned)-1;
- }
-
- ////////////////////////////////////////////////////////////////////////////////
- // Constructors etc.
-
- template<typename NT, typename AT>
- digraph<NT,AT>::digraph(void) :
- m_nodes_begin(0), m_nodes_end(0), m_arcs_begin(0), m_arcs_end(0)
- {
- // node and arc lists are circular double-linked lists
- // they start out empty (no dummy end node)
- }
-
- template<typename NT, typename AT>
- digraph<NT,AT>::~digraph(void)
- {
- clear();
- }
-
- template<typename NT, typename AT>
- digraph<NT,AT>::digraph(const digraph<NT,AT>& r) :
- m_nodes_begin(0), m_nodes_end(0), m_arcs_begin(0), m_arcs_end(0)
- {
- *this = r;
- }
-
- template<typename NT, typename AT>
- digraph<NT,AT>& digraph<NT,AT>::operator=(const digraph<NT,AT>& r)
- {
- // make it self-copy safe i.e. a=a; is a valid instruction
- if (this == &r) return *this;
- clear();
- // first phase is to copy the nodes, creating a map of cross references from the old nodes to their new equivalents
- std::map<digraph_iterator<NT,AT,const NT&,const NT*>, digraph_iterator<NT,AT,NT&,NT*> > xref;
- for (digraph_iterator<NT,AT,const NT&,const NT*> n = r.begin(); n != r.end(); n++)
- xref[n] = insert(*n);
- // second phase is to copy the arcs, using the map to convert the old to and from nodes to the new nodes
- for (digraph_arc_iterator<NT,AT, const AT&,const AT*> a = r.arc_begin(); a != r.arc_end(); a++)
- arc_insert(xref[r.arc_from(a)],xref[r.arc_to(a)],*a);
- return *this;
- }
-
- ////////////////////////////////////////////////////////////////////////////////
- // Basic Node functions
-
- template<typename NT, typename AT>
- bool digraph<NT,AT>::empty(void) const
- {
- return m_nodes_begin == 0;
- }
-
- template<typename NT, typename AT>
- unsigned digraph<NT,AT>::size(void) const
- {
- unsigned count = 0;
- for (digraph_iterator<NT,AT,const NT&,const NT*> i = begin(); i != end(); i++)
- count++;
- return count;
- }
-
- template<typename NT, typename AT>
- TYPENAME digraph<NT,AT>::iterator digraph<NT,AT>::insert(const NT& node_data)
- {
- digraph_node<NT,AT>* new_node = new digraph_node<NT,AT>(this,node_data);
- if (!m_nodes_end)
- {
- // insert into an empty list
- m_nodes_begin = new_node;
- m_nodes_end = new_node;
- }
- else
- {
- // insert at the end of the list
- new_node->m_prev = m_nodes_end;
- m_nodes_end->m_next = new_node;
- m_nodes_end = new_node;
- }
- return digraph_iterator<NT,AT,NT&,NT*>(new_node);
- }
-
- template<typename NT, typename AT>
- TYPENAME digraph<NT,AT>::iterator digraph<NT,AT>::erase(TYPENAME digraph<NT,AT>::iterator iter)
- throw(wrong_object,null_dereference,end_dereference)
- {
- iter.assert_valid(this);
- // remove all arcs connected to this node first
- // use arc_erase rather than arcs.erase because that tidies up the node at the other end of the arc too
- for (unsigned i = fanin(iter); i--; )
- arc_erase(input(iter,i));
- for (unsigned j = fanout(iter); j--; )
- arc_erase(output(iter,j));
- // now unlink the node from the list and delete it
- if (iter.node()->m_next)
- iter.node()->m_next->m_prev = iter.node()->m_prev;
- if (iter.node()->m_prev)
- iter.node()->m_prev->m_next = iter.node()->m_next;
- digraph_node<NT,AT>* next = iter.node()->m_next;
- delete iter.node();
- // return the next node in the list
- if (next)
- return digraph_iterator<NT,AT,NT&,NT*>(next);
- else
- return digraph_iterator<NT,AT,NT&,NT*>(this);
- }
-
- template<typename NT, typename AT>
- void digraph<NT,AT>::clear(void)
- {
- // clearing the nodes also clears the arcs
- for (digraph_iterator<NT,AT,NT&,NT*> i = begin(); i != end(); )
- i = erase(i);
- }
-
- template<typename NT, typename AT>
- TYPENAME digraph<NT,AT>::const_iterator digraph<NT,AT>::begin(void) const
- {
- if (m_nodes_begin)
- return digraph_iterator<NT,AT,const NT&,const NT*>(m_nodes_begin);
- else
- return digraph_iterator<NT,AT,const NT&,const NT*>(this);
- }
-
- template<typename NT, typename AT>
- TYPENAME digraph<NT,AT>::iterator digraph<NT,AT>::begin(void)
- {
- if (m_nodes_begin)
- return digraph_iterator<NT,AT,NT&,NT*>(m_nodes_begin);
- else
- return digraph_iterator<NT,AT,NT&,NT*>(this);
- }
-
- template<typename NT, typename AT>
- TYPENAME digraph<NT,AT>::const_iterator digraph<NT,AT>::end(void) const
- {
- return digraph_iterator<NT,AT,const NT&,const NT*>(this);
- }
-
- template<typename NT, typename AT>
- TYPENAME digraph<NT,AT>::iterator digraph<NT,AT>::end(void)
- {
- return digraph_iterator<NT,AT,NT&,NT*>(this);
- }
-
- template<typename NT, typename AT>
- unsigned digraph<NT,AT>::fanin(TYPENAME digraph<NT,AT>::const_iterator iter) const
- throw(wrong_object,null_dereference,end_dereference)
- {
- iter.assert_valid(this);
- return iter.node()->m_inputs.size();
- }
-
- template<typename NT, typename AT>
- unsigned digraph<NT,AT>::fanin(TYPENAME digraph<NT,AT>::iterator iter)
- throw(wrong_object,null_dereference,end_dereference)
- {
- iter.assert_valid(this);
- return iter.node()->m_inputs.size();
- }
-
- template<typename NT, typename AT>
- TYPENAME digraph<NT,AT>::const_arc_iterator digraph<NT,AT>::input(TYPENAME digraph<NT,AT>::const_iterator iter, unsigned i) const
- throw(wrong_object,null_dereference,end_dereference,std::out_of_range)
- {
- iter.assert_valid(this);
- if (i >= iter.node()->m_inputs.size()) throw std::out_of_range("digraph::input");
- return digraph_arc_iterator<NT,AT, const AT&,const AT*>(iter.node()->m_inputs[i]);
- }
-
- template<typename NT, typename AT>
- TYPENAME digraph<NT,AT>::arc_iterator digraph<NT,AT>::input(TYPENAME digraph<NT,AT>::iterator iter, unsigned i)
- throw(wrong_object,null_dereference,end_dereference,std::out_of_range)
- {
- iter.assert_valid(this);
- if (i >= iter.node()->m_inputs.size()) throw std::out_of_range("digraph::input");
- return digraph_arc_iterator<NT,AT,AT&,AT*>(iter.node()->m_inputs[i]);
- }
-
- template<typename NT, typename AT>
- unsigned digraph<NT,AT>::fanout(TYPENAME digraph<NT,AT>::const_iterator iter) const
- throw(wrong_object,null_dereference,end_dereference)
- {
- iter.assert_valid(this);
- return iter.node()->m_outputs.size();
- }
-
- template<typename NT, typename AT>
- unsigned digraph<NT,AT>::fanout(TYPENAME digraph<NT,AT>::iterator iter)
- throw(wrong_object,null_dereference,end_dereference)
- {
- iter.assert_valid(this);
- return iter.node()->m_outputs.size();
- }
-
- template<typename NT, typename AT>
- TYPENAME digraph<NT,AT>::const_arc_iterator digraph<NT,AT>::output(TYPENAME digraph<NT,AT>::const_iterator iter, unsigned i) const
- throw(wrong_object,null_dereference,end_dereference,std::out_of_range)
- {
- iter.assert_valid(this);
- if (i >= iter.node()->m_outputs.size()) throw std::out_of_range("digraph::output");
- return digraph_arc_iterator<NT,AT, const AT&,const AT*>(iter.node()->m_outputs[i]);
- }
-
- template<typename NT, typename AT>
- TYPENAME digraph<NT,AT>::arc_iterator digraph<NT,AT>::output(TYPENAME digraph<NT,AT>::iterator iter, unsigned i)
- throw(wrong_object,null_dereference,end_dereference,std::out_of_range)
- {
- iter.assert_valid(this);
- if (i >= iter.node()->m_outputs.size()) throw std::out_of_range("digraph::output");
- return digraph_arc_iterator<NT,AT,AT&,AT*>(iter.node()->m_outputs[i]);
- }
-
- template<typename NT, typename AT>
- TYPENAME digraph<NT,AT>::const_arc_vector digraph<NT,AT>::inputs(TYPENAME digraph<NT,AT>::const_iterator node) const
- throw(wrong_object,null_dereference,end_dereference)
- {
- node.assert_valid(this);
- std::vector<digraph_arc_iterator<NT,AT,const AT&, const AT*> > result;
- for (unsigned i = 0; i < fanin(node); i++)
- result.push_back(input(node,i));
- return result;
- }
-
- template<typename NT, typename AT>
- TYPENAME digraph<NT,AT>::arc_vector digraph<NT,AT>::inputs(TYPENAME digraph<NT,AT>::iterator node)
- throw(wrong_object,null_dereference,end_dereference)
- {
- node.assert_valid(this);
- std::vector<digraph_arc_iterator<NT,AT,AT&,AT*> > result;
- for (unsigned i = 0; i < fanin(node); i++)
- result.push_back(input(node,i));
- return result;
- }
-
- template<typename NT, typename AT>
- TYPENAME digraph<NT,AT>::const_arc_vector digraph<NT,AT>::outputs(TYPENAME digraph<NT,AT>::const_iterator node) const
- throw(wrong_object,null_dereference,end_dereference)
- {
- node.assert_valid(this);
- std::vector<digraph_arc_iterator<NT,AT,const AT&, const AT*> > result;
- for (unsigned i = 0; i < fanout(node); i++)
- result.push_back(output(node,i));
- return result;
- }
-
- template<typename NT, typename AT>
- TYPENAME digraph<NT,AT>::arc_vector digraph<NT,AT>::outputs(TYPENAME digraph<NT,AT>::iterator node)
- throw(wrong_object,null_dereference,end_dereference)
- {
- node.assert_valid(this);
- std::vector<digraph_arc_iterator<NT,AT,AT&,AT*> > result;
- for (unsigned i = 0; i < fanout(node); i++)
- result.push_back(output(node,i));
- return result;
- }
-
- template<typename NT, typename AT>
- unsigned digraph<NT,AT>::output_offset(TYPENAME digraph<NT,AT>::const_iterator from,
- TYPENAME digraph<NT,AT>::const_arc_iterator arc) const
- throw(wrong_object,null_dereference,end_dereference)
- {
- from.assert_valid(this);
- arc.assert_valid(this);
- for (unsigned i = 0; i < fanout(from); i++)
- {
- if (output(from,i) == arc)
- return i;
- }
- return digraph<NT,AT>::npos();
- }
-
- template<typename NT, typename AT>
- unsigned digraph<NT,AT>::output_offset(TYPENAME digraph<NT,AT>::iterator from,
- TYPENAME digraph<NT,AT>::arc_iterator arc)
- throw(wrong_object,null_dereference,end_dereference)
- {
- from.assert_valid(this);
- arc.assert_valid(this);
- for (unsigned i = 0; i < fanout(from); i++)
- {
- if (output(from,i) == arc)
- return i;
- }
- return digraph<NT,AT>::npos();
- }
-
- template<typename NT, typename AT>
- unsigned digraph<NT,AT>::input_offset(TYPENAME digraph<NT,AT>::const_iterator to,
- TYPENAME digraph<NT,AT>::const_arc_iterator arc) const
- throw(wrong_object,null_dereference,end_dereference)
- {
- to.assert_valid(this);
- arc.assert_valid(this);
- for (unsigned i = 0; i < fanin(to); i++)
- {
- if (input(to,i) == arc)
- return i;
- }
- return digraph<NT,AT>::npos();
- }
-
- template<typename NT, typename AT>
- unsigned digraph<NT,AT>::input_offset(TYPENAME digraph<NT,AT>::iterator to,
- TYPENAME digraph<NT,AT>::arc_iterator arc)
- throw(wrong_object,null_dereference,end_dereference)
- {
- to.assert_valid(this);
- arc.assert_valid(this);
- for (unsigned i = 0; i < fanin(to); i++)
- {
- if (input(to,i) == arc)
- return i;
- }
- return digraph<NT,AT>::npos();
- }
-
- ////////////////////////////////////////////////////////////////////////////////
- // Basic Arc functions
-
- template<typename NT, typename AT>
- bool digraph<NT,AT>::arc_empty(void) const
- {
- return m_arcs_end == 0;
- }
-
- template<typename NT, typename AT>
- unsigned digraph<NT,AT>::arc_size(void) const
- {
- unsigned count = 0;
- for (digraph_arc_iterator<NT,AT, const AT&,const AT*> i = arc_begin(); i != arc_end(); i++)
- count++;
- return count;
- }
-
- template<typename NT, typename AT>
- TYPENAME digraph<NT,AT>::arc_iterator digraph<NT,AT>::arc_insert(TYPENAME digraph<NT,AT>::iterator from,
- TYPENAME digraph<NT,AT>::iterator to,
- const AT& arc_data)
- throw(wrong_object,null_dereference,end_dereference)
- {
- from.assert_valid(this);
- to.assert_valid(this);
- // create the new arc and link it in to the arc list
- digraph_arc<NT,AT>* new_arc = new digraph_arc<NT,AT>(this, from.node(), to.node(), arc_data);
- if (!m_arcs_end)
- {
- // insert into an empty list
- m_arcs_begin = new_arc;
- m_arcs_end = new_arc;
- }
- else
- {
- // insert at the end of the list
- new_arc->m_prev = m_arcs_end;
- m_arcs_end->m_next = new_arc;
- m_arcs_end = new_arc;
- }
- // add this arc to the inputs and outputs of the end nodes
- from.node()->m_outputs.push_back(new_arc);
- to.node()->m_inputs.push_back(new_arc);
- return digraph_arc_iterator<NT,AT,AT&,AT*>(new_arc);
- }
-
- template<typename NT, typename AT>
- TYPENAME digraph<NT,AT>::arc_iterator digraph<NT,AT>::arc_erase(TYPENAME digraph<NT,AT>::arc_iterator iter)
- throw(wrong_object,null_dereference,end_dereference)
- {
- iter.assert_valid(this);
- // first remove this arc's pointers from the from/to nodes
- for (TYPENAME std::vector<digraph_arc<NT,AT>*>::iterator i = iter.node()->m_to->m_inputs.begin(); i != iter.node()->m_to->m_inputs.end(); )
- {
- if (*i == iter.node())
- i = iter.node()->m_to->m_inputs.erase(i);
- else
- i++;
- }
- for (TYPENAME std::vector<digraph_arc<NT,AT>*>::iterator o = iter.node()->m_from->m_outputs.begin(); o != iter.node()->m_from->m_outputs.end(); )
- {
- if (*o == iter.node())
- o = iter.node()->m_from->m_outputs.erase(o);
- else
- o++;
- }
- // now unlink the arc from the list and delete it
- if (iter.node()->m_next)
- iter.node()->m_next->m_prev = iter.node()->m_prev;
- if (iter.node()->m_prev)
- iter.node()->m_prev->m_next = iter.node()->m_next;
- digraph_arc<NT,AT>* next = iter.node()->m_next;
- delete iter.node();
- if (next)
- return digraph_arc_iterator<NT,AT,AT&,AT*>(next);
- else
- return digraph_arc_iterator<NT,AT,AT&,AT*>(this);
- }
-
- template<typename NT, typename AT>
- void digraph<NT,AT>::arc_clear(void)
- {
- for (digraph_arc_iterator<NT,AT,AT&,AT*> a = arc_begin(); a != arc_end(); )
- a = arc_erase(a);
- }
-
- template<typename NT, typename AT>
- TYPENAME digraph<NT,AT>::const_arc_iterator digraph<NT,AT>::arc_begin(void) const
- {
- if (m_arcs_begin)
- return digraph_arc_iterator<NT,AT, const AT&,const AT*>(m_arcs_begin);
- else
- return digraph_arc_iterator<NT,AT, const AT&,const AT*>(this);
- }
-
- template<typename NT, typename AT>
- TYPENAME digraph<NT,AT>::arc_iterator digraph<NT,AT>::arc_begin(void)
- {
- if (m_arcs_begin)
- return digraph_arc_iterator<NT,AT,AT&,AT*>(m_arcs_begin);
- else
- return digraph_arc_iterator<NT,AT,AT&,AT*>(this);
- }
-
- template<typename NT, typename AT>
- TYPENAME digraph<NT,AT>::const_arc_iterator digraph<NT,AT>::arc_end(void) const
- {
- return digraph_arc_iterator<NT,AT, const AT&,const AT*>(this);
- }
-
- template<typename NT, typename AT>
- TYPENAME digraph<NT,AT>::arc_iterator digraph<NT,AT>::arc_end(void)
- {
- return digraph_arc_iterator<NT,AT,AT&,AT*>(this);
- }
-
- template<typename NT, typename AT>
- TYPENAME digraph<NT,AT>::const_iterator digraph<NT,AT>::arc_from(TYPENAME digraph<NT,AT>::const_arc_iterator iter) const
- throw(wrong_object,null_dereference,end_dereference)
- {
- iter.assert_valid(this);
- return digraph_iterator<NT,AT,const NT&,const NT*>(iter.node()->m_from);
- }
-
- template<typename NT, typename AT>
- TYPENAME digraph<NT,AT>::iterator digraph<NT,AT>::arc_from(TYPENAME digraph<NT,AT>::arc_iterator iter)
- throw(wrong_object,null_dereference,end_dereference)
- {
- iter.assert_valid(this);
- return digraph_iterator<NT,AT,NT&,NT*>(iter.node()->m_from);
- }
-
- template<typename NT, typename AT>
- TYPENAME digraph<NT,AT>::const_iterator digraph<NT,AT>::arc_to(TYPENAME digraph<NT,AT>::const_arc_iterator iter) const
- throw(wrong_object,null_dereference,end_dereference)
- {
- iter.assert_valid(this);
- return digraph_iterator<NT,AT,const NT&,const NT*>(iter.node()->m_to);
- }
-
- template<typename NT, typename AT>
- TYPENAME digraph<NT,AT>::iterator digraph<NT,AT>::arc_to(TYPENAME digraph<NT,AT>::arc_iterator iter)
- throw(wrong_object,null_dereference,end_dereference)
- {
- iter.assert_valid(this);
- return digraph_iterator<NT,AT,NT&,NT*>(iter.node()->m_to);
- }
-
- template<typename NT, typename AT>
- void digraph<NT,AT>::arc_move(TYPENAME digraph<NT,AT>::arc_iterator arc,
- TYPENAME digraph<NT,AT>::iterator from,
- TYPENAME digraph<NT,AT>::iterator to)
- throw(wrong_object,null_dereference,end_dereference)
- {
- arc_move_to(arc,to);
- arc_move_from(arc,from);
- }
-
- template<typename NT, typename AT>
- void digraph<NT,AT>::arc_move_from(TYPENAME digraph<NT,AT>::arc_iterator arc,
- TYPENAME digraph<NT,AT>::iterator from)
- throw(wrong_object,null_dereference,end_dereference)
- {
- arc.assert_valid(this);
- from.assert_valid(this);
- for (TYPENAME std::vector<digraph_arc<NT,AT>*>::iterator o = arc.node()->m_from->m_outputs.begin(); o != arc.node()->m_from->m_outputs.end(); )
- {
- if (*o == arc.node())
- o = arc.node()->m_from->m_outputs.erase(o);
- else
- o++;
- }
- from.node()->m_outputs.push_back(arc.node());
- arc.node()->m_from = from.node();
- }
-
- template<typename NT, typename AT>
- void digraph<NT,AT>::arc_move_to(TYPENAME digraph<NT,AT>::arc_iterator arc,
- TYPENAME digraph<NT,AT>::iterator to)
- throw(wrong_object,null_dereference,end_dereference)
- {
- arc.assert_valid(this);
- to.assert_valid(this);
- for (TYPENAME std::vector<digraph_arc<NT,AT>*>::iterator i = arc.node()->m_to->m_inputs.begin(); i != arc.node()->m_to->m_inputs.end(); )
- {
- if (*i == arc.node())
- i = arc.node()->m_to->m_inputs.erase(i);
- else
- i++;
- }
- to.node()->m_inputs.push_back(arc.node());
- arc.node()->m_to = to.node();
- }
-
- template<typename NT, typename AT>
- void digraph<NT,AT>::arc_flip(TYPENAME digraph<NT,AT>::arc_iterator arc)
- throw(wrong_object,null_dereference,end_dereference)
- {
- arc_move(arc,arc_to(arc),arc_from(arc));
- }
-
- ////////////////////////////////////////////////////////////////////////////////
- // Adjacency Algorithms
-
- template<typename NT, typename AT>
- bool digraph<NT,AT>::adjacent(TYPENAME digraph<NT,AT>::const_iterator from,
- TYPENAME digraph<NT,AT>::const_iterator to) const
- throw(wrong_object,null_dereference,end_dereference)
- {
- return adjacent_arc(from,to) != arc_end();
- }
-
- template<typename NT, typename AT>
- bool digraph<NT,AT>::adjacent(TYPENAME digraph<NT,AT>::iterator from,
- TYPENAME digraph<NT,AT>::iterator to)
- throw(wrong_object,null_dereference,end_dereference)
- {
- return adjacent_arc(from,to) != arc_end();
- }
-
- template<typename NT, typename AT>
- TYPENAME digraph<NT,AT>::const_arc_iterator digraph<NT,AT>::adjacent_arc(TYPENAME digraph<NT,AT>::const_iterator from,
- TYPENAME digraph<NT,AT>::const_iterator to) const
- throw(wrong_object,null_dereference,end_dereference)
- {
- from.assert_valid(this);
- to.assert_valid(this);
- for (unsigned arc = 0; arc < fanout(from); arc++)
- {
- if (arc_to(output(from, arc)) == to)
- return output(from,arc);
- }
- return arc_end();
- }
-
- template<typename NT, typename AT>
- TYPENAME digraph<NT,AT>::arc_iterator digraph<NT,AT>::adjacent_arc(TYPENAME digraph<NT,AT>::iterator from,
- TYPENAME digraph<NT,AT>::iterator to)
- throw(wrong_object,null_dereference,end_dereference)
- {
- return adjacent_arc(from.constify(), to.constify()).deconstify();
- }
-
- template<typename NT, typename AT>
- TYPENAME digraph<NT,AT>::const_arc_vector digraph<NT,AT>::adjacent_arcs(TYPENAME digraph<NT,AT>::const_iterator from,
- TYPENAME digraph<NT,AT>::const_iterator to) const
- throw(wrong_object,null_dereference,end_dereference)
- {
- from.assert_valid(this);
- to.assert_valid(this);
- std::vector<digraph_arc_iterator<NT,AT,const AT&,const AT*> > result;
- for (unsigned arc = 0; arc < fanout(from); arc++)
- {
- if (arc_to(output(from, arc)) == to)
- result.push_back(output(from,arc));
- }
- return result;
- }
-
- template<typename NT, typename AT>
- TYPENAME digraph<NT,AT>::arc_vector digraph<NT,AT>::adjacent_arcs(TYPENAME digraph<NT,AT>::iterator from,
- TYPENAME digraph<NT,AT>::iterator to)
- throw(wrong_object,null_dereference,end_dereference)
- {
- return deconstify_arcs(adjacent_arcs(from.constify(), to.constify()));
- }
-
- template<typename NT, typename AT>
- TYPENAME digraph<NT,AT>::const_node_vector digraph<NT,AT>::input_adjacencies(TYPENAME digraph<NT,AT>::const_iterator to) const
- throw(wrong_object,null_dereference,end_dereference)
- {
- std::vector<digraph_iterator<NT,AT,const NT&,const NT*> > result;
- for (unsigned arc = 0; arc < fanin(to); arc++)
- {
- digraph_iterator<NT,AT,const NT&,const NT*> from = arc_from(input(to, arc));
- if (std::find(result.begin(), result.end(), from) == result.end())
- result.push_back(from);
- }
- return result;
- }
-
- template<typename NT, typename AT>
- TYPENAME digraph<NT,AT>::node_vector digraph<NT,AT>::input_adjacencies(TYPENAME digraph<NT,AT>::iterator to)
- throw(wrong_object,null_dereference,end_dereference)
- {
- return deconstify_nodes(input_adjacencies(to.constify()));
- }
-
- template<typename NT, typename AT>
- TYPENAME digraph<NT,AT>::const_node_vector digraph<NT,AT>::output_adjacencies(TYPENAME digraph<NT,AT>::const_iterator from) const
- throw(wrong_object,null_dereference,end_dereference)
- {
- std::vector<digraph_iterator<NT,AT,const NT&,const NT*> > result;
- for (unsigned arc = 0; arc < fanout(from); arc++)
- {
- digraph_iterator<NT,AT,const NT&,const NT*> to = arc_to(output(from, arc));
- if (find(result.begin(), result.end(), to) == result.end())
- result.push_back(to);
- }
- return result;
- }
-
- template<typename NT, typename AT>
- TYPENAME digraph<NT,AT>::node_vector digraph<NT,AT>::output_adjacencies(TYPENAME digraph<NT,AT>::iterator from)
- throw(wrong_object,null_dereference,end_dereference)
- {
- return deconstify_nodes(output_adjacencies(from.constify()));
- }
-
- ////////////////////////////////////////////////////////////////////////////////
- // Topographical Sort Algorithms
-
- template<typename NT, typename AT>
- std::pair<TYPENAME digraph<NT,AT>::const_node_vector, TYPENAME digraph<NT,AT>::const_arc_vector>
- digraph<NT,AT>::sort(TYPENAME digraph<NT,AT>::arc_select_fn select) const
- {
- std::vector<digraph_iterator<NT,AT,const NT&,const NT*> > result;
- std::vector<digraph_arc_iterator<NT,AT,const AT&,const AT*> > errors;
- // build a map containing the number of fanins to each node that must be visited before this one
- std::map<digraph_iterator<NT,AT,const NT&,const NT*>,unsigned> fanin_map;
- for (digraph_iterator<NT,AT,const NT&,const NT*> n = begin(); n != end(); n++)
- {
- unsigned predecessors = 0;
- // only count predecessors connected by selected arcs
- for (unsigned f = 0; f < fanin(n); f++)
- {
- digraph_arc_iterator<NT,AT, const AT&,const AT*> input_arc = input(n,f);
- digraph_iterator<NT,AT,const NT&,const NT*> predecessor = arc_from(input_arc);
- if (!select || select(*this,input_arc))
- predecessors++;
- }
- if (predecessors == 0)
- {
- result.push_back(n);
- }
- else
- {
- fanin_map[n] = predecessors;
- }
- }
- // main algorithm applies the topographical sort repeatedly. For a DAG, it
- // will complete first time. However, with backward arcs, the first
- // iteration will fail. The algorithm then tries breaking random arcs to try
- // to get an ordering.
- for(unsigned i = 0; !fanin_map.empty(); )
- {
- // now visit each node in traversal order, decrementing the fanin count of
- // all successors. As each successor's fanin count goes to zero, it is
- // appended to the result.
- for (; i < result.size(); i++)
- {
- // Note: dereferencing gives us a node iterator
- digraph_iterator<NT,AT,const NT&,const NT*> current = result[i];
- for (unsigned f = 0; f < fanout(current); f++)
- {
- // only consider successors connected by selected arcs
- digraph_arc_iterator<NT,AT, const AT&,const AT*> output_arc = output(current, f);
- digraph_iterator<NT,AT,const NT&,const NT*> successor = arc_to(output_arc);
- if (!select || select(*this,output_arc))
- {
- // don't consider arcs that have been eliminated to break a loop
- if (fanin_map.find(successor) != fanin_map.end())
- {
- --fanin_map[successor];
- if ((fanin_map[successor]) == 0)
- {
- result.push_back(successor);
- fanin_map.erase(fanin_map.find(successor));
- }
- }
- }
- }
- }
- if (!fanin_map.empty())
- {
- // there must be backward arcs preventing completion
- // try removing arcs from the sort to get a partial ordering containing all the nodes
-
- // select an arc that is still relevant to the sort and break it
- // first select a node that has non-zero fanin and its predecessor that has non-zero fanin
- digraph_iterator<NT,AT,const NT&,const NT*> stuck_node = fanin_map.begin()->first;
- for (unsigned f = 0; f < fanin(stuck_node); f++)
- {
- // now successively remove input arcs that are still part of the sort until the fanin reduces to zero
- // first find a relevant arc - this must be a selected arc that has not yet been traversed by the first half of the algorithm
- digraph_arc_iterator<NT,AT, const AT&,const AT*> input_arc = input(stuck_node, f);
- if (!select || select(*this,input_arc))
- {
- digraph_iterator<NT,AT,const NT&,const NT*> predecessor = arc_from(input_arc);
- if (fanin_map.find(predecessor) != fanin_map.end())
- {
- // found the right combination - remove this arc and then drop out of the fanin loop to restart the outer sort loop
- errors.push_back(input_arc);
- --fanin_map[stuck_node];
- if ((fanin_map[stuck_node]) == 0)
- {
- result.push_back(stuck_node);
- fanin_map.erase(fanin_map.find(stuck_node));
- break;
- }
- }
- }
- }
- }
- }
- return std::make_pair(result,errors);
- }
-
- template<typename NT, typename AT>
- std::pair<TYPENAME digraph<NT,AT>::node_vector, TYPENAME digraph<NT,AT>::arc_vector>
- digraph<NT,AT>::sort(TYPENAME digraph<NT,AT>::arc_select_fn select)
- {
- std::pair<std::vector<digraph_iterator<NT,AT,const NT&,const NT*> >,
- std::vector<digraph_arc_iterator<NT,AT,const AT&,const AT*> > > const_result =
- const_cast<const digraph<NT,AT>*>(this)->sort(select);
-
- std::pair<std::vector<digraph_iterator<NT,AT,NT&,NT*> >,
- std::vector<digraph_arc_iterator<NT,AT,AT&,AT*> > > result =
- std::make_pair(deconstify_nodes(const_result.first),deconstify_arcs(const_result.second));
- return result;
- }
-
- template<typename NT, typename AT>
- TYPENAME digraph<NT,AT>::const_node_vector digraph<NT,AT>::dag_sort(TYPENAME digraph<NT,AT>::arc_select_fn select) const
- {
- std::pair<std::vector<digraph_iterator<NT,AT,const NT&,const NT*> >,
- std::vector<digraph_arc_iterator<NT,AT,const AT&,const AT*> > > result = sort(select);
- if (result.second.empty()) return result.first;
- return std::vector<digraph_iterator<NT,AT,const NT&,const NT*> >();
- }
-
- template<typename NT, typename AT>
- TYPENAME digraph<NT,AT>::node_vector digraph<NT,AT>::dag_sort(TYPENAME digraph<NT,AT>::arc_select_fn select)
- {
- return deconstify_nodes(const_cast<const digraph<NT,AT>*>(this)->dag_sort(select));
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Path Algorithms
-
- template<typename NT, typename AT>
- bool digraph<NT,AT>::path_exists_r(TYPENAME digraph<NT,AT>::const_iterator from,
- TYPENAME digraph<NT,AT>::const_iterator to,
- TYPENAME digraph<NT,AT>::const_iterator_set& visited,
- TYPENAME digraph<NT,AT>::arc_select_fn select) const
- throw(wrong_object,null_dereference,end_dereference)
- {
- // Recursive part of the digraph::path_exists function. This is based on a
- // depth first search algorithm and stops the moment it finds a path
- // regardless of its length. Simply traverse every output and recurse on that
- // node until we find the to node or run out of things to recurse on. However,
- // to avoid infinite recursion due to cycles in the graph, I need to maintain
- // a set of visited nodes. The visited set is updated when a candidate is
- // found but tested before the recursion on the candidate so that the number of
- // function calls is minimised.
- for (unsigned i = 0; i < fanout(from); i++)
- {
- digraph_arc_iterator<NT,AT, const AT&,const AT*> arc = output(from,i);
- if (!select || select(*this, arc))
- {
- digraph_iterator<NT,AT,const NT&,const NT*> node = arc_to(arc);
- // if the node is the target, return immediately
- if (node == to) return true;
- // update the visited set and give up if the insert fails, which indicates that the node has already been visited
- if (!(visited.insert(node).second)) return false;
- // now recurse - a path exists from from to to if a path exists from an adjacent node to to
- if (path_exists_r(node,to,visited,select)) return true;
- }
- }
- return false;
- }
-
- template<typename NT, typename AT>
- bool digraph<NT,AT>::path_exists(TYPENAME digraph<NT,AT>::const_iterator from,
- TYPENAME digraph<NT,AT>::const_iterator to,
- TYPENAME digraph<NT,AT>::arc_select_fn select) const
- throw(wrong_object,null_dereference,end_dereference)
- {
- // set up the recursion with its initial visited set and then recurse
- std::set<digraph_iterator<NT,AT,const NT&,const NT*> > visited;
- visited.insert(from);
- return path_exists_r(from, to, visited, select);
- }
-
- template<typename NT, typename AT>
- bool digraph<NT,AT>::path_exists(TYPENAME digraph<NT,AT>::iterator from,
- TYPENAME digraph<NT,AT>::iterator to,
- TYPENAME digraph<NT,AT>::arc_select_fn select)
- throw(wrong_object,null_dereference,end_dereference)
- {
- return path_exists(from.constify(), to.constify(), select);
- }
-
- template<typename NT, typename AT>
- void digraph<NT,AT>::all_paths_r(TYPENAME digraph<NT,AT>::const_iterator from,
- TYPENAME digraph<NT,AT>::const_iterator to,
- TYPENAME digraph<NT,AT>::const_arc_vector& so_far,
- TYPENAME digraph<NT,AT>::const_path_vector& result,
- TYPENAME digraph<NT,AT>::arc_select_fn select) const
- throw(wrong_object,null_dereference,end_dereference)
- {
- // This is the recursive part of the all_paths function. The field so_far
- // contains the path so far so that when 'to' is reached, the path is
- // complete. It serves the same purpose as the visited set in the path_exists
- // function except that it also preserves the path order. It also serves the
- // purpose of detecting cycles and thus stopping infinite recursion. Every
- // time the recursion reaches the to node, a copy of so_far is appended to the
- // path set.
- for (unsigned i = 0; i < fanout(from); i++)
- {
- digraph_arc_iterator<NT,AT, const AT&,const AT*> candidate = output(from,i);
- // assert_valid that the arc is selected and then assert_valid that the candidate has not
- // been visited on this path and only allow further recursion if it hasn't
- if ((!select || select(*this, candidate)) && std::find(so_far.begin(), so_far.end(), candidate) == so_far.end())
- {
- // extend the path tracing the route to this arc
- so_far.push_back(candidate);
- // if the candidate arc points to the target, update the result set and prevent further recursion, otherwise recurse
- if (arc_to(candidate) == to)
- result.push_back(so_far);
- else
- all_paths_r(arc_to(candidate),to,so_far,result,select);
- so_far.pop_back();
- }
- }
- }
-
- template<typename NT, typename AT>
- TYPENAME digraph<NT,AT>::const_path_vector
- digraph<NT,AT>::all_paths(TYPENAME digraph<NT,AT>::const_iterator from,
- TYPENAME digraph<NT,AT>::const_iterator to,
- TYPENAME digraph<NT,AT>::arc_select_fn select) const
- throw(wrong_object,null_dereference,end_dereference)
- {
- // set up the recursion with empty data fields and then recurse
- std::vector<std::vector<digraph_arc_iterator<NT,AT,const AT&,const AT*> > > result;
- std::vector<digraph_arc_iterator<NT,AT,const AT&,const AT*> > so_far;
- all_paths_r(from, to, so_far, result, select);
- return result;
- }
-
- template<typename NT, typename AT>
- TYPENAME digraph<NT,AT>::path_vector
- digraph<NT,AT>::all_paths(TYPENAME digraph<NT,AT>::iterator from,
- TYPENAME digraph<NT,AT>::iterator to,
- TYPENAME digraph<NT,AT>::arc_select_fn select)
- throw(wrong_object,null_dereference,end_dereference)
- {
- return deconstify_paths(all_paths(from.constify(), to.constify(), select));
- }
-
- template<typename NT, typename AT>
- void digraph<NT,AT>::reachable_nodes_r(TYPENAME digraph<NT,AT>::const_iterator from,
- TYPENAME digraph<NT,AT>::const_iterator_set& visited,
- TYPENAME digraph<NT,AT>::arc_select_fn select) const
- throw(wrong_object,null_dereference,end_dereference)
- {
- // The recursive part of the reachable_nodes function.
- // This is a depth-first traversal again but this time it carries on to find all the reachable nodes
- // Just keep recursing on all the adjacent nodes of each node, skipping already visited nodes to avoid cycles
- for (unsigned i = 0; i < fanout(from); i++)
- {
- digraph_arc_iterator<NT,AT, const AT&,const AT*> arc = output(from,i);
- if (!select || select(*this,arc))
- {
- digraph_iterator<NT,AT,const NT&,const NT*> candidate = arc_to(arc);
- if (visited.insert(candidate).second)
- reachable_nodes_r(candidate,visited,select);
- }
- }
- }
-
- template<typename NT, typename AT>
- TYPENAME digraph<NT,AT>::const_node_vector
- digraph<NT,AT>::reachable_nodes(TYPENAME digraph<NT,AT>::const_iterator from,
- TYPENAME digraph<NT,AT>::arc_select_fn select) const
- throw(wrong_object,null_dereference,end_dereference)
- {
- // seed the recursion, marking the starting node as already visited
- std::set<digraph_iterator<NT,AT,const NT&,const NT*> > visited;
- visited.insert(from);
- reachable_nodes_r(from, visited, select);
- // convert the visited set into the required output form
- // exclude the starting node
- std::vector<digraph_iterator<NT,AT,const NT&,const NT*> > result;
- for (TYPENAME std::set<digraph_iterator<NT,AT,const NT&,const NT*> >::iterator i = visited.begin(); i != visited.end(); i++)
- if (*i != from)
- result.push_back(*i);
- return result;
- }
-
- template<typename NT, typename AT>
- TYPENAME digraph<NT,AT>::node_vector
- digraph<NT,AT>::reachable_nodes(TYPENAME digraph<NT,AT>::iterator from,
- TYPENAME digraph<NT,AT>::arc_select_fn select)
- throw(wrong_object,null_dereference,end_dereference)
- {
- return deconstify_nodes(reachable_nodes(from.constify(), select));
- }
-
- template<typename NT, typename AT>
- void digraph<NT,AT>::reaching_nodes_r(TYPENAME digraph<NT,AT>::const_iterator to,
- TYPENAME digraph<NT,AT>::const_iterator_set& visited,
- TYPENAME digraph<NT,AT>::arc_select_fn select) const
- throw(wrong_object,null_dereference,end_dereference)
- {
- // The recursive part of the reaching_nodes function.
- // Just like the reachable_nodes_r function but it goes backwards
- for (unsigned i = 0; i < fanin(to); i++)
- {
- digraph_arc_iterator<NT,AT, const AT&,const AT*> arc = input(to,i);
- if (!select || select(*this,arc))
- {
- digraph_iterator<NT,AT,const NT&,const NT*> candidate = arc_from(input(to,i));
- if (visited.insert(candidate).second)
- reaching_nodes_r(candidate,visited,select);
- }
- }
- }
-
- template<typename NT, typename AT>
- TYPENAME digraph<NT,AT>::const_node_vector
- digraph<NT,AT>::reaching_nodes(TYPENAME digraph<NT,AT>::const_iterator to,
- TYPENAME digraph<NT,AT>::arc_select_fn select) const
- throw(wrong_object,null_dereference,end_dereference)
- {
- // seed the recursion, marking the starting node as already visited
- std::set<digraph_iterator<NT,AT,const NT&,const NT*> > visited;
- visited.insert(to);
- reaching_nodes_r(to,visited,select);
- // convert the visited set into the required output form
- // exclude the end node
- std::vector<digraph_iterator<NT,AT,const NT&,const NT*> > result;
- for (TYPENAME std::set<digraph_iterator<NT,AT,const NT&,const NT*> >::iterator i = visited.begin(); i != visited.end(); i++)
- if (*i != to)
- result.push_back(*i);
- return result;
- }
-
- template<typename NT, typename AT>
- TYPENAME digraph<NT,AT>::node_vector
- digraph<NT,AT>::reaching_nodes(TYPENAME digraph<NT,AT>::iterator to,
- TYPENAME digraph<NT,AT>::arc_select_fn select)
- throw(wrong_object,null_dereference,end_dereference)
- {
- return deconstify_nodes(reaching_nodes(to.constify(),select));
- }
-
- ////////////////////////////////////////////////////////////////////////////////
- // Shortest Path Algorithms
-
- template<typename NT, typename AT>
- TYPENAME digraph<NT,AT>::const_arc_vector
- digraph<NT,AT>::shortest_path(TYPENAME digraph<NT,AT>::const_iterator from,
- TYPENAME digraph<NT,AT>::const_iterator to,
- TYPENAME digraph<NT,AT>::arc_select_fn select) const
- throw(wrong_object,null_dereference,end_dereference)
- {
- std::vector<std::vector<digraph_arc_iterator<NT,AT,const AT&,const AT*> > > paths = all_paths(from,to,select);
- std::vector<digraph_arc_iterator<NT,AT,const AT&,const AT*> > shortest;
- for (TYPENAME std::vector<std::vector<digraph_arc_iterator<NT,AT,const AT&,const AT*> > >::iterator i = paths.begin(); i != paths.end(); i++)
- if (shortest.empty() || i->size() < shortest.size())
- shortest = *i;
- return shortest;
- }
-
- template<typename NT, typename AT>
- TYPENAME digraph<NT,AT>::arc_vector
- digraph<NT,AT>::shortest_path(TYPENAME digraph<NT,AT>::iterator from,
- TYPENAME digraph<NT,AT>::iterator to,
- TYPENAME digraph<NT,AT>::arc_select_fn select)
- throw(wrong_object,null_dereference,end_dereference)
- {
- return deconstify_arcs(shortest_path(from.constify(),to.constify(),select));
- }
-
- template<typename NT, typename AT>
- TYPENAME digraph<NT,AT>::const_path_vector
- digraph<NT,AT>::shortest_paths(TYPENAME digraph<NT,AT>::const_iterator from,
- TYPENAME digraph<NT,AT>::arc_select_fn select) const
- throw(wrong_object,null_dereference,end_dereference)
- {
- from.assert_valid(this);
- // This is an unweighted shortest path algorithm based on the algorithm from
- // Weiss's book. This is essentially a breadth-first traversal or graph
- // colouring algorithm. It is an iterative algorithm, so no recursion here! It
- // works by creating a node queue initialised with the starting node. It then
- // consumes the queue from front to back. For each node, it finds the
- // successors and appends them to the queue. If a node is already 'known' it
- // is not added - this avoids cycles. Thus the queue insert ordering
- // represents the breadth-first ordering. On the way it creates a map of
- // visited nodes. This is a map not a set because it also stores the arc that
- // nominated this node as a shortest path. The full path can then be recreated
- // from the map by just walking back through the predecessors. The depth (or
- // colour) can be determined by the path length.
- std::vector<std::vector<digraph_arc_iterator<NT,AT,const AT&,const AT*> > > result;
- // initialise the iteration by creating a queue and adding the start node
- std::deque<digraph_iterator<NT,AT,const NT&,const NT*> > nodes;
- nodes.push_back(from);
- // Create a map to store the set of known nodes mapped to their predecessor
- // arcs. Initialise it with the current node, which has no predecessor. Note
- // that the algorithm uses the feature of digraph iterators that they can be
- // null iterators and that all null iterators are equal.
- typedef std::map<digraph_iterator<NT,AT,const NT&,const NT*>,
- digraph_arc_iterator<NT,AT,const AT&,const AT*> > known_map;
- known_map known;
- known.insert(std::make_pair(from,digraph_arc_iterator<NT,AT, const AT&,const AT*>()));
- // now the iterative part of the algorithm
- while(!nodes.empty())
- {
- // pop the queue to get the next node to process - unfortunately the STL
- // deque::pop does not return the popped value
- digraph_iterator<NT,AT,const NT&,const NT*> current = nodes.front();
- nodes.pop_front();
- // now visit all the successors
- for (unsigned i = 0; i < fanout(current); i++)
- {
- digraph_arc_iterator<NT,AT, const AT&,const AT*> next_arc = output(current,i);
- // assert_valid whether the successor arc is a selected arc and can be part of a path
- if (!select || select(*this,next_arc))
- {
- digraph_iterator<NT,AT,const NT&,const NT*> next = arc_to(next_arc);
- // Discard any successors that are known because to be known already they
- // must have another shorter path. Otherwise add the successor node to the
- // queue to be visited later. To minimise the overhead of map lookup I use
- // the usual trick of trying to insert the node and determining whether
- // the node was known by the success or failure of the insertion - this is
- // a Good STL Trick (TM).
- if (known.insert(std::make_pair(next,next_arc)).second)
- nodes.push_back(next);
- }
- }
- }
- // The map contains the results as an unordered set of nodes, mapped to their
- // predecessor arcs and weight. This now needs to be converted into a set of
- // paths. This is done by starting with a node from the map, finding its
- // predecessor arc and therefore its predecessor node, looking that up in the
- // map to find its predecessor and so on until the start node is reached (it
- // has a null predecessor). Note that the known set includes the from node
- // which does not generate a path.
- for (TYPENAME known_map::iterator i = known.begin(); i != known.end(); i++)
- {
- if (i->first != from)
- {
- const_arc_vector this_path;
- for (TYPENAME known_map::iterator node = i;
- node->second.valid();
- node = known.find(arc_from(node->second)))
- this_path.insert(this_path.begin(),node->second);
- result.push_back(this_path);
- }
- }
- return result;
- }
-
- template<typename NT, typename AT>
- TYPENAME digraph<NT,AT>::path_vector
- digraph<NT,AT>::shortest_paths(TYPENAME digraph<NT,AT>::iterator from,
- TYPENAME digraph<NT,AT>::arc_select_fn select)
- throw(wrong_object,null_dereference,end_dereference)
- {
- return deconstify_paths(shortest_paths(from.constify(),select));
- }
-
- ////////////////////////////////////////////////////////////////////////////////
-
-} // end namespace stlplus
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+// Author: Andy Rushton\r
+// Copyright: (c) Southampton University 1999-2004\r
+// (c) Andy Rushton 2004 onwards\r
+// License: BSD License, see ../docs/license.html\r
+\r
+// Note: I tried to write this using STL lists for the node and arc lists, but\r
+// it got far too hairy. The specific problem is that I wanted a digraph\r
+// iterator to contain a list::iterator so I needed to be able to generate a\r
+// list::iterator from a node or arc and STL list iterators don't give you that\r
+// functionality. I tried burgling the data structures, but that was\r
+// non-portable between different STL implementations so needed lots of #ifdefs\r
+// and so was mind-bogglingly awful and unreadable - in other words a\r
+// maintenance nightmare. I gave up and impemented my own lists - not difficult.\r
+\r
+// I use circular double-linked lists. The circular design means that both\r
+// ends of the list are equally accessible in unit time. An empty list\r
+// contains no objects. There is no end node in the list - unlike the STL\r
+// lists which have a dummy node for end iterators to point to -\r
+// conceptually the end iterator points one element beyond the end of the\r
+// list. However, I implement the end iterator concept in the iterator\r
+// itself, so do not need the dummy end node.\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include <algorithm>\r
+#include <deque>\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// Internals\r
+\r
+namespace stlplus\r
+{\r
+\r
+ template<typename NT, typename AT>\r
+ class digraph_node\r
+ {\r
+ public:\r
+ master_iterator<digraph<NT,AT>, digraph_node<NT,AT> > m_master;\r
+ NT m_data;\r
+ digraph_node<NT,AT>* m_prev;\r
+ digraph_node<NT,AT>* m_next;\r
+ std::vector<digraph_arc<NT,AT>*> m_inputs;\r
+ std::vector<digraph_arc<NT,AT>*> m_outputs;\r
+ public:\r
+ digraph_node(const digraph<NT,AT>* owner, const NT& d = NT()) :\r
+ m_master(owner,this), m_data(d), m_prev(0), m_next(0)\r
+ {\r
+ }\r
+ ~digraph_node(void)\r
+ {\r
+ }\r
+ };\r
+\r
+ template<typename NT, typename AT>\r
+ class digraph_arc\r
+ {\r
+ public:\r
+ master_iterator<digraph<NT,AT>, digraph_arc<NT,AT> > m_master;\r
+ AT m_data;\r
+ digraph_arc<NT,AT>* m_prev;\r
+ digraph_arc<NT,AT>* m_next;\r
+ digraph_node<NT,AT>* m_from;\r
+ digraph_node<NT,AT>* m_to;\r
+ digraph_arc(const digraph<NT,AT>* owner, digraph_node<NT,AT>* from = 0, digraph_node<NT,AT>* to = 0, const AT& d = AT()) : \r
+ m_master(owner,this), m_data(d), m_prev(0), m_next(0), m_from(from), m_to(to)\r
+ {\r
+ }\r
+ };\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // Iterators\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // Node iterator\r
+\r
+ // construct a null iterator\r
+ template<typename NT, typename AT, typename NRef, typename NPtr>\r
+ digraph_iterator<NT,AT,NRef,NPtr>::digraph_iterator(void)\r
+ {\r
+ }\r
+\r
+ // valid iterator\r
+ template<typename NT, typename AT, typename NRef, typename NPtr>\r
+ digraph_iterator<NT,AT,NRef,NPtr>::digraph_iterator(digraph_node<NT,AT>* node) :\r
+ safe_iterator<digraph<NT,AT>,digraph_node<NT,AT> >(node->m_master)\r
+ {\r
+ }\r
+\r
+ // end iterator\r
+ template<typename NT, typename AT, typename NRef, typename NPtr>\r
+ digraph_iterator<NT,AT,NRef,NPtr>::digraph_iterator(const digraph<NT,AT>* owner) :\r
+ safe_iterator<digraph<NT,AT>,digraph_node<NT,AT> >(owner)\r
+ {\r
+ }\r
+\r
+ // alias an iterator\r
+ template<typename NT, typename AT, typename NRef, typename NPtr>\r
+ digraph_iterator<NT,AT,NRef,NPtr>::digraph_iterator(const safe_iterator<digraph<NT,AT>, digraph_node<NT,AT> >& iterator) : \r
+ safe_iterator<digraph<NT,AT>,digraph_node<NT,AT> >(iterator)\r
+ {\r
+ }\r
+\r
+ // destructor\r
+ template<typename NT, typename AT, typename NRef, typename NPtr>\r
+ digraph_iterator<NT,AT,NRef,NPtr>::~digraph_iterator(void)\r
+ {\r
+ }\r
+\r
+ template<typename NT, typename AT, typename NRef, typename NPtr>\r
+ TYPENAME digraph_iterator<NT,AT,NRef,NPtr>::const_iterator digraph_iterator<NT,AT,NRef,NPtr>::constify (void) const\r
+ {\r
+ return digraph_iterator<NT,AT,const NT&,const NT*>(*this);\r
+ }\r
+\r
+ template<typename NT, typename AT, typename NRef, typename NPtr>\r
+ TYPENAME digraph_iterator<NT,AT,NRef,NPtr>::iterator digraph_iterator<NT,AT,NRef,NPtr>::deconstify (void) const\r
+ {\r
+ return digraph_iterator<NT,AT,NT&,NT*>(*this);\r
+ }\r
+\r
+ template<typename NT, typename AT, typename NRef, typename NPtr>\r
+ TYPENAME digraph_iterator<NT,AT,NRef,NPtr>::this_iterator& digraph_iterator<NT,AT,NRef,NPtr>::operator ++ (void)\r
+ throw(null_dereference,end_dereference)\r
+ {\r
+ this->assert_valid();\r
+ if (this->node()->m_next)\r
+ this->set(this->node()->m_next->m_master);\r
+ else\r
+ this->set_end();\r
+ return *this;\r
+ }\r
+\r
+ template<typename NT, typename AT, typename NRef, typename NPtr>\r
+ TYPENAME digraph_iterator<NT,AT,NRef,NPtr>::this_iterator digraph_iterator<NT,AT,NRef,NPtr>::operator ++ (int)\r
+ throw(null_dereference,end_dereference)\r
+ {\r
+ // post-increment is defined in terms of the pre-increment\r
+ digraph_iterator<NT,AT,NRef,NPtr> result(*this);\r
+ ++(*this);\r
+ return result;\r
+ }\r
+\r
+ template<typename NT, typename AT, typename NRef, typename NPtr>\r
+ TYPENAME digraph_iterator<NT,AT,NRef,NPtr>::this_iterator& digraph_iterator<NT,AT,NRef,NPtr>::operator -- (void)\r
+ throw(null_dereference,end_dereference)\r
+ {\r
+ this->assert_valid();\r
+ if (this->node()->m_prev)\r
+ this->set(this->node()->m_prev->m_master);\r
+ else\r
+ this->set_end();\r
+ return *this;\r
+ }\r
+\r
+ template<typename NT, typename AT, typename NRef, typename NPtr>\r
+ TYPENAME digraph_iterator<NT,AT,NRef,NPtr>::this_iterator digraph_iterator<NT,AT,NRef,NPtr>::operator -- (int)\r
+ throw(null_dereference,end_dereference)\r
+ {\r
+ // post-decrement is defined in terms of the pre-decrement\r
+ digraph_iterator<NT,AT,NRef,NPtr> result(*this);\r
+ --(*this);\r
+ return result;\r
+ }\r
+\r
+ template<typename NT, typename AT, typename NRef, typename NPtr>\r
+ bool digraph_iterator<NT,AT,NRef,NPtr>::operator == (const TYPENAME digraph_iterator<NT,AT,NRef,NPtr>::this_iterator& r) const\r
+ {\r
+ return equal(r);\r
+ }\r
+\r
+ template<typename NT, typename AT, typename NRef, typename NPtr>\r
+ bool digraph_iterator<NT,AT,NRef,NPtr>::operator != (const TYPENAME digraph_iterator<NT,AT,NRef,NPtr>::this_iterator& r) const\r
+ {\r
+ return !operator==(r);\r
+ }\r
+\r
+ template<typename NT, typename AT, typename NRef, typename NPtr>\r
+ bool digraph_iterator<NT,AT,NRef,NPtr>::operator < (const TYPENAME digraph_iterator<NT,AT,NRef,NPtr>::this_iterator& r) const\r
+ {\r
+ return compare(r) < 0;\r
+ }\r
+\r
+ template<typename NT, typename AT, typename NRef, typename NPtr>\r
+ TYPENAME digraph_iterator<NT,AT,NRef,NPtr>::reference digraph_iterator<NT,AT,NRef,NPtr>::operator*(void) const\r
+ throw(null_dereference,end_dereference)\r
+ {\r
+ this->assert_valid();\r
+ return this->node()->m_data;\r
+ }\r
+\r
+ template<typename NT, typename AT, typename NRef, typename NPtr>\r
+ TYPENAME digraph_iterator<NT,AT,NRef,NPtr>::pointer digraph_iterator<NT,AT,NRef,NPtr>::operator->(void) const\r
+ throw(null_dereference,end_dereference)\r
+ {\r
+ return &(operator*());\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // Arc Iterator\r
+\r
+ template<typename NT, typename AT, typename ARef, typename APtr>\r
+ digraph_arc_iterator<NT,AT,ARef,APtr>::digraph_arc_iterator(void)\r
+ {\r
+ }\r
+\r
+ // valid iterator\r
+ template<typename NT, typename AT, typename NRef, typename NPtr>\r
+ digraph_arc_iterator<NT,AT,NRef,NPtr>::digraph_arc_iterator(digraph_arc<NT,AT>* arc) :\r
+ safe_iterator<digraph<NT,AT>,digraph_arc<NT,AT> >(arc->m_master)\r
+ {\r
+ }\r
+\r
+ // end iterator\r
+ template<typename NT, typename AT, typename NRef, typename NPtr>\r
+ digraph_arc_iterator<NT,AT,NRef,NPtr>::digraph_arc_iterator(const digraph<NT,AT>* owner) :\r
+ safe_iterator<digraph<NT,AT>,digraph_arc<NT,AT> >(owner)\r
+ {\r
+ }\r
+\r
+ // alias an iterator\r
+ template<typename NT, typename AT, typename NRef, typename NPtr>\r
+ digraph_arc_iterator<NT,AT,NRef,NPtr>::digraph_arc_iterator(const safe_iterator<digraph<NT,AT>, digraph_arc<NT,AT> >& iterator) : \r
+ safe_iterator<digraph<NT,AT>,digraph_arc<NT,AT> >(iterator)\r
+ {\r
+ }\r
+\r
+ template<typename NT, typename AT, typename ARef, typename APtr>\r
+ digraph_arc_iterator<NT,AT,ARef,APtr>::~digraph_arc_iterator(void)\r
+ {\r
+ }\r
+\r
+ template<typename NT, typename AT, typename NRef, typename NPtr>\r
+ TYPENAME digraph_arc_iterator<NT,AT,NRef,NPtr>::const_iterator digraph_arc_iterator<NT,AT,NRef,NPtr>::constify (void) const\r
+ {\r
+ return digraph_arc_iterator<NT,AT,const AT&,const AT*>(*this);\r
+ }\r
+\r
+ template<typename NT, typename AT, typename NRef, typename NPtr>\r
+ TYPENAME digraph_arc_iterator<NT,AT,NRef,NPtr>::iterator digraph_arc_iterator<NT,AT,NRef,NPtr>::deconstify (void) const\r
+ {\r
+ return digraph_arc_iterator<NT,AT,AT&,AT*>(*this);\r
+ }\r
+\r
+ template<typename NT, typename AT, typename ARef, typename APtr>\r
+ TYPENAME digraph_arc_iterator<NT,AT,ARef,APtr>::this_iterator& digraph_arc_iterator<NT,AT,ARef,APtr>::operator ++ (void)\r
+ throw(null_dereference,end_dereference)\r
+ {\r
+ this->assert_valid();\r
+ if (this->node()->m_next)\r
+ this->set(this->node()->m_next->m_master);\r
+ else\r
+ this->set_end();\r
+ return *this;\r
+ }\r
+\r
+ template<typename NT, typename AT, typename ARef, typename APtr>\r
+ TYPENAME digraph_arc_iterator<NT,AT,ARef,APtr>::this_iterator digraph_arc_iterator<NT,AT,ARef,APtr>::operator ++ (int)\r
+ throw(null_dereference,end_dereference)\r
+ {\r
+ // post-increment is defined in terms of the pre-increment\r
+ digraph_arc_iterator<NT,AT,ARef,APtr> result(*this);\r
+ ++(*this);\r
+ return result;\r
+ }\r
+\r
+ template<typename NT, typename AT, typename ARef, typename APtr>\r
+ TYPENAME digraph_arc_iterator<NT,AT,ARef,APtr>::this_iterator& digraph_arc_iterator<NT,AT,ARef,APtr>::operator -- (void)\r
+ throw(null_dereference,end_dereference)\r
+ {\r
+ this->assert_valid();\r
+ if (this->node()->m_prev)\r
+ this->set(this->node()->m_prev->m_master);\r
+ else\r
+ this->set_end();\r
+ return *this;\r
+ }\r
+\r
+ template<typename NT, typename AT, typename ARef, typename APtr>\r
+ TYPENAME digraph_arc_iterator<NT,AT,ARef,APtr>::this_iterator digraph_arc_iterator<NT,AT,ARef,APtr>::operator -- (int)\r
+ throw(null_dereference,end_dereference)\r
+ {\r
+ // post-decrement is defined in terms of the pre-decrement\r
+ digraph_arc_iterator<NT,AT,ARef,APtr> result(*this);\r
+ --(*this);\r
+ return result;\r
+ }\r
+\r
+ template<typename NT, typename AT, typename ARef, typename APtr>\r
+ bool digraph_arc_iterator<NT,AT,ARef,APtr>::operator == (const TYPENAME digraph_arc_iterator<NT,AT,ARef,APtr>::this_iterator& r) const\r
+ {\r
+ return equal(r);\r
+ }\r
+\r
+ template<typename NT, typename AT, typename ARef, typename APtr>\r
+ bool digraph_arc_iterator<NT,AT,ARef,APtr>::operator != (const TYPENAME digraph_arc_iterator<NT,AT,ARef,APtr>::this_iterator& r) const\r
+ {\r
+ return !operator==(r);\r
+ }\r
+\r
+ template<typename NT, typename AT, typename ARef, typename APtr>\r
+ bool digraph_arc_iterator<NT,AT,ARef,APtr>::operator < (const TYPENAME digraph_arc_iterator<NT,AT,ARef,APtr>::this_iterator& r) const\r
+ {\r
+ return compare(r) < 0;\r
+ }\r
+\r
+ template<typename NT, typename AT, typename ARef, typename APtr>\r
+ TYPENAME digraph_arc_iterator<NT,AT,ARef,APtr>::reference digraph_arc_iterator<NT,AT,ARef,APtr>::operator*(void) const\r
+ throw(null_dereference,end_dereference)\r
+ {\r
+ this->assert_valid();\r
+ return this->node()->m_data;\r
+ }\r
+\r
+ template<typename NT, typename AT, typename ARef, typename APtr>\r
+ TYPENAME digraph_arc_iterator<NT,AT,ARef,APtr>::pointer digraph_arc_iterator<NT,AT,ARef,APtr>::operator->(void) const\r
+ throw(null_dereference,end_dereference)\r
+ {\r
+ return &(operator*());\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // subtype utilities\r
+\r
+ template<typename NT, typename AT>\r
+ TYPENAME digraph<NT,AT>::const_arc_vector digraph<NT,AT>::constify_arcs(const TYPENAME digraph<NT,AT>::arc_vector& arcs) const\r
+ throw(wrong_object,null_dereference,end_dereference)\r
+ {\r
+ std::vector<digraph_arc_iterator<NT,AT,const AT&,const AT*> > result;\r
+ for (unsigned i = 0; i < arcs.size(); i++)\r
+ {\r
+ arcs[i].assert_valid(this);\r
+ result.push_back(arcs[i].constify());\r
+ }\r
+ return result;\r
+ }\r
+\r
+ template<typename NT, typename AT>\r
+ TYPENAME digraph<NT,AT>::arc_vector digraph<NT,AT>::deconstify_arcs(const TYPENAME digraph<NT,AT>::const_arc_vector& arcs) const\r
+ throw(wrong_object,null_dereference,end_dereference)\r
+ {\r
+ std::vector<digraph_arc_iterator<NT,AT,AT&,AT*> > result;\r
+ for (unsigned i = 0; i < arcs.size(); i++)\r
+ {\r
+ arcs[i].assert_valid(this);\r
+ result.push_back(arcs[i].deconstify());\r
+ }\r
+ return result;\r
+ }\r
+\r
+ template<typename NT, typename AT>\r
+ TYPENAME digraph<NT,AT>::const_path_vector digraph<NT,AT>::constify_paths(const TYPENAME digraph<NT,AT>::path_vector& paths) const\r
+ throw(wrong_object,null_dereference,end_dereference)\r
+ {\r
+ std::vector<std::vector<digraph_arc_iterator<NT,AT,const AT&,const AT*> > > result;\r
+ for (unsigned i = 0; i < paths.size(); i++)\r
+ result.push_back(constify_arcs(paths[i]));\r
+ return result;\r
+ }\r
+\r
+ template<typename NT, typename AT>\r
+ TYPENAME digraph<NT,AT>::path_vector digraph<NT,AT>::deconstify_paths(const TYPENAME digraph<NT,AT>::const_path_vector& paths) const\r
+ throw(wrong_object,null_dereference,end_dereference)\r
+ {\r
+ std::vector<std::vector<digraph_arc_iterator<NT,AT,AT&,AT*> > > result;\r
+ for (unsigned i = 0; i < paths.size(); i++)\r
+ result.push_back(deconstify_arcs(paths[i]));\r
+ return result;\r
+ }\r
+\r
+ template<typename NT, typename AT>\r
+ TYPENAME digraph<NT,AT>::const_node_vector digraph<NT,AT>::constify_nodes(const TYPENAME digraph<NT,AT>::node_vector& nodes) const\r
+ throw(wrong_object,null_dereference,end_dereference)\r
+ {\r
+ std::vector<digraph_iterator<NT,AT,const NT&,const NT*> > result;\r
+ for (unsigned i = 0; i < nodes.size(); i++)\r
+ {\r
+ nodes[i].assert_valid(this);\r
+ result.push_back(nodes[i].constify());\r
+ }\r
+ return result;\r
+ }\r
+\r
+ template<typename NT, typename AT>\r
+ TYPENAME digraph<NT,AT>::node_vector digraph<NT,AT>::deconstify_nodes(const TYPENAME digraph<NT,AT>::const_node_vector& nodes) const\r
+ throw(wrong_object,null_dereference,end_dereference)\r
+ {\r
+ std::vector<digraph_iterator<NT,AT,NT&,NT*> > result;\r
+ for (unsigned i = 0; i < nodes.size(); i++)\r
+ {\r
+ nodes[i].assert_valid(this);\r
+ result.push_back(nodes[i].deconstify());\r
+ }\r
+ return result;\r
+ }\r
+\r
+ template<typename NT, typename AT>\r
+ unsigned digraph<NT,AT>::npos(void)\r
+ {\r
+ return(unsigned)-1;\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // Constructors etc.\r
+\r
+ template<typename NT, typename AT>\r
+ digraph<NT,AT>::digraph(void) :\r
+ m_nodes_begin(0), m_nodes_end(0), m_arcs_begin(0), m_arcs_end(0)\r
+ {\r
+ // node and arc lists are circular double-linked lists\r
+ // they start out empty (no dummy end node)\r
+ }\r
+\r
+ template<typename NT, typename AT>\r
+ digraph<NT,AT>::~digraph(void)\r
+ {\r
+ clear();\r
+ }\r
+\r
+ template<typename NT, typename AT>\r
+ digraph<NT,AT>::digraph(const digraph<NT,AT>& r) :\r
+ m_nodes_begin(0), m_nodes_end(0), m_arcs_begin(0), m_arcs_end(0)\r
+ {\r
+ *this = r;\r
+ }\r
+\r
+ template<typename NT, typename AT>\r
+ digraph<NT,AT>& digraph<NT,AT>::operator=(const digraph<NT,AT>& r)\r
+ {\r
+ // make it self-copy safe i.e. a=a; is a valid instruction\r
+ if (this == &r) return *this;\r
+ clear();\r
+ // first phase is to copy the nodes, creating a map of cross references from the old nodes to their new equivalents\r
+ std::map<digraph_iterator<NT,AT,const NT&,const NT*>, digraph_iterator<NT,AT,NT&,NT*> > xref;\r
+ for (digraph_iterator<NT,AT,const NT&,const NT*> n = r.begin(); n != r.end(); n++)\r
+ xref[n] = insert(*n);\r
+ // second phase is to copy the arcs, using the map to convert the old to and from nodes to the new nodes\r
+ for (digraph_arc_iterator<NT,AT, const AT&,const AT*> a = r.arc_begin(); a != r.arc_end(); a++)\r
+ arc_insert(xref[r.arc_from(a)],xref[r.arc_to(a)],*a);\r
+ return *this;\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // Basic Node functions\r
+\r
+ template<typename NT, typename AT>\r
+ bool digraph<NT,AT>::empty(void) const\r
+ {\r
+ return m_nodes_begin == 0;\r
+ }\r
+\r
+ template<typename NT, typename AT>\r
+ unsigned digraph<NT,AT>::size(void) const\r
+ {\r
+ unsigned count = 0;\r
+ for (digraph_iterator<NT,AT,const NT&,const NT*> i = begin(); i != end(); i++)\r
+ count++;\r
+ return count;\r
+ }\r
+\r
+ template<typename NT, typename AT>\r
+ TYPENAME digraph<NT,AT>::iterator digraph<NT,AT>::insert(const NT& node_data)\r
+ {\r
+ digraph_node<NT,AT>* new_node = new digraph_node<NT,AT>(this,node_data);\r
+ if (!m_nodes_end)\r
+ {\r
+ // insert into an empty list\r
+ m_nodes_begin = new_node;\r
+ m_nodes_end = new_node;\r
+ }\r
+ else\r
+ {\r
+ // insert at the end of the list\r
+ new_node->m_prev = m_nodes_end;\r
+ m_nodes_end->m_next = new_node;\r
+ m_nodes_end = new_node;\r
+ }\r
+ return digraph_iterator<NT,AT,NT&,NT*>(new_node);\r
+ }\r
+\r
+ template<typename NT, typename AT>\r
+ TYPENAME digraph<NT,AT>::iterator digraph<NT,AT>::erase(TYPENAME digraph<NT,AT>::iterator iter)\r
+ throw(wrong_object,null_dereference,end_dereference)\r
+ {\r
+ iter.assert_valid(this);\r
+ // remove all arcs connected to this node first\r
+ // use arc_erase rather than arcs.erase because that tidies up the node at the other end of the arc too\r
+ for (unsigned i = fanin(iter); i--; )\r
+ arc_erase(input(iter,i));\r
+ for (unsigned j = fanout(iter); j--; )\r
+ arc_erase(output(iter,j));\r
+ // now unlink the node from the list and delete it\r
+ if (iter.node()->m_next)\r
+ iter.node()->m_next->m_prev = iter.node()->m_prev;\r
+ if (iter.node()->m_prev)\r
+ iter.node()->m_prev->m_next = iter.node()->m_next;\r
+ digraph_node<NT,AT>* next = iter.node()->m_next;\r
+ delete iter.node();\r
+ // return the next node in the list\r
+ if (next)\r
+ return digraph_iterator<NT,AT,NT&,NT*>(next);\r
+ else\r
+ return digraph_iterator<NT,AT,NT&,NT*>(this);\r
+ }\r
+\r
+ template<typename NT, typename AT>\r
+ void digraph<NT,AT>::clear(void)\r
+ {\r
+ // clearing the nodes also clears the arcs\r
+ for (digraph_iterator<NT,AT,NT&,NT*> i = begin(); i != end(); )\r
+ i = erase(i);\r
+ }\r
+\r
+ template<typename NT, typename AT>\r
+ TYPENAME digraph<NT,AT>::const_iterator digraph<NT,AT>::begin(void) const\r
+ {\r
+ if (m_nodes_begin)\r
+ return digraph_iterator<NT,AT,const NT&,const NT*>(m_nodes_begin);\r
+ else\r
+ return digraph_iterator<NT,AT,const NT&,const NT*>(this);\r
+ }\r
+\r
+ template<typename NT, typename AT>\r
+ TYPENAME digraph<NT,AT>::iterator digraph<NT,AT>::begin(void)\r
+ {\r
+ if (m_nodes_begin)\r
+ return digraph_iterator<NT,AT,NT&,NT*>(m_nodes_begin);\r
+ else\r
+ return digraph_iterator<NT,AT,NT&,NT*>(this);\r
+ }\r
+\r
+ template<typename NT, typename AT>\r
+ TYPENAME digraph<NT,AT>::const_iterator digraph<NT,AT>::end(void) const\r
+ {\r
+ return digraph_iterator<NT,AT,const NT&,const NT*>(this);\r
+ }\r
+\r
+ template<typename NT, typename AT>\r
+ TYPENAME digraph<NT,AT>::iterator digraph<NT,AT>::end(void)\r
+ {\r
+ return digraph_iterator<NT,AT,NT&,NT*>(this);\r
+ }\r
+\r
+ template<typename NT, typename AT>\r
+ unsigned digraph<NT,AT>::fanin(TYPENAME digraph<NT,AT>::const_iterator iter) const\r
+ throw(wrong_object,null_dereference,end_dereference)\r
+ {\r
+ iter.assert_valid(this);\r
+ return iter.node()->m_inputs.size();\r
+ }\r
+\r
+ template<typename NT, typename AT>\r
+ unsigned digraph<NT,AT>::fanin(TYPENAME digraph<NT,AT>::iterator iter)\r
+ throw(wrong_object,null_dereference,end_dereference)\r
+ {\r
+ iter.assert_valid(this);\r
+ return iter.node()->m_inputs.size();\r
+ }\r
+\r
+ template<typename NT, typename AT>\r
+ TYPENAME digraph<NT,AT>::const_arc_iterator digraph<NT,AT>::input(TYPENAME digraph<NT,AT>::const_iterator iter, unsigned i) const\r
+ throw(wrong_object,null_dereference,end_dereference,std::out_of_range)\r
+ {\r
+ iter.assert_valid(this);\r
+ if (i >= iter.node()->m_inputs.size()) throw std::out_of_range("digraph::input");\r
+ return digraph_arc_iterator<NT,AT, const AT&,const AT*>(iter.node()->m_inputs[i]);\r
+ }\r
+\r
+ template<typename NT, typename AT>\r
+ TYPENAME digraph<NT,AT>::arc_iterator digraph<NT,AT>::input(TYPENAME digraph<NT,AT>::iterator iter, unsigned i)\r
+ throw(wrong_object,null_dereference,end_dereference,std::out_of_range)\r
+ {\r
+ iter.assert_valid(this);\r
+ if (i >= iter.node()->m_inputs.size()) throw std::out_of_range("digraph::input");\r
+ return digraph_arc_iterator<NT,AT,AT&,AT*>(iter.node()->m_inputs[i]);\r
+ }\r
+\r
+ template<typename NT, typename AT>\r
+ unsigned digraph<NT,AT>::fanout(TYPENAME digraph<NT,AT>::const_iterator iter) const\r
+ throw(wrong_object,null_dereference,end_dereference)\r
+ {\r
+ iter.assert_valid(this);\r
+ return iter.node()->m_outputs.size();\r
+ }\r
+\r
+ template<typename NT, typename AT>\r
+ unsigned digraph<NT,AT>::fanout(TYPENAME digraph<NT,AT>::iterator iter)\r
+ throw(wrong_object,null_dereference,end_dereference)\r
+ {\r
+ iter.assert_valid(this);\r
+ return iter.node()->m_outputs.size();\r
+ }\r
+\r
+ template<typename NT, typename AT>\r
+ TYPENAME digraph<NT,AT>::const_arc_iterator digraph<NT,AT>::output(TYPENAME digraph<NT,AT>::const_iterator iter, unsigned i) const\r
+ throw(wrong_object,null_dereference,end_dereference,std::out_of_range)\r
+ {\r
+ iter.assert_valid(this);\r
+ if (i >= iter.node()->m_outputs.size()) throw std::out_of_range("digraph::output");\r
+ return digraph_arc_iterator<NT,AT, const AT&,const AT*>(iter.node()->m_outputs[i]);\r
+ }\r
+\r
+ template<typename NT, typename AT>\r
+ TYPENAME digraph<NT,AT>::arc_iterator digraph<NT,AT>::output(TYPENAME digraph<NT,AT>::iterator iter, unsigned i)\r
+ throw(wrong_object,null_dereference,end_dereference,std::out_of_range)\r
+ {\r
+ iter.assert_valid(this);\r
+ if (i >= iter.node()->m_outputs.size()) throw std::out_of_range("digraph::output");\r
+ return digraph_arc_iterator<NT,AT,AT&,AT*>(iter.node()->m_outputs[i]);\r
+ }\r
+\r
+ template<typename NT, typename AT>\r
+ TYPENAME digraph<NT,AT>::const_arc_vector digraph<NT,AT>::inputs(TYPENAME digraph<NT,AT>::const_iterator node) const\r
+ throw(wrong_object,null_dereference,end_dereference)\r
+ {\r
+ node.assert_valid(this);\r
+ std::vector<digraph_arc_iterator<NT,AT,const AT&, const AT*> > result;\r
+ for (unsigned i = 0; i < fanin(node); i++)\r
+ result.push_back(input(node,i));\r
+ return result;\r
+ }\r
+\r
+ template<typename NT, typename AT>\r
+ TYPENAME digraph<NT,AT>::arc_vector digraph<NT,AT>::inputs(TYPENAME digraph<NT,AT>::iterator node)\r
+ throw(wrong_object,null_dereference,end_dereference)\r
+ {\r
+ node.assert_valid(this);\r
+ std::vector<digraph_arc_iterator<NT,AT,AT&,AT*> > result;\r
+ for (unsigned i = 0; i < fanin(node); i++)\r
+ result.push_back(input(node,i));\r
+ return result;\r
+ }\r
+\r
+ template<typename NT, typename AT>\r
+ TYPENAME digraph<NT,AT>::const_arc_vector digraph<NT,AT>::outputs(TYPENAME digraph<NT,AT>::const_iterator node) const\r
+ throw(wrong_object,null_dereference,end_dereference)\r
+ {\r
+ node.assert_valid(this);\r
+ std::vector<digraph_arc_iterator<NT,AT,const AT&, const AT*> > result;\r
+ for (unsigned i = 0; i < fanout(node); i++)\r
+ result.push_back(output(node,i));\r
+ return result;\r
+ }\r
+\r
+ template<typename NT, typename AT>\r
+ TYPENAME digraph<NT,AT>::arc_vector digraph<NT,AT>::outputs(TYPENAME digraph<NT,AT>::iterator node)\r
+ throw(wrong_object,null_dereference,end_dereference)\r
+ {\r
+ node.assert_valid(this);\r
+ std::vector<digraph_arc_iterator<NT,AT,AT&,AT*> > result;\r
+ for (unsigned i = 0; i < fanout(node); i++)\r
+ result.push_back(output(node,i));\r
+ return result;\r
+ }\r
+\r
+ template<typename NT, typename AT>\r
+ unsigned digraph<NT,AT>::output_offset(TYPENAME digraph<NT,AT>::const_iterator from,\r
+ TYPENAME digraph<NT,AT>::const_arc_iterator arc) const\r
+ throw(wrong_object,null_dereference,end_dereference)\r
+ {\r
+ from.assert_valid(this);\r
+ arc.assert_valid(this);\r
+ for (unsigned i = 0; i < fanout(from); i++)\r
+ {\r
+ if (output(from,i) == arc)\r
+ return i;\r
+ }\r
+ return digraph<NT,AT>::npos();\r
+ }\r
+\r
+ template<typename NT, typename AT>\r
+ unsigned digraph<NT,AT>::output_offset(TYPENAME digraph<NT,AT>::iterator from,\r
+ TYPENAME digraph<NT,AT>::arc_iterator arc)\r
+ throw(wrong_object,null_dereference,end_dereference)\r
+ {\r
+ from.assert_valid(this);\r
+ arc.assert_valid(this);\r
+ for (unsigned i = 0; i < fanout(from); i++)\r
+ {\r
+ if (output(from,i) == arc)\r
+ return i;\r
+ }\r
+ return digraph<NT,AT>::npos();\r
+ }\r
+\r
+ template<typename NT, typename AT>\r
+ unsigned digraph<NT,AT>::input_offset(TYPENAME digraph<NT,AT>::const_iterator to,\r
+ TYPENAME digraph<NT,AT>::const_arc_iterator arc) const\r
+ throw(wrong_object,null_dereference,end_dereference)\r
+ {\r
+ to.assert_valid(this);\r
+ arc.assert_valid(this);\r
+ for (unsigned i = 0; i < fanin(to); i++)\r
+ {\r
+ if (input(to,i) == arc)\r
+ return i;\r
+ }\r
+ return digraph<NT,AT>::npos();\r
+ }\r
+\r
+ template<typename NT, typename AT>\r
+ unsigned digraph<NT,AT>::input_offset(TYPENAME digraph<NT,AT>::iterator to,\r
+ TYPENAME digraph<NT,AT>::arc_iterator arc)\r
+ throw(wrong_object,null_dereference,end_dereference)\r
+ {\r
+ to.assert_valid(this);\r
+ arc.assert_valid(this);\r
+ for (unsigned i = 0; i < fanin(to); i++)\r
+ {\r
+ if (input(to,i) == arc)\r
+ return i;\r
+ }\r
+ return digraph<NT,AT>::npos();\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // Basic Arc functions\r
+\r
+ template<typename NT, typename AT>\r
+ bool digraph<NT,AT>::arc_empty(void) const\r
+ {\r
+ return m_arcs_end == 0;\r
+ }\r
+\r
+ template<typename NT, typename AT>\r
+ unsigned digraph<NT,AT>::arc_size(void) const\r
+ {\r
+ unsigned count = 0;\r
+ for (digraph_arc_iterator<NT,AT, const AT&,const AT*> i = arc_begin(); i != arc_end(); i++)\r
+ count++;\r
+ return count;\r
+ }\r
+\r
+ template<typename NT, typename AT>\r
+ TYPENAME digraph<NT,AT>::arc_iterator digraph<NT,AT>::arc_insert(TYPENAME digraph<NT,AT>::iterator from,\r
+ TYPENAME digraph<NT,AT>::iterator to,\r
+ const AT& arc_data)\r
+ throw(wrong_object,null_dereference,end_dereference)\r
+ {\r
+ from.assert_valid(this);\r
+ to.assert_valid(this);\r
+ // create the new arc and link it in to the arc list\r
+ digraph_arc<NT,AT>* new_arc = new digraph_arc<NT,AT>(this, from.node(), to.node(), arc_data);\r
+ if (!m_arcs_end)\r
+ {\r
+ // insert into an empty list\r
+ m_arcs_begin = new_arc;\r
+ m_arcs_end = new_arc;\r
+ }\r
+ else\r
+ {\r
+ // insert at the end of the list\r
+ new_arc->m_prev = m_arcs_end;\r
+ m_arcs_end->m_next = new_arc;\r
+ m_arcs_end = new_arc;\r
+ }\r
+ // add this arc to the inputs and outputs of the end nodes\r
+ from.node()->m_outputs.push_back(new_arc);\r
+ to.node()->m_inputs.push_back(new_arc);\r
+ return digraph_arc_iterator<NT,AT,AT&,AT*>(new_arc);\r
+ }\r
+\r
+ template<typename NT, typename AT>\r
+ TYPENAME digraph<NT,AT>::arc_iterator digraph<NT,AT>::arc_erase(TYPENAME digraph<NT,AT>::arc_iterator iter)\r
+ throw(wrong_object,null_dereference,end_dereference)\r
+ {\r
+ iter.assert_valid(this);\r
+ // first remove this arc's pointers from the from/to nodes\r
+ for (TYPENAME std::vector<digraph_arc<NT,AT>*>::iterator i = iter.node()->m_to->m_inputs.begin(); i != iter.node()->m_to->m_inputs.end(); )\r
+ {\r
+ if (*i == iter.node())\r
+ i = iter.node()->m_to->m_inputs.erase(i);\r
+ else\r
+ i++;\r
+ }\r
+ for (TYPENAME std::vector<digraph_arc<NT,AT>*>::iterator o = iter.node()->m_from->m_outputs.begin(); o != iter.node()->m_from->m_outputs.end(); )\r
+ {\r
+ if (*o == iter.node())\r
+ o = iter.node()->m_from->m_outputs.erase(o);\r
+ else\r
+ o++;\r
+ }\r
+ // now unlink the arc from the list and delete it\r
+ if (iter.node()->m_next)\r
+ iter.node()->m_next->m_prev = iter.node()->m_prev;\r
+ if (iter.node()->m_prev)\r
+ iter.node()->m_prev->m_next = iter.node()->m_next;\r
+ digraph_arc<NT,AT>* next = iter.node()->m_next;\r
+ delete iter.node();\r
+ if (next)\r
+ return digraph_arc_iterator<NT,AT,AT&,AT*>(next);\r
+ else\r
+ return digraph_arc_iterator<NT,AT,AT&,AT*>(this);\r
+ }\r
+\r
+ template<typename NT, typename AT>\r
+ void digraph<NT,AT>::arc_clear(void)\r
+ {\r
+ for (digraph_arc_iterator<NT,AT,AT&,AT*> a = arc_begin(); a != arc_end(); )\r
+ a = arc_erase(a);\r
+ }\r
+\r
+ template<typename NT, typename AT>\r
+ TYPENAME digraph<NT,AT>::const_arc_iterator digraph<NT,AT>::arc_begin(void) const\r
+ {\r
+ if (m_arcs_begin)\r
+ return digraph_arc_iterator<NT,AT, const AT&,const AT*>(m_arcs_begin);\r
+ else\r
+ return digraph_arc_iterator<NT,AT, const AT&,const AT*>(this);\r
+ }\r
+\r
+ template<typename NT, typename AT>\r
+ TYPENAME digraph<NT,AT>::arc_iterator digraph<NT,AT>::arc_begin(void)\r
+ {\r
+ if (m_arcs_begin)\r
+ return digraph_arc_iterator<NT,AT,AT&,AT*>(m_arcs_begin);\r
+ else\r
+ return digraph_arc_iterator<NT,AT,AT&,AT*>(this);\r
+ }\r
+\r
+ template<typename NT, typename AT>\r
+ TYPENAME digraph<NT,AT>::const_arc_iterator digraph<NT,AT>::arc_end(void) const\r
+ {\r
+ return digraph_arc_iterator<NT,AT, const AT&,const AT*>(this);\r
+ }\r
+\r
+ template<typename NT, typename AT>\r
+ TYPENAME digraph<NT,AT>::arc_iterator digraph<NT,AT>::arc_end(void)\r
+ {\r
+ return digraph_arc_iterator<NT,AT,AT&,AT*>(this);\r
+ }\r
+\r
+ template<typename NT, typename AT>\r
+ TYPENAME digraph<NT,AT>::const_iterator digraph<NT,AT>::arc_from(TYPENAME digraph<NT,AT>::const_arc_iterator iter) const\r
+ throw(wrong_object,null_dereference,end_dereference)\r
+ {\r
+ iter.assert_valid(this);\r
+ return digraph_iterator<NT,AT,const NT&,const NT*>(iter.node()->m_from);\r
+ }\r
+\r
+ template<typename NT, typename AT>\r
+ TYPENAME digraph<NT,AT>::iterator digraph<NT,AT>::arc_from(TYPENAME digraph<NT,AT>::arc_iterator iter)\r
+ throw(wrong_object,null_dereference,end_dereference)\r
+ {\r
+ iter.assert_valid(this);\r
+ return digraph_iterator<NT,AT,NT&,NT*>(iter.node()->m_from);\r
+ }\r
+\r
+ template<typename NT, typename AT>\r
+ TYPENAME digraph<NT,AT>::const_iterator digraph<NT,AT>::arc_to(TYPENAME digraph<NT,AT>::const_arc_iterator iter) const\r
+ throw(wrong_object,null_dereference,end_dereference)\r
+ {\r
+ iter.assert_valid(this);\r
+ return digraph_iterator<NT,AT,const NT&,const NT*>(iter.node()->m_to);\r
+ }\r
+\r
+ template<typename NT, typename AT>\r
+ TYPENAME digraph<NT,AT>::iterator digraph<NT,AT>::arc_to(TYPENAME digraph<NT,AT>::arc_iterator iter)\r
+ throw(wrong_object,null_dereference,end_dereference)\r
+ {\r
+ iter.assert_valid(this);\r
+ return digraph_iterator<NT,AT,NT&,NT*>(iter.node()->m_to);\r
+ }\r
+\r
+ template<typename NT, typename AT>\r
+ void digraph<NT,AT>::arc_move(TYPENAME digraph<NT,AT>::arc_iterator arc,\r
+ TYPENAME digraph<NT,AT>::iterator from,\r
+ TYPENAME digraph<NT,AT>::iterator to)\r
+ throw(wrong_object,null_dereference,end_dereference)\r
+ {\r
+ arc_move_to(arc,to);\r
+ arc_move_from(arc,from);\r
+ }\r
+\r
+ template<typename NT, typename AT>\r
+ void digraph<NT,AT>::arc_move_from(TYPENAME digraph<NT,AT>::arc_iterator arc,\r
+ TYPENAME digraph<NT,AT>::iterator from)\r
+ throw(wrong_object,null_dereference,end_dereference)\r
+ {\r
+ arc.assert_valid(this);\r
+ from.assert_valid(this);\r
+ for (TYPENAME std::vector<digraph_arc<NT,AT>*>::iterator o = arc.node()->m_from->m_outputs.begin(); o != arc.node()->m_from->m_outputs.end(); )\r
+ {\r
+ if (*o == arc.node())\r
+ o = arc.node()->m_from->m_outputs.erase(o);\r
+ else\r
+ o++;\r
+ }\r
+ from.node()->m_outputs.push_back(arc.node());\r
+ arc.node()->m_from = from.node();\r
+ }\r
+\r
+ template<typename NT, typename AT>\r
+ void digraph<NT,AT>::arc_move_to(TYPENAME digraph<NT,AT>::arc_iterator arc,\r
+ TYPENAME digraph<NT,AT>::iterator to)\r
+ throw(wrong_object,null_dereference,end_dereference)\r
+ {\r
+ arc.assert_valid(this);\r
+ to.assert_valid(this);\r
+ for (TYPENAME std::vector<digraph_arc<NT,AT>*>::iterator i = arc.node()->m_to->m_inputs.begin(); i != arc.node()->m_to->m_inputs.end(); )\r
+ {\r
+ if (*i == arc.node())\r
+ i = arc.node()->m_to->m_inputs.erase(i);\r
+ else\r
+ i++;\r
+ }\r
+ to.node()->m_inputs.push_back(arc.node());\r
+ arc.node()->m_to = to.node();\r
+ }\r
+\r
+ template<typename NT, typename AT>\r
+ void digraph<NT,AT>::arc_flip(TYPENAME digraph<NT,AT>::arc_iterator arc)\r
+ throw(wrong_object,null_dereference,end_dereference)\r
+ {\r
+ arc_move(arc,arc_to(arc),arc_from(arc));\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // Adjacency Algorithms\r
+\r
+ template<typename NT, typename AT>\r
+ bool digraph<NT,AT>::adjacent(TYPENAME digraph<NT,AT>::const_iterator from,\r
+ TYPENAME digraph<NT,AT>::const_iterator to) const\r
+ throw(wrong_object,null_dereference,end_dereference)\r
+ {\r
+ return adjacent_arc(from,to) != arc_end();\r
+ }\r
+\r
+ template<typename NT, typename AT>\r
+ bool digraph<NT,AT>::adjacent(TYPENAME digraph<NT,AT>::iterator from,\r
+ TYPENAME digraph<NT,AT>::iterator to)\r
+ throw(wrong_object,null_dereference,end_dereference)\r
+ {\r
+ return adjacent_arc(from,to) != arc_end();\r
+ }\r
+\r
+ template<typename NT, typename AT>\r
+ TYPENAME digraph<NT,AT>::const_arc_iterator digraph<NT,AT>::adjacent_arc(TYPENAME digraph<NT,AT>::const_iterator from,\r
+ TYPENAME digraph<NT,AT>::const_iterator to) const\r
+ throw(wrong_object,null_dereference,end_dereference)\r
+ {\r
+ from.assert_valid(this);\r
+ to.assert_valid(this);\r
+ for (unsigned arc = 0; arc < fanout(from); arc++)\r
+ {\r
+ if (arc_to(output(from, arc)) == to)\r
+ return output(from,arc);\r
+ }\r
+ return arc_end();\r
+ }\r
+\r
+ template<typename NT, typename AT>\r
+ TYPENAME digraph<NT,AT>::arc_iterator digraph<NT,AT>::adjacent_arc(TYPENAME digraph<NT,AT>::iterator from,\r
+ TYPENAME digraph<NT,AT>::iterator to)\r
+ throw(wrong_object,null_dereference,end_dereference)\r
+ {\r
+ return adjacent_arc(from.constify(), to.constify()).deconstify();\r
+ }\r
+\r
+ template<typename NT, typename AT>\r
+ TYPENAME digraph<NT,AT>::const_arc_vector digraph<NT,AT>::adjacent_arcs(TYPENAME digraph<NT,AT>::const_iterator from,\r
+ TYPENAME digraph<NT,AT>::const_iterator to) const\r
+ throw(wrong_object,null_dereference,end_dereference)\r
+ {\r
+ from.assert_valid(this);\r
+ to.assert_valid(this);\r
+ std::vector<digraph_arc_iterator<NT,AT,const AT&,const AT*> > result;\r
+ for (unsigned arc = 0; arc < fanout(from); arc++)\r
+ {\r
+ if (arc_to(output(from, arc)) == to)\r
+ result.push_back(output(from,arc));\r
+ }\r
+ return result;\r
+ }\r
+\r
+ template<typename NT, typename AT>\r
+ TYPENAME digraph<NT,AT>::arc_vector digraph<NT,AT>::adjacent_arcs(TYPENAME digraph<NT,AT>::iterator from,\r
+ TYPENAME digraph<NT,AT>::iterator to)\r
+ throw(wrong_object,null_dereference,end_dereference)\r
+ {\r
+ return deconstify_arcs(adjacent_arcs(from.constify(), to.constify()));\r
+ }\r
+\r
+ template<typename NT, typename AT>\r
+ TYPENAME digraph<NT,AT>::const_node_vector digraph<NT,AT>::input_adjacencies(TYPENAME digraph<NT,AT>::const_iterator to) const\r
+ throw(wrong_object,null_dereference,end_dereference)\r
+ {\r
+ std::vector<digraph_iterator<NT,AT,const NT&,const NT*> > result;\r
+ for (unsigned arc = 0; arc < fanin(to); arc++)\r
+ {\r
+ digraph_iterator<NT,AT,const NT&,const NT*> from = arc_from(input(to, arc));\r
+ if (std::find(result.begin(), result.end(), from) == result.end())\r
+ result.push_back(from);\r
+ }\r
+ return result;\r
+ }\r
+\r
+ template<typename NT, typename AT>\r
+ TYPENAME digraph<NT,AT>::node_vector digraph<NT,AT>::input_adjacencies(TYPENAME digraph<NT,AT>::iterator to)\r
+ throw(wrong_object,null_dereference,end_dereference)\r
+ {\r
+ return deconstify_nodes(input_adjacencies(to.constify()));\r
+ }\r
+\r
+ template<typename NT, typename AT>\r
+ TYPENAME digraph<NT,AT>::const_node_vector digraph<NT,AT>::output_adjacencies(TYPENAME digraph<NT,AT>::const_iterator from) const\r
+ throw(wrong_object,null_dereference,end_dereference)\r
+ {\r
+ std::vector<digraph_iterator<NT,AT,const NT&,const NT*> > result;\r
+ for (unsigned arc = 0; arc < fanout(from); arc++)\r
+ {\r
+ digraph_iterator<NT,AT,const NT&,const NT*> to = arc_to(output(from, arc));\r
+ if (find(result.begin(), result.end(), to) == result.end())\r
+ result.push_back(to);\r
+ }\r
+ return result;\r
+ }\r
+\r
+ template<typename NT, typename AT>\r
+ TYPENAME digraph<NT,AT>::node_vector digraph<NT,AT>::output_adjacencies(TYPENAME digraph<NT,AT>::iterator from)\r
+ throw(wrong_object,null_dereference,end_dereference)\r
+ {\r
+ return deconstify_nodes(output_adjacencies(from.constify()));\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // Topographical Sort Algorithms\r
+\r
+ template<typename NT, typename AT>\r
+ std::pair<TYPENAME digraph<NT,AT>::const_node_vector, TYPENAME digraph<NT,AT>::const_arc_vector>\r
+ digraph<NT,AT>::sort(TYPENAME digraph<NT,AT>::arc_select_fn select) const\r
+ {\r
+ std::vector<digraph_iterator<NT,AT,const NT&,const NT*> > result;\r
+ std::vector<digraph_arc_iterator<NT,AT,const AT&,const AT*> > errors;\r
+ // build a map containing the number of fanins to each node that must be visited before this one\r
+ std::map<digraph_iterator<NT,AT,const NT&,const NT*>,unsigned> fanin_map;\r
+ for (digraph_iterator<NT,AT,const NT&,const NT*> n = begin(); n != end(); n++)\r
+ {\r
+ unsigned predecessors = 0;\r
+ // only count predecessors connected by selected arcs\r
+ for (unsigned f = 0; f < fanin(n); f++)\r
+ {\r
+ digraph_arc_iterator<NT,AT, const AT&,const AT*> input_arc = input(n,f);\r
+ digraph_iterator<NT,AT,const NT&,const NT*> predecessor = arc_from(input_arc);\r
+ if (!select || select(*this,input_arc))\r
+ predecessors++;\r
+ }\r
+ if (predecessors == 0)\r
+ {\r
+ result.push_back(n);\r
+ }\r
+ else\r
+ {\r
+ fanin_map[n] = predecessors;\r
+ }\r
+ }\r
+ // main algorithm applies the topographical sort repeatedly. For a DAG, it\r
+ // will complete first time. However, with backward arcs, the first\r
+ // iteration will fail. The algorithm then tries breaking random arcs to try\r
+ // to get an ordering.\r
+ for(unsigned i = 0; !fanin_map.empty(); )\r
+ {\r
+ // now visit each node in traversal order, decrementing the fanin count of\r
+ // all successors. As each successor's fanin count goes to zero, it is\r
+ // appended to the result.\r
+ for (; i < result.size(); i++)\r
+ {\r
+ // Note: dereferencing gives us a node iterator\r
+ digraph_iterator<NT,AT,const NT&,const NT*> current = result[i];\r
+ for (unsigned f = 0; f < fanout(current); f++)\r
+ {\r
+ // only consider successors connected by selected arcs\r
+ digraph_arc_iterator<NT,AT, const AT&,const AT*> output_arc = output(current, f);\r
+ digraph_iterator<NT,AT,const NT&,const NT*> successor = arc_to(output_arc);\r
+ if (!select || select(*this,output_arc))\r
+ {\r
+ // don't consider arcs that have been eliminated to break a loop\r
+ if (fanin_map.find(successor) != fanin_map.end())\r
+ {\r
+ --fanin_map[successor];\r
+ if ((fanin_map[successor]) == 0)\r
+ {\r
+ result.push_back(successor);\r
+ fanin_map.erase(fanin_map.find(successor));\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+ if (!fanin_map.empty())\r
+ {\r
+ // there must be backward arcs preventing completion\r
+ // try removing arcs from the sort to get a partial ordering containing all the nodes\r
+\r
+ // select an arc that is still relevant to the sort and break it\r
+ // first select a node that has non-zero fanin and its predecessor that has non-zero fanin\r
+ digraph_iterator<NT,AT,const NT&,const NT*> stuck_node = fanin_map.begin()->first;\r
+ for (unsigned f = 0; f < fanin(stuck_node); f++)\r
+ {\r
+ // now successively remove input arcs that are still part of the sort until the fanin reduces to zero\r
+ // first find a relevant arc - this must be a selected arc that has not yet been traversed by the first half of the algorithm\r
+ digraph_arc_iterator<NT,AT, const AT&,const AT*> input_arc = input(stuck_node, f);\r
+ if (!select || select(*this,input_arc))\r
+ {\r
+ digraph_iterator<NT,AT,const NT&,const NT*> predecessor = arc_from(input_arc);\r
+ if (fanin_map.find(predecessor) != fanin_map.end())\r
+ {\r
+ // found the right combination - remove this arc and then drop out of the fanin loop to restart the outer sort loop\r
+ errors.push_back(input_arc);\r
+ --fanin_map[stuck_node];\r
+ if ((fanin_map[stuck_node]) == 0)\r
+ {\r
+ result.push_back(stuck_node);\r
+ fanin_map.erase(fanin_map.find(stuck_node));\r
+ break;\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+ return std::make_pair(result,errors);\r
+ }\r
+\r
+ template<typename NT, typename AT>\r
+ std::pair<TYPENAME digraph<NT,AT>::node_vector, TYPENAME digraph<NT,AT>::arc_vector>\r
+ digraph<NT,AT>::sort(TYPENAME digraph<NT,AT>::arc_select_fn select)\r
+ {\r
+ std::pair<std::vector<digraph_iterator<NT,AT,const NT&,const NT*> >,\r
+ std::vector<digraph_arc_iterator<NT,AT,const AT&,const AT*> > > const_result =\r
+ const_cast<const digraph<NT,AT>*>(this)->sort(select);\r
+\r
+ std::pair<std::vector<digraph_iterator<NT,AT,NT&,NT*> >,\r
+ std::vector<digraph_arc_iterator<NT,AT,AT&,AT*> > > result =\r
+ std::make_pair(deconstify_nodes(const_result.first),deconstify_arcs(const_result.second));\r
+ return result;\r
+ }\r
+\r
+ template<typename NT, typename AT>\r
+ TYPENAME digraph<NT,AT>::const_node_vector digraph<NT,AT>::dag_sort(TYPENAME digraph<NT,AT>::arc_select_fn select) const\r
+ {\r
+ std::pair<std::vector<digraph_iterator<NT,AT,const NT&,const NT*> >,\r
+ std::vector<digraph_arc_iterator<NT,AT,const AT&,const AT*> > > result = sort(select);\r
+ if (result.second.empty()) return result.first;\r
+ return std::vector<digraph_iterator<NT,AT,const NT&,const NT*> >();\r
+ }\r
+\r
+ template<typename NT, typename AT>\r
+ TYPENAME digraph<NT,AT>::node_vector digraph<NT,AT>::dag_sort(TYPENAME digraph<NT,AT>::arc_select_fn select)\r
+ {\r
+ return deconstify_nodes(const_cast<const digraph<NT,AT>*>(this)->dag_sort(select));\r
+ }\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // Path Algorithms\r
+\r
+ template<typename NT, typename AT>\r
+ bool digraph<NT,AT>::path_exists_r(TYPENAME digraph<NT,AT>::const_iterator from,\r
+ TYPENAME digraph<NT,AT>::const_iterator to,\r
+ TYPENAME digraph<NT,AT>::const_iterator_set& visited,\r
+ TYPENAME digraph<NT,AT>::arc_select_fn select) const\r
+ throw(wrong_object,null_dereference,end_dereference)\r
+ {\r
+ // Recursive part of the digraph::path_exists function. This is based on a\r
+ // depth first search algorithm and stops the moment it finds a path\r
+ // regardless of its length. Simply traverse every output and recurse on that\r
+ // node until we find the to node or run out of things to recurse on. However,\r
+ // to avoid infinite recursion due to cycles in the graph, I need to maintain\r
+ // a set of visited nodes. The visited set is updated when a candidate is\r
+ // found but tested before the recursion on the candidate so that the number of\r
+ // function calls is minimised.\r
+ for (unsigned i = 0; i < fanout(from); i++)\r
+ {\r
+ digraph_arc_iterator<NT,AT, const AT&,const AT*> arc = output(from,i);\r
+ if (!select || select(*this, arc))\r
+ {\r
+ digraph_iterator<NT,AT,const NT&,const NT*> node = arc_to(arc);\r
+ // if the node is the target, return immediately\r
+ if (node == to) return true;\r
+ // update the visited set and give up if the insert fails, which indicates that the node has already been visited\r
+ if (!(visited.insert(node).second)) return false;\r
+ // now recurse - a path exists from from to to if a path exists from an adjacent node to to\r
+ if (path_exists_r(node,to,visited,select)) return true;\r
+ }\r
+ }\r
+ return false;\r
+ }\r
+\r
+ template<typename NT, typename AT>\r
+ bool digraph<NT,AT>::path_exists(TYPENAME digraph<NT,AT>::const_iterator from,\r
+ TYPENAME digraph<NT,AT>::const_iterator to, \r
+ TYPENAME digraph<NT,AT>::arc_select_fn select) const\r
+ throw(wrong_object,null_dereference,end_dereference)\r
+ {\r
+ // set up the recursion with its initial visited set and then recurse\r
+ std::set<digraph_iterator<NT,AT,const NT&,const NT*> > visited;\r
+ visited.insert(from);\r
+ return path_exists_r(from, to, visited, select);\r
+ }\r
+\r
+ template<typename NT, typename AT>\r
+ bool digraph<NT,AT>::path_exists(TYPENAME digraph<NT,AT>::iterator from,\r
+ TYPENAME digraph<NT,AT>::iterator to,\r
+ TYPENAME digraph<NT,AT>::arc_select_fn select)\r
+ throw(wrong_object,null_dereference,end_dereference)\r
+ {\r
+ return path_exists(from.constify(), to.constify(), select);\r
+ }\r
+\r
+ template<typename NT, typename AT>\r
+ void digraph<NT,AT>::all_paths_r(TYPENAME digraph<NT,AT>::const_iterator from,\r
+ TYPENAME digraph<NT,AT>::const_iterator to,\r
+ TYPENAME digraph<NT,AT>::const_arc_vector& so_far,\r
+ TYPENAME digraph<NT,AT>::const_path_vector& result,\r
+ TYPENAME digraph<NT,AT>::arc_select_fn select) const\r
+ throw(wrong_object,null_dereference,end_dereference)\r
+ {\r
+ // This is the recursive part of the all_paths function. The field so_far\r
+ // contains the path so far so that when 'to' is reached, the path is\r
+ // complete. It serves the same purpose as the visited set in the path_exists\r
+ // function except that it also preserves the path order. It also serves the\r
+ // purpose of detecting cycles and thus stopping infinite recursion. Every\r
+ // time the recursion reaches the to node, a copy of so_far is appended to the\r
+ // path set.\r
+ for (unsigned i = 0; i < fanout(from); i++)\r
+ {\r
+ digraph_arc_iterator<NT,AT, const AT&,const AT*> candidate = output(from,i);\r
+ // assert_valid that the arc is selected and then assert_valid that the candidate has not\r
+ // been visited on this path and only allow further recursion if it hasn't\r
+ if ((!select || select(*this, candidate)) && std::find(so_far.begin(), so_far.end(), candidate) == so_far.end())\r
+ {\r
+ // extend the path tracing the route to this arc\r
+ so_far.push_back(candidate);\r
+ // if the candidate arc points to the target, update the result set and prevent further recursion, otherwise recurse\r
+ if (arc_to(candidate) == to)\r
+ result.push_back(so_far);\r
+ else\r
+ all_paths_r(arc_to(candidate),to,so_far,result,select);\r
+ so_far.pop_back();\r
+ }\r
+ }\r
+ }\r
+\r
+ template<typename NT, typename AT>\r
+ TYPENAME digraph<NT,AT>::const_path_vector \r
+ digraph<NT,AT>::all_paths(TYPENAME digraph<NT,AT>::const_iterator from, \r
+ TYPENAME digraph<NT,AT>::const_iterator to,\r
+ TYPENAME digraph<NT,AT>::arc_select_fn select) const\r
+ throw(wrong_object,null_dereference,end_dereference)\r
+ {\r
+ // set up the recursion with empty data fields and then recurse\r
+ std::vector<std::vector<digraph_arc_iterator<NT,AT,const AT&,const AT*> > > result;\r
+ std::vector<digraph_arc_iterator<NT,AT,const AT&,const AT*> > so_far;\r
+ all_paths_r(from, to, so_far, result, select);\r
+ return result;\r
+ }\r
+\r
+ template<typename NT, typename AT>\r
+ TYPENAME digraph<NT,AT>::path_vector\r
+ digraph<NT,AT>::all_paths(TYPENAME digraph<NT,AT>::iterator from, \r
+ TYPENAME digraph<NT,AT>::iterator to,\r
+ TYPENAME digraph<NT,AT>::arc_select_fn select)\r
+ throw(wrong_object,null_dereference,end_dereference)\r
+ {\r
+ return deconstify_paths(all_paths(from.constify(), to.constify(), select));\r
+ }\r
+\r
+ template<typename NT, typename AT>\r
+ void digraph<NT,AT>::reachable_nodes_r(TYPENAME digraph<NT,AT>::const_iterator from,\r
+ TYPENAME digraph<NT,AT>::const_iterator_set& visited,\r
+ TYPENAME digraph<NT,AT>::arc_select_fn select) const\r
+ throw(wrong_object,null_dereference,end_dereference)\r
+ {\r
+ // The recursive part of the reachable_nodes function.\r
+ // This is a depth-first traversal again but this time it carries on to find all the reachable nodes\r
+ // Just keep recursing on all the adjacent nodes of each node, skipping already visited nodes to avoid cycles\r
+ for (unsigned i = 0; i < fanout(from); i++)\r
+ {\r
+ digraph_arc_iterator<NT,AT, const AT&,const AT*> arc = output(from,i);\r
+ if (!select || select(*this,arc))\r
+ {\r
+ digraph_iterator<NT,AT,const NT&,const NT*> candidate = arc_to(arc);\r
+ if (visited.insert(candidate).second)\r
+ reachable_nodes_r(candidate,visited,select);\r
+ }\r
+ }\r
+ }\r
+\r
+ template<typename NT, typename AT>\r
+ TYPENAME digraph<NT,AT>::const_node_vector\r
+ digraph<NT,AT>::reachable_nodes(TYPENAME digraph<NT,AT>::const_iterator from,\r
+ TYPENAME digraph<NT,AT>::arc_select_fn select) const\r
+ throw(wrong_object,null_dereference,end_dereference)\r
+ {\r
+ // seed the recursion, marking the starting node as already visited\r
+ std::set<digraph_iterator<NT,AT,const NT&,const NT*> > visited;\r
+ visited.insert(from);\r
+ reachable_nodes_r(from, visited, select);\r
+ // convert the visited set into the required output form\r
+ // exclude the starting node\r
+ std::vector<digraph_iterator<NT,AT,const NT&,const NT*> > result;\r
+ for (TYPENAME std::set<digraph_iterator<NT,AT,const NT&,const NT*> >::iterator i = visited.begin(); i != visited.end(); i++)\r
+ if (*i != from)\r
+ result.push_back(*i);\r
+ return result;\r
+ }\r
+\r
+ template<typename NT, typename AT>\r
+ TYPENAME digraph<NT,AT>::node_vector\r
+ digraph<NT,AT>::reachable_nodes(TYPENAME digraph<NT,AT>::iterator from,\r
+ TYPENAME digraph<NT,AT>::arc_select_fn select)\r
+ throw(wrong_object,null_dereference,end_dereference)\r
+ {\r
+ return deconstify_nodes(reachable_nodes(from.constify(), select));\r
+ }\r
+\r
+ template<typename NT, typename AT>\r
+ void digraph<NT,AT>::reaching_nodes_r(TYPENAME digraph<NT,AT>::const_iterator to,\r
+ TYPENAME digraph<NT,AT>::const_iterator_set& visited,\r
+ TYPENAME digraph<NT,AT>::arc_select_fn select) const\r
+ throw(wrong_object,null_dereference,end_dereference)\r
+ {\r
+ // The recursive part of the reaching_nodes function.\r
+ // Just like the reachable_nodes_r function but it goes backwards\r
+ for (unsigned i = 0; i < fanin(to); i++)\r
+ {\r
+ digraph_arc_iterator<NT,AT, const AT&,const AT*> arc = input(to,i);\r
+ if (!select || select(*this,arc))\r
+ {\r
+ digraph_iterator<NT,AT,const NT&,const NT*> candidate = arc_from(input(to,i));\r
+ if (visited.insert(candidate).second)\r
+ reaching_nodes_r(candidate,visited,select);\r
+ }\r
+ }\r
+ }\r
+\r
+ template<typename NT, typename AT>\r
+ TYPENAME digraph<NT,AT>::const_node_vector\r
+ digraph<NT,AT>::reaching_nodes(TYPENAME digraph<NT,AT>::const_iterator to,\r
+ TYPENAME digraph<NT,AT>::arc_select_fn select) const\r
+ throw(wrong_object,null_dereference,end_dereference)\r
+ {\r
+ // seed the recursion, marking the starting node as already visited\r
+ std::set<digraph_iterator<NT,AT,const NT&,const NT*> > visited;\r
+ visited.insert(to);\r
+ reaching_nodes_r(to,visited,select);\r
+ // convert the visited set into the required output form\r
+ // exclude the end node\r
+ std::vector<digraph_iterator<NT,AT,const NT&,const NT*> > result;\r
+ for (TYPENAME std::set<digraph_iterator<NT,AT,const NT&,const NT*> >::iterator i = visited.begin(); i != visited.end(); i++)\r
+ if (*i != to)\r
+ result.push_back(*i);\r
+ return result;\r
+ }\r
+\r
+ template<typename NT, typename AT>\r
+ TYPENAME digraph<NT,AT>::node_vector\r
+ digraph<NT,AT>::reaching_nodes(TYPENAME digraph<NT,AT>::iterator to,\r
+ TYPENAME digraph<NT,AT>::arc_select_fn select)\r
+ throw(wrong_object,null_dereference,end_dereference)\r
+ {\r
+ return deconstify_nodes(reaching_nodes(to.constify(),select));\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // Shortest Path Algorithms\r
+\r
+ template<typename NT, typename AT>\r
+ TYPENAME digraph<NT,AT>::const_arc_vector\r
+ digraph<NT,AT>::shortest_path(TYPENAME digraph<NT,AT>::const_iterator from,\r
+ TYPENAME digraph<NT,AT>::const_iterator to,\r
+ TYPENAME digraph<NT,AT>::arc_select_fn select) const\r
+ throw(wrong_object,null_dereference,end_dereference)\r
+ {\r
+ std::vector<std::vector<digraph_arc_iterator<NT,AT,const AT&,const AT*> > > paths = all_paths(from,to,select);\r
+ std::vector<digraph_arc_iterator<NT,AT,const AT&,const AT*> > shortest;\r
+ for (TYPENAME std::vector<std::vector<digraph_arc_iterator<NT,AT,const AT&,const AT*> > >::iterator i = paths.begin(); i != paths.end(); i++)\r
+ if (shortest.empty() || i->size() < shortest.size())\r
+ shortest = *i;\r
+ return shortest;\r
+ }\r
+\r
+ template<typename NT, typename AT>\r
+ TYPENAME digraph<NT,AT>::arc_vector\r
+ digraph<NT,AT>::shortest_path(TYPENAME digraph<NT,AT>::iterator from, \r
+ TYPENAME digraph<NT,AT>::iterator to,\r
+ TYPENAME digraph<NT,AT>::arc_select_fn select)\r
+ throw(wrong_object,null_dereference,end_dereference)\r
+ {\r
+ return deconstify_arcs(shortest_path(from.constify(),to.constify(),select));\r
+ }\r
+\r
+ template<typename NT, typename AT>\r
+ TYPENAME digraph<NT,AT>::const_path_vector\r
+ digraph<NT,AT>::shortest_paths(TYPENAME digraph<NT,AT>::const_iterator from,\r
+ TYPENAME digraph<NT,AT>::arc_select_fn select) const\r
+ throw(wrong_object,null_dereference,end_dereference)\r
+ {\r
+ from.assert_valid(this);\r
+ // This is an unweighted shortest path algorithm based on the algorithm from\r
+ // Weiss's book. This is essentially a breadth-first traversal or graph\r
+ // colouring algorithm. It is an iterative algorithm, so no recursion here! It\r
+ // works by creating a node queue initialised with the starting node. It then\r
+ // consumes the queue from front to back. For each node, it finds the\r
+ // successors and appends them to the queue. If a node is already 'known' it\r
+ // is not added - this avoids cycles. Thus the queue insert ordering\r
+ // represents the breadth-first ordering. On the way it creates a map of\r
+ // visited nodes. This is a map not a set because it also stores the arc that\r
+ // nominated this node as a shortest path. The full path can then be recreated\r
+ // from the map by just walking back through the predecessors. The depth (or\r
+ // colour) can be determined by the path length.\r
+ std::vector<std::vector<digraph_arc_iterator<NT,AT,const AT&,const AT*> > > result;\r
+ // initialise the iteration by creating a queue and adding the start node\r
+ std::deque<digraph_iterator<NT,AT,const NT&,const NT*> > nodes;\r
+ nodes.push_back(from);\r
+ // Create a map to store the set of known nodes mapped to their predecessor\r
+ // arcs. Initialise it with the current node, which has no predecessor. Note\r
+ // that the algorithm uses the feature of digraph iterators that they can be\r
+ // null iterators and that all null iterators are equal.\r
+ typedef std::map<digraph_iterator<NT,AT,const NT&,const NT*>,\r
+ digraph_arc_iterator<NT,AT,const AT&,const AT*> > known_map;\r
+ known_map known;\r
+ known.insert(std::make_pair(from,digraph_arc_iterator<NT,AT, const AT&,const AT*>()));\r
+ // now the iterative part of the algorithm\r
+ while(!nodes.empty())\r
+ {\r
+ // pop the queue to get the next node to process - unfortunately the STL\r
+ // deque::pop does not return the popped value\r
+ digraph_iterator<NT,AT,const NT&,const NT*> current = nodes.front();\r
+ nodes.pop_front();\r
+ // now visit all the successors\r
+ for (unsigned i = 0; i < fanout(current); i++)\r
+ {\r
+ digraph_arc_iterator<NT,AT, const AT&,const AT*> next_arc = output(current,i);\r
+ // assert_valid whether the successor arc is a selected arc and can be part of a path\r
+ if (!select || select(*this,next_arc))\r
+ {\r
+ digraph_iterator<NT,AT,const NT&,const NT*> next = arc_to(next_arc);\r
+ // Discard any successors that are known because to be known already they\r
+ // must have another shorter path. Otherwise add the successor node to the\r
+ // queue to be visited later. To minimise the overhead of map lookup I use\r
+ // the usual trick of trying to insert the node and determining whether\r
+ // the node was known by the success or failure of the insertion - this is\r
+ // a Good STL Trick (TM).\r
+ if (known.insert(std::make_pair(next,next_arc)).second)\r
+ nodes.push_back(next);\r
+ }\r
+ }\r
+ }\r
+ // The map contains the results as an unordered set of nodes, mapped to their\r
+ // predecessor arcs and weight. This now needs to be converted into a set of\r
+ // paths. This is done by starting with a node from the map, finding its\r
+ // predecessor arc and therefore its predecessor node, looking that up in the\r
+ // map to find its predecessor and so on until the start node is reached (it\r
+ // has a null predecessor). Note that the known set includes the from node\r
+ // which does not generate a path.\r
+ for (TYPENAME known_map::iterator i = known.begin(); i != known.end(); i++)\r
+ {\r
+ if (i->first != from)\r
+ {\r
+ const_arc_vector this_path;\r
+ for (TYPENAME known_map::iterator node = i; \r
+ node->second.valid(); \r
+ node = known.find(arc_from(node->second)))\r
+ this_path.insert(this_path.begin(),node->second);\r
+ result.push_back(this_path);\r
+ }\r
+ }\r
+ return result;\r
+ }\r
+\r
+ template<typename NT, typename AT>\r
+ TYPENAME digraph<NT,AT>::path_vector\r
+ digraph<NT,AT>::shortest_paths(TYPENAME digraph<NT,AT>::iterator from,\r
+ TYPENAME digraph<NT,AT>::arc_select_fn select)\r
+ throw(wrong_object,null_dereference,end_dereference)\r
+ {\r
+ return deconstify_paths(shortest_paths(from.constify(),select));\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
-#ifndef STLPLUS_EXCEPTIONS
-#define STLPLUS_EXCEPTIONS
-////////////////////////////////////////////////////////////////////////////////
-
-// Author: Andy Rushton
-// Copyright: (c) Southampton University 1999-2004
-// (c) Andy Rushton 2004-2009
-// License: BSD License, see ../docs/license.html
-
-// The set of general exceptions thrown by STLplus components
-
-////////////////////////////////////////////////////////////////////////////////
-#include "containers_fixes.hpp"
-#include <stdexcept>
-#include <string>
-
-namespace stlplus
-{
-
- ////////////////////////////////////////////////////////////////////////////////
- // Thrown if a pointer or an iterator is dereferenced when it is null
-
- class null_dereference : public std::logic_error
- {
- public:
- null_dereference(const std::string& description) throw() :
- std::logic_error(std::string("stlplus::null_dereference: ") + description) {}
- ~null_dereference(void) throw() {}
- };
-
- ////////////////////////////////////////////////////////////////////////////////
- // Thrown if an iterator is dereferenced when it is pointing to the end element
-
- class end_dereference : public std::logic_error
- {
- public:
- end_dereference(const std::string& description) throw() :
- std::logic_error("stlplus::end_dereference: " + description) {}
- ~end_dereference(void) throw() {}
- };
-
- ////////////////////////////////////////////////////////////////////////////////
- // Thrown if an iterator is used with the wrong container. In other words, an
- // iterator is created as a pointer to a sub-object within a container. If
- // that iterator is then used with a different container, this exception is
- // thrown.
-
- class wrong_object : public std::logic_error
- {
- public:
- wrong_object(const std::string& description) throw() :
- std::logic_error("stlplus::wrong_object: " + description) {}
- ~wrong_object(void) throw() {}
- };
-
- ////////////////////////////////////////////////////////////////////////////////
- // Thrown if an attempt is made to copy an object that is uncopyable
-
- class illegal_copy : public std::logic_error
- {
- public:
- illegal_copy(const std::string& description) throw() :
- std::logic_error("stlplus::illegal_copy: " + description) {}
- ~illegal_copy(void) throw() {}
- };
-
- ////////////////////////////////////////////////////////////////////////////////
-
-} // end namespace stlplus
-
-#endif
+#ifndef STLPLUS_EXCEPTIONS\r
+#define STLPLUS_EXCEPTIONS\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+// Author: Andy Rushton\r
+// Copyright: (c) Southampton University 1999-2004\r
+// (c) Andy Rushton 2004 onwards\r
+// License: BSD License, see ../docs/license.html\r
+\r
+// The set of general exceptions thrown by STLplus components\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "containers_fixes.hpp"\r
+#include <stdexcept>\r
+#include <string>\r
+\r
+namespace stlplus\r
+{\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // Thrown if a pointer or an iterator is dereferenced when it is null\r
+\r
+ class null_dereference : public std::logic_error\r
+ {\r
+ public:\r
+ null_dereference(const std::string& description) throw() :\r
+ std::logic_error(std::string("stlplus::null_dereference: ") + description) {}\r
+ ~null_dereference(void) throw() {}\r
+ };\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // Thrown if an iterator is dereferenced when it is pointing to the end element\r
+\r
+ class end_dereference : public std::logic_error\r
+ {\r
+ public:\r
+ end_dereference(const std::string& description) throw() :\r
+ std::logic_error("stlplus::end_dereference: " + description) {}\r
+ ~end_dereference(void) throw() {}\r
+ };\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // Thrown if an iterator is used with the wrong container. In other words, an\r
+ // iterator is created as a pointer to a sub-object within a container. If\r
+ // that iterator is then used with a different container, this exception is\r
+ // thrown.\r
+\r
+ class wrong_object : public std::logic_error\r
+ {\r
+ public:\r
+ wrong_object(const std::string& description) throw() :\r
+ std::logic_error("stlplus::wrong_object: " + description) {}\r
+ ~wrong_object(void) throw() {}\r
+ };\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // Thrown if an attempt is made to copy an object that is uncopyable\r
+\r
+ class illegal_copy : public std::logic_error\r
+ {\r
+ public:\r
+ illegal_copy(const std::string& description) throw() :\r
+ std::logic_error("stlplus::illegal_copy: " + description) {}\r
+ ~illegal_copy(void) throw() {}\r
+ };\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
+\r
+#endif\r
-#ifndef STLPLUS_FOURSOME
-#define STLPLUS_FOURSOME
-////////////////////////////////////////////////////////////////////////////////
-
-// Author: Andy Rushton, from an original by Dan Milton
-// Copyright: (c) Southampton University 1999-2004
-// (c) Andy Rushton 2004-2009
-// License: BSD License, see ../docs/license.html
-
-// The next in the series pair->triple->foursome
-
-// Originally called quadruple but that clashed (as did quad) with system
-// libraries on some operating systems
-
-////////////////////////////////////////////////////////////////////////////////
-#include "containers_fixes.hpp"
-
-namespace stlplus
-{
-
- ////////////////////////////////////////////////////////////////////////////////
- // the foursome class
-
- template<typename T1, typename T2, typename T3, typename T4>
- struct foursome
- {
- typedef T1 first_type;
- typedef T2 second_type;
- typedef T3 third_type;
- typedef T4 fourth_type;
-
- T1 first;
- T2 second;
- T3 third;
- T4 fourth;
-
- foursome(void);
- foursome(const T1& p1, const T2& p2, const T3& p3, const T4& p4);
- foursome(const foursome<T1,T2,T3,T4>& t2);
- };
-
- ////////////////////////////////////////////////////////////////////////////////
- // creation
-
- template<typename T1, typename T2, typename T3, typename T4>
- foursome<T1,T2,T3,T4> make_foursome(const T1& first, const T2& second, const T3& third, const T4& fourth);
-
- ////////////////////////////////////////////////////////////////////////////////
- // comparison
-
- template<typename T1, typename T2, typename T3, typename T4>
- bool operator == (const foursome<T1,T2,T3,T4>& left, const foursome<T1,T2,T3,T4>& right);
-
- ////////////////////////////////////////////////////////////////////////////////
-
-} // end namespace stlplus
-
-#include "foursome.tpp"
-#endif
+#ifndef STLPLUS_FOURSOME\r
+#define STLPLUS_FOURSOME\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+// Author: Andy Rushton, from an original by Dan Milton\r
+// Copyright: (c) Southampton University 1999-2004\r
+// (c) Andy Rushton 2004 onwards\r
+// License: BSD License, see ../docs/license.html\r
+\r
+// The next in the series pair->triple->foursome\r
+\r
+// Originally called quadruple but that clashed (as did quad) with system\r
+// libraries on some operating systems\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "containers_fixes.hpp"\r
+\r
+namespace stlplus\r
+{\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // the foursome class\r
+\r
+ template<typename T1, typename T2, typename T3, typename T4>\r
+ struct foursome\r
+ {\r
+ typedef T1 first_type;\r
+ typedef T2 second_type;\r
+ typedef T3 third_type;\r
+ typedef T4 fourth_type;\r
+\r
+ T1 first;\r
+ T2 second;\r
+ T3 third;\r
+ T4 fourth;\r
+\r
+ foursome(void);\r
+ foursome(const T1& p1, const T2& p2, const T3& p3, const T4& p4);\r
+ foursome(const foursome<T1,T2,T3,T4>& t2);\r
+ };\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // creation\r
+\r
+ template<typename T1, typename T2, typename T3, typename T4>\r
+ foursome<T1,T2,T3,T4> make_foursome(const T1& first, const T2& second, const T3& third, const T4& fourth);\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // comparison\r
+\r
+ template<typename T1, typename T2, typename T3, typename T4>\r
+ bool operator == (const foursome<T1,T2,T3,T4>& left, const foursome<T1,T2,T3,T4>& right);\r
+ template<typename T1, typename T2, typename T3, typename T4>\r
+ bool operator < (const foursome<T1,T2,T3,T4>& left, const foursome<T1,T2,T3,T4>& right);\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
+\r
+#include "foursome.tpp"\r
+#endif\r
-////////////////////////////////////////////////////////////////////////////////
-
-// Author: Andy Rushton, from an original by Dan Milton
-// Copyright: (c) Southampton University 1999-2004
-// (c) Andy Rushton 2004-2009
-// License: BSD License, see ../docs/license.html
-
-////////////////////////////////////////////////////////////////////////////////
-
-namespace stlplus
-{
-
- ////////////////////////////////////////////////////////////////////////////////
- // the foursome class
-
- template<typename T1, typename T2, typename T3, typename T4>
- foursome<T1,T2,T3,T4>::foursome(void) :
- first(), second(), third(), fourth()
- {
- }
-
- template<typename T1, typename T2, typename T3, typename T4>
- foursome<T1,T2,T3,T4>::foursome(const T1& p1, const T2& p2, const T3& p3, const T4& p4) :
- first(p1), second(p2), third(p3), fourth(p4)
- {
- }
-
- template<typename T1, typename T2, typename T3, typename T4>
- foursome<T1,T2,T3,T4>::foursome(const foursome<T1,T2,T3,T4>& t2) :
- first(t2.first), second(t2.second), third(t2.third), fourth(t2.fourth)
- {
- }
-
- ////////////////////////////////////////////////////////////////////////////////
- // creation
-
- template<typename T1, typename T2, typename T3, typename T4>
- foursome<T1,T2,T3,T4> make_foursome(const T1& first, const T2& second, const T3& third, const T4& fourth)
- {
- return foursome<T1,T2,T3,T4>(first,second,third,fourth);
- }
-
- ////////////////////////////////////////////////////////////////////////////////
- // comparison
-
- template<typename T1, typename T2, typename T3, typename T4>
- bool operator == (const foursome<T1,T2,T3,T4>& left, const foursome<T1,T2,T3,T4>& right)
- {
- // foursomes are equal if all elements are equal
- return
- left.first == right.first &&
- left.second == right.second &&
- left.third == right.third &&
- left.fourth == right.fourth;
- }
-
- ////////////////////////////////////////////////////////////////////////////////
-
-} // end namespace stlplus
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+// Author: Andy Rushton, from an original by Dan Milton\r
+// Copyright: (c) Southampton University 1999-2004\r
+// (c) Andy Rushton 2004 onwards\r
+// License: BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // the foursome class\r
+\r
+ template<typename T1, typename T2, typename T3, typename T4>\r
+ foursome<T1,T2,T3,T4>::foursome(void) :\r
+ first(), second(), third(), fourth()\r
+ {\r
+ }\r
+\r
+ template<typename T1, typename T2, typename T3, typename T4>\r
+ foursome<T1,T2,T3,T4>::foursome(const T1& p1, const T2& p2, const T3& p3, const T4& p4) :\r
+ first(p1), second(p2), third(p3), fourth(p4)\r
+ {\r
+ }\r
+\r
+ template<typename T1, typename T2, typename T3, typename T4>\r
+ foursome<T1,T2,T3,T4>::foursome(const foursome<T1,T2,T3,T4>& t2) :\r
+ first(t2.first), second(t2.second), third(t2.third), fourth(t2.fourth)\r
+ {\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // creation\r
+\r
+ template<typename T1, typename T2, typename T3, typename T4>\r
+ foursome<T1,T2,T3,T4> make_foursome(const T1& first, const T2& second, const T3& third, const T4& fourth)\r
+ {\r
+ return foursome<T1,T2,T3,T4>(first,second,third,fourth);\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // comparison\r
+\r
+ template<typename T1, typename T2, typename T3, typename T4>\r
+ bool operator == (const foursome<T1,T2,T3,T4>& left, const foursome<T1,T2,T3,T4>& right)\r
+ {\r
+ // foursomes are equal if all elements are equal\r
+ return\r
+ left.first == right.first &&\r
+ left.second == right.second &&\r
+ left.third == right.third &&\r
+ left.fourth == right.fourth;\r
+ }\r
+\r
+ template<typename T1, typename T2, typename T3, typename T4>\r
+ bool operator < (const foursome<T1,T2,T3,T4>& left, const foursome<T1,T2,T3,T4>& right)\r
+ {\r
+ // use the < operator on each element\r
+ return left.first < right.first ? true :\r
+ right.first < left.first ? false :\r
+ left.second < right.second ? true :\r
+ right.second < left.second ? false :\r
+ left.third < right.third ? true :\r
+ right.third < left.third ? false :\r
+ left.fourth < right.fourth;\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
-#ifndef STLPLUS_HASH
-#define STLPLUS_HASH
-////////////////////////////////////////////////////////////////////////////////
-
-// Author: Andy Rushton
-// Copyright: (c) Southampton University 1999-2004
-// (c) Andy Rushton 2004-2009
-// License: BSD License, see ../docs/license.html
-
-// A chained hash table using STL semantics
-
-////////////////////////////////////////////////////////////////////////////////
-#include "containers_fixes.hpp"
-#include "exceptions.hpp"
-#include "safe_iterator.hpp"
-#include <map>
-#include <iostream>
-
-namespace stlplus
-{
-
- ////////////////////////////////////////////////////////////////////////////////
- // internals
-
- template<typename K, typename T, class H, class E> class hash;
- template<typename K, typename T, class H, class E> class hash_element;
-
- ////////////////////////////////////////////////////////////////////////////////
- // iterator class
-
- template<typename K, typename T, class H, class E, typename V>
- class hash_iterator : public safe_iterator<hash<K,T,H,E>,hash_element<K,T,H,E> >
- {
- public:
- friend class hash<K,T,H,E>;
-
- // local type definitions
- // an iterator points to a value whilst a const_iterator points to a const value
- typedef V value_type;
- typedef hash_iterator<K,T,H,E,std::pair<const K,T> > iterator;
- typedef hash_iterator<K,T,H,E,const std::pair<const K,T> > const_iterator;
- typedef hash_iterator<K,T,H,E,V> this_iterator;
- typedef V& reference;
- typedef V* pointer;
-
- // constructor to create a null iterator - you must assign a valid value to this iterator before using it
- // any attempt to dereference or use a null iterator is an error
- // the only valid thing you can do is assign an iterator to it
- hash_iterator(void);
- ~hash_iterator(void);
-
- // Type conversion methods allow const_iterator and iterator to be converted
- // convert an iterator/const_iterator to a const_iterator
- const_iterator constify(void) const;
- // convert an iterator/const_iterator to an iterator
- iterator deconstify(void) const;
-
- // increment operators used to step through the set of all values in a hash
- // it is only legal to increment a valid iterator
- // there's no decrement - I've only implemented this as a unidirectional iterator
- // pre-increment
- this_iterator& operator ++ (void)
- throw(null_dereference,end_dereference);
- // post-increment
- this_iterator operator ++ (int)
- throw(null_dereference,end_dereference);
-
- // test useful for testing whether iteration has completed
- bool operator == (const this_iterator& r) const;
- bool operator != (const this_iterator& r) const;
- bool operator < (const this_iterator& r) const;
-
- // access the value - a const_iterator gives you a const value, an iterator a non-const value
- // it is illegal to dereference an invalid (i.e. null or end) iterator
- reference operator*(void) const
- throw(null_dereference,end_dereference);
- pointer operator->(void) const
- throw(null_dereference,end_dereference);
-
- private:
- friend class hash_element<K,T,H,E>;
-
- // constructor used by hash to create a non-null iterator
- // you cannot create a valid iterator except by calling a hash method that returns one
- explicit hash_iterator(hash_element<K,T,H,E>* element);
- // constructor used to create an end iterator
- explicit hash_iterator(const hash<K,T,H,E>* owner);
- // used to create an alias of an iterator
- explicit hash_iterator(const safe_iterator<hash<K,T,H,E>, hash_element<K,T,H,E> >& iterator);
- };
-
- ////////////////////////////////////////////////////////////////////////////////
- // Hash class
- // K = key type
- // T = value type
- // H = hash function object with the profile 'unsigned H(const K&)'
- // E = equal function object with profile 'bool E(const K&, const K&)' defaults to equal_to which in turn calls '=='
-
- template<typename K, typename T, class H, class E = std::equal_to<K> >
- class hash
- {
- public:
- typedef unsigned size_type;
- typedef K key_type;
- typedef T data_type;
- typedef T mapped_type;
- typedef std::pair<const K, T> value_type;
- typedef hash_iterator<K,T,H,E,value_type> iterator;
- typedef hash_iterator<K,T,H,E,const value_type> const_iterator;
-
- // construct a hash table with specified number of bins
- // the default 0 bins means leave it to the table to decide
- // specifying 0 bins also enables auto-rehashing, otherwise auto-rehashing defaults off
- hash(unsigned bins = 0);
- ~hash(void);
-
- // copy and equality copy the data elements but not the size of the copied table
- hash(const hash&);
- hash& operator = (const hash&);
-
- // test for an empty table and for the size of a table
- // efficient because the size is stored separately from the table contents
- bool empty(void) const;
- unsigned size(void) const;
-
- // test for equality - two hashes are equal if they contain equal values
- bool operator == (const hash&) const;
- bool operator != (const hash&) const;
-
- // switch auto-rehash on
- void auto_rehash(void);
- // switch auto-rehash off
- void manual_rehash(void);
- // force a rehash now
- // default of 0 means implement built-in size calculation for rehashing (recommended - it doubles the number of bins)
- void rehash(unsigned bins = 0);
- // test the loading ratio, which is the size divided by the number of bins
- // use this if you are doing your own rehashing
- // the recommendation is to double the bins when the loading exceeds 0.5 which is what auto-rehashing does
- float loading(void) const;
-
- // test for the presence of a key
- bool present(const K& key) const;
- // provide map equivalent key count function (0 or 1, as not a multimap)
- size_type count(const K& key) const;
-
- // insert a new key/data pair - replaces any previous value for this key
- iterator insert(const K& key, const T& data);
- // insert a copy of the pair into the table (std::map compatible)
- std::pair<iterator, bool> insert(const value_type& value);
- // insert a new key and return the iterator so that the data can be filled in
- iterator insert(const K& key);
-
- // remove a key/data pair from the hash table
- // as in map, this returns the number of elements erased
- size_type erase(const K& key);
- // remove an element from the hash table using an iterator
- // as in map, returns an iterator to the next element
- iterator erase(iterator it);
- // remove all elements from the hash table
- void erase(void);
- // map equivalent of above
- void clear(void);
-
- // find a key and return an iterator to it
- // The iterator is like a pointer to a pair<const K,T>
- // end() is returned if the find fails
- const_iterator find(const K& key) const;
- iterator find(const K& key);
-
- // returns the data corresponding to the key
- // const version is used for const hashes and cannot change the hash, so failure causes an exception
- // non-const version is for non-const hashes and is like map - it creates a new key/data pair if find fails
- const T& operator[] (const K& key) const throw(std::out_of_range);
- T& operator[] (const K& key);
-
- // iterators allow the hash table to be traversed
- // iterators remain valid unless an item is removed or unless a rehash happens
- const_iterator begin(void) const;
- iterator begin(void);
- const_iterator end(void) const;
- iterator end(void);
-
- // diagnostic report shows the number of items in each bin so can be used
- // to diagnose effectiveness of hash functions
- void debug_report(std::ostream&) const;
-
- // internals
- private:
- friend class hash_element<K,T,H,E>;
- friend class hash_iterator<K,T,H,E,std::pair<const K,T> >;
- friend class hash_iterator<K,T,H,E,const std::pair<const K,T> >;
-
- unsigned m_rehash;
- unsigned m_bins;
- unsigned m_size;
- hash_element<K,T,H,E>** m_values;
- };
-
- ////////////////////////////////////////////////////////////////////////////////
-
-} // end namespace stlplus
-
-#include "hash.tpp"
-#endif
+#ifndef STLPLUS_HASH\r
+#define STLPLUS_HASH\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+// Author: Andy Rushton\r
+// Copyright: (c) Southampton University 1999-2004\r
+// (c) Andy Rushton 2004 onwards\r
+// License: BSD License, see ../docs/license.html\r
+\r
+// A chained hash table using STL semantics\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "containers_fixes.hpp"\r
+#include "exceptions.hpp"\r
+#include "safe_iterator.hpp"\r
+#include <map>\r
+#include <iostream>\r
+\r
+namespace stlplus\r
+{\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // internals\r
+\r
+ template<typename K, typename T, class H, class E> class hash;\r
+ template<typename K, typename T, class H, class E> class hash_element;\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // iterator class\r
+\r
+ template<typename K, typename T, class H, class E, typename V>\r
+ class hash_iterator : public safe_iterator<hash<K,T,H,E>,hash_element<K,T,H,E> >\r
+ {\r
+ public:\r
+ friend class hash<K,T,H,E>;\r
+\r
+ // local type definitions\r
+ // an iterator points to a value whilst a const_iterator points to a const value\r
+ typedef V value_type;\r
+ typedef hash_iterator<K,T,H,E,std::pair<const K,T> > iterator;\r
+ typedef hash_iterator<K,T,H,E,const std::pair<const K,T> > const_iterator;\r
+ typedef hash_iterator<K,T,H,E,V> this_iterator;\r
+ typedef V& reference;\r
+ typedef V* pointer;\r
+\r
+ // constructor to create a null iterator - you must assign a valid value to this iterator before using it\r
+ // any attempt to dereference or use a null iterator is an error\r
+ // the only valid thing you can do is assign an iterator to it\r
+ hash_iterator(void);\r
+ ~hash_iterator(void);\r
+\r
+ // Type conversion methods allow const_iterator and iterator to be converted\r
+ // convert an iterator/const_iterator to a const_iterator\r
+ const_iterator constify(void) const;\r
+ // convert an iterator/const_iterator to an iterator\r
+ iterator deconstify(void) const;\r
+\r
+ // increment operators used to step through the set of all values in a hash\r
+ // it is only legal to increment a valid iterator\r
+ // there's no decrement - I've only implemented this as a unidirectional iterator\r
+ // pre-increment\r
+ this_iterator& operator ++ (void)\r
+ throw(null_dereference,end_dereference);\r
+ // post-increment\r
+ this_iterator operator ++ (int)\r
+ throw(null_dereference,end_dereference);\r
+\r
+ // test useful for testing whether iteration has completed\r
+ bool operator == (const this_iterator& r) const;\r
+ bool operator != (const this_iterator& r) const;\r
+ bool operator < (const this_iterator& r) const;\r
+\r
+ // access the value - a const_iterator gives you a const value, an iterator a non-const value\r
+ // it is illegal to dereference an invalid (i.e. null or end) iterator\r
+ reference operator*(void) const\r
+ throw(null_dereference,end_dereference);\r
+ pointer operator->(void) const\r
+ throw(null_dereference,end_dereference);\r
+\r
+ private:\r
+ friend class hash_element<K,T,H,E>;\r
+\r
+ // constructor used by hash to create a non-null iterator\r
+ // you cannot create a valid iterator except by calling a hash method that returns one\r
+ explicit hash_iterator(hash_element<K,T,H,E>* element);\r
+ // constructor used to create an end iterator\r
+ explicit hash_iterator(const hash<K,T,H,E>* owner);\r
+ // used to create an alias of an iterator\r
+ explicit hash_iterator(const safe_iterator<hash<K,T,H,E>, hash_element<K,T,H,E> >& iterator);\r
+ };\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // Hash class\r
+ // K = key type\r
+ // T = value type\r
+ // H = hash function object with the profile 'unsigned H(const K&)'\r
+ // E = equal function object with profile 'bool E(const K&, const K&)' defaults to equal_to which in turn calls '=='\r
+\r
+ template<typename K, typename T, class H, class E = std::equal_to<K> >\r
+ class hash\r
+ {\r
+ public:\r
+ typedef unsigned size_type;\r
+ typedef K key_type;\r
+ typedef T data_type;\r
+ typedef T mapped_type;\r
+ typedef std::pair<const K, T> value_type;\r
+ typedef hash_iterator<K,T,H,E,value_type> iterator;\r
+ typedef hash_iterator<K,T,H,E,const value_type> const_iterator;\r
+\r
+ // construct a hash table with specified number of bins\r
+ // the default 0 bins means leave it to the table to decide\r
+ // specifying 0 bins also enables auto-rehashing, otherwise auto-rehashing defaults off\r
+ hash(unsigned bins = 0);\r
+ ~hash(void);\r
+\r
+ // copy and equality copy the data elements but not the size of the copied table\r
+ hash(const hash&);\r
+ hash& operator = (const hash&);\r
+\r
+ // test for an empty table and for the size of a table\r
+ // efficient because the size is stored separately from the table contents\r
+ bool empty(void) const;\r
+ unsigned size(void) const;\r
+\r
+ // test for equality - two hashes are equal if they contain equal values\r
+ bool operator == (const hash&) const;\r
+ bool operator != (const hash&) const;\r
+\r
+ // switch auto-rehash on\r
+ void auto_rehash(void);\r
+ // switch auto-rehash off\r
+ void manual_rehash(void);\r
+ // force a rehash now\r
+ // default of 0 means implement built-in size calculation for rehashing (recommended - it doubles the number of bins)\r
+ void rehash(unsigned bins = 0);\r
+ // test the loading ratio, which is the size divided by the number of bins\r
+ // use this if you are doing your own rehashing\r
+ // the recommendation is to double the bins when the loading exceeds 0.5 which is what auto-rehashing does\r
+ float loading(void) const;\r
+\r
+ // test for the presence of a key\r
+ bool present(const K& key) const;\r
+ // provide map equivalent key count function (0 or 1, as not a multimap)\r
+ size_type count(const K& key) const;\r
+\r
+ // insert a new key/data pair - replaces any previous value for this key\r
+ iterator insert(const K& key, const T& data);\r
+ // insert a copy of the pair into the table (std::map compatible)\r
+ std::pair<iterator, bool> insert(const value_type& value);\r
+ // insert a new key and return the iterator so that the data can be filled in\r
+ iterator insert(const K& key);\r
+\r
+ // remove a key/data pair from the hash table\r
+ // as in map, this returns the number of elements erased\r
+ size_type erase(const K& key);\r
+ // remove an element from the hash table using an iterator\r
+ // as in map, returns an iterator to the next element\r
+ iterator erase(iterator it);\r
+ // remove all elements from the hash table\r
+ void erase(void);\r
+ // map equivalent of above\r
+ void clear(void);\r
+\r
+ // find a key and return an iterator to it\r
+ // The iterator is like a pointer to a pair<const K,T>\r
+ // end() is returned if the find fails\r
+ const_iterator find(const K& key) const;\r
+ iterator find(const K& key);\r
+\r
+ // returns the data corresponding to the key\r
+ // const version is used for const hashes and cannot change the hash, so failure causes an exception\r
+ // non-const version is for non-const hashes and is like map - it creates a new key/data pair if find fails\r
+ const T& operator[] (const K& key) const throw(std::out_of_range);\r
+ T& operator[] (const K& key);\r
+\r
+ // iterators allow the hash table to be traversed\r
+ // iterators remain valid unless an item is removed or unless a rehash happens\r
+ const_iterator begin(void) const;\r
+ iterator begin(void);\r
+ const_iterator end(void) const;\r
+ iterator end(void);\r
+\r
+ // diagnostic report shows the number of items in each bin so can be used\r
+ // to diagnose effectiveness of hash functions\r
+ void debug_report(std::ostream&) const;\r
+\r
+ // internals\r
+ private:\r
+ friend class hash_element<K,T,H,E>;\r
+ friend class hash_iterator<K,T,H,E,std::pair<const K,T> >;\r
+ friend class hash_iterator<K,T,H,E,const std::pair<const K,T> >;\r
+\r
+ unsigned m_rehash;\r
+ unsigned m_bins;\r
+ unsigned m_size;\r
+ hash_element<K,T,H,E>** m_values;\r
+ };\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
+\r
+#include "hash.tpp"\r
+#endif\r
-////////////////////////////////////////////////////////////////////////////////
-
-// Author: Andy Rushton
-// Copyright: (c) Southampton University 1999-2004
-// (c) Andy Rushton 2004-2009
-// License: BSD License, see ../docs/license.html
-
-////////////////////////////////////////////////////////////////////////////////
-#include <iomanip>
-
-namespace stlplus
-{
-
- ////////////////////////////////////////////////////////////////////////////////
- // the element stored in the hash
-
- template<typename K, typename T, typename H, typename E>
- class hash_element
- {
- public:
- master_iterator<hash<K,T,H,E>, hash_element<K,T,H,E> > m_master;
- std::pair<const K, T> m_value;
- hash_element<K,T,H,E>* m_next;
- unsigned m_hash;
-
- hash_element(const hash<K,T,H,E>* owner, const K& key, const T& data, unsigned hash) :
- m_master(owner,this), m_value(key,data), m_next(0), m_hash(hash)
- {
- }
-
- hash_element(const hash<K,T,H,E>* owner, const std::pair<const K,T>& value, unsigned hash) :
- m_master(owner,this), m_value(value), m_next(0), m_hash(hash)
- {
- }
-
- ~hash_element(void)
- {
- m_next = 0;
- m_hash = 0;
- }
-
- const hash<K,T,H,E>* owner(void) const
- {
- return m_master.owner();
- }
-
- // generate the bin number from the hash value and the owner's number of bins
- unsigned bin(void) const
- {
- return m_hash % (owner()->m_bins);
- }
- };
-
- ////////////////////////////////////////////////////////////////////////////////
- // iterator
-
- // null constructor
- template<typename K, typename T, class H, class E, typename V>
- hash_iterator<K,T,H,E,V>::hash_iterator(void)
- {
- }
-
- // non-null constructor used from within the hash to construct a valid iterator
- template<typename K, typename T, class H, class E, typename V>
- hash_iterator<K,T,H,E,V>::hash_iterator(hash_element<K,T,H,E>* element) :
- safe_iterator<hash<K,T,H,E>,hash_element<K,T,H,E> >(element->m_master)
- {
- }
-
- // constructor used to create an end iterator
- template<typename K, typename T, class H, class E, typename V>
- hash_iterator<K,T,H,E,V>::hash_iterator(const hash<K,T,H,E>* owner) :
- safe_iterator<hash<K,T,H,E>,hash_element<K,T,H,E> >(owner)
- {
- }
-
- template<typename K, typename T, class H, class E, typename V>
- hash_iterator<K,T,H,E,V>::hash_iterator(const safe_iterator<hash<K,T,H,E>, hash_element<K,T,H,E> >& iterator) :
- safe_iterator<hash<K,T,H,E>,hash_element<K,T,H,E> >(iterator)
- {
- }
-
- // destructor
-
- template<typename K, typename T, class H, class E, typename V>
- hash_iterator<K,T,H,E,V>::~hash_iterator(void)
- {
- }
-
- // mode conversions
-
- template<typename K, typename T, class H, class E, typename V>
- TYPENAME hash_iterator<K,T,H,E,V>::const_iterator hash_iterator<K,T,H,E,V>::constify(void) const
- {
- return hash_iterator<K,T,H,E,const std::pair<const K,T> >(*this);
- }
-
- template<typename K, typename T, class H, class E, typename V>
- TYPENAME hash_iterator<K,T,H,E,V>::iterator hash_iterator<K,T,H,E,V>::deconstify(void) const
- {
- return hash_iterator<K,T,H,E,std::pair<const K,T> >(*this);
- }
-
- // increment operator looks for the next element in the table
- // if there isn't one, then this becomes an end() iterator with m_bin = m_bins
- template<typename K, typename T, class H, class E, typename V>
- TYPENAME hash_iterator<K,T,H,E,V>::this_iterator& hash_iterator<K,T,H,E,V>::operator ++ (void)
- throw(null_dereference,end_dereference)
- {
- this->assert_valid();
- if (this->node()->m_next)
- set(this->node()->m_next->m_master);
- else
- {
- // failing that, subsequent hash values are tried until either an element is found or there are no more bins
- // in which case it becomes an end() iterator
- hash_element<K,T,H,E>* element = 0;
- unsigned current_bin = this->node()->bin();
- for(current_bin++; !element && (current_bin < this->owner()->m_bins); current_bin++)
- element = this->owner()->m_values[current_bin];
- if (element)
- set(element->m_master);
- else
- this->set_end();
- }
- return *this;
- }
-
- // post-increment is defined in terms of pre-increment
- template<typename K, typename T, class H, class E, typename V>
- TYPENAME hash_iterator<K,T,H,E,V>::this_iterator hash_iterator<K,T,H,E,V>::operator ++ (int)
- throw(null_dereference,end_dereference)
- {
- hash_iterator<K,T,H,E,V> old(*this);
- ++(*this);
- return old;
- }
-
- // two iterators are equal if they point to the same element
- // both iterators must be non-null and belong to the same table
- template<typename K, typename T, class H, class E, typename V>
- bool hash_iterator<K,T,H,E,V>::operator == (const hash_iterator<K,T,H,E,V>& r) const
- {
- return equal(r);
- }
-
- template<typename K, typename T, class H, class E, typename V>
- bool hash_iterator<K,T,H,E,V>::operator != (const hash_iterator<K,T,H,E,V>& r) const
- {
- return !operator==(r);
- }
-
- template<typename K, typename T, class H, class E, typename V>
- bool hash_iterator<K,T,H,E,V>::operator < (const hash_iterator<K,T,H,E,V>& r) const
- {
- return compare(r) < 0;
- }
-
- // iterator dereferencing is only legal on a non-null iterator
- template<typename K, typename T, class H, class E, typename V>
- V& hash_iterator<K,T,H,E,V>::operator*(void) const
- throw(null_dereference,end_dereference)
- {
- this->assert_valid();
- return this->node()->m_value;
- }
-
- template<typename K, typename T, class H, class E, typename V>
- V* hash_iterator<K,T,H,E,V>::operator->(void) const
- throw(null_dereference,end_dereference)
- {
- return &(operator*());
- }
-
- ////////////////////////////////////////////////////////////////////////////////
- // hash
-
- // totally arbitrary initial size used for auto-rehashed tables
- static unsigned hash_default_bins = 127;
-
- // constructor
- // tests whether the user wants auto-rehash
- // sets the rehash point to be a loading of 1.0 by setting it to the number of bins
- // uses the user's size unless this is zero, in which case implement the default
-
- template<typename K, typename T, class H, class E>
- hash<K,T,H,E>::hash(unsigned bins) :
- m_rehash(bins), m_bins(bins > 0 ? bins : hash_default_bins), m_size(0), m_values(0)
- {
- m_values = new hash_element<K,T,H,E>*[m_bins];
- for (unsigned i = 0; i < m_bins; i++)
- m_values[i] = 0;
- }
-
- template<typename K, typename T, class H, class E>
- hash<K,T,H,E>::~hash(void)
- {
- // delete all the elements
- clear();
- // and delete the data structure
- delete[] m_values;
- m_values = 0;
- }
-
- // as usual, implement the copy constructor i.t.o. the assignment operator
-
- template<typename K, typename T, class H, class E>
- hash<K,T,H,E>::hash(const hash<K,T,H,E>& right) :
- m_rehash(right.m_rehash), m_bins(right.m_bins), m_size(0), m_values(0)
- {
- m_values = new hash_element<K,T,H,E>*[right.m_bins];
- // copy the rehash behaviour as well as the size
- for (unsigned i = 0; i < m_bins; i++)
- m_values[i] = 0;
- *this = right;
- }
-
- // assignment operator
- // this is done by copying the elements
- // the source and target hashes can be different sizes
- // the hash is self-copy safe, i.e. it is legal to say x = x;
-
- template<typename K, typename T, class H, class E>
- hash<K,T,H,E>& hash<K,T,H,E>::operator = (const hash<K,T,H,E>& r)
- {
- // make self-copy safe
- if (&r == this) return *this;
- // remove all the existing elements
- clear();
- // copy the elements across - remember that this is rehashing because the two
- // tables can be different sizes so there is no quick way of doing this by
- // copying the lists
- for (hash_iterator<K,T,H,E,const std::pair<const K,T> > i = r.begin(); i != r.end(); ++i)
- insert(i->first, i->second);
- return *this;
- }
-
- // number of values in the hash
- template<typename K, typename T, class H, class E>
- bool hash<K,T,H,E>::empty(void) const
- {
- return m_size == 0;
- }
-
- template<typename K, typename T, class H, class E>
- unsigned hash<K,T,H,E>::size(void) const
- {
- return m_size;
- }
-
- // equality
- template<typename K, typename T, class H, class E>
- bool hash<K,T,H,E>::operator == (const hash<K,T,H,E>& right) const
- {
- // this table is the same as the right table if they are the same table!
- if (&right == this) return true;
- // they must be the same size to be equal
- if (m_size != right.m_size) return false;
- // now every key in this must be in right and have the same data
- for (hash_iterator<K,T,H,E,const std::pair<const K,T> > i = begin(); i != end(); i++)
- {
- hash_iterator<K,T,H,E,const std::pair<const K,T> > found = right.find(i->first);
- if (found == right.end()) return false;
- if (!(i->second == found->second)) return false;
- }
- return true;
- }
-
- // set up the hash to auto-rehash at a specific size
- // setting the rehash size to 0 forces manual rehashing
- template<typename K, typename T, class H, class E>
- void hash<K,T,H,E>::auto_rehash(void)
- {
- m_rehash = m_bins;
- }
-
- template<typename K, typename T, class H, class E>
- void hash<K,T,H,E>::manual_rehash(void)
- {
- m_rehash = 0;
- }
-
- // the rehash function
- // builds a new hash table and moves the elements (without copying) from the old to the new
- // I store the un-modulused hash value in the element for more efficient rehashing
- // passing 0 to the bins parameter does auto-rehashing
- // passing any other value forces the number of bins
-
- template<typename K, typename T, class H, class E>
- void hash<K,T,H,E>::rehash(unsigned bins)
- {
- // user specified size: just take the user's value
- // auto calculate: if the load is high, increase the size; else do nothing
- unsigned new_bins = bins ? bins : m_bins;
- if (bins == 0 && m_size > 0)
- {
- // these numbers are pretty arbitrary
- // TODO - make them user-customisable?
- float load = loading();
- if (load > 2.0)
- new_bins = (unsigned)(m_bins * load);
- else if (load > 1.0)
- new_bins = m_bins * 2;
- }
- if (new_bins == m_bins) return;
- // set the new rehashing point if auto-rehashing is on
- if (m_rehash) m_rehash = new_bins;
- // move aside the old structure
- hash_element<K,T,H,E>** old_values = m_values;
- unsigned old_bins = m_bins;
- // create a replacement structure
- m_values = new hash_element<K,T,H,E>*[new_bins];
- for (unsigned i = 0; i < new_bins; i++)
- m_values[i] = 0;
- m_bins = new_bins;
- // move all the old elements across, rehashing each one
- for (unsigned j = 0; j < old_bins; j++)
- {
- while(old_values[j])
- {
- // unhook from the old structure
- hash_element<K,T,H,E>* current = old_values[j];
- old_values[j] = current->m_next;
- // rehash using the stored hash value
- unsigned bin = current->bin();
- // hook it into the new structure
- current->m_next = m_values[bin];
- m_values[bin] = current;
- }
- }
- // now delete the old structure
- delete[] old_values;
- }
-
- // the loading is the average number of elements per bin
- // this simplifies to the total elements divided by the number of bins
-
- template<typename K, typename T, class H, class E>
- float hash<K,T,H,E>::loading(void) const
- {
- return (float)m_size / (float)m_bins;
- }
-
- // remove all elements from the table
-
- template<typename K, typename T, class H, class E>
- void hash<K,T,H,E>::erase(void)
- {
- // unhook the list elements and destroy them
- for (unsigned i = 0; i < m_bins; i++)
- {
- hash_element<K,T,H,E>* current = m_values[i];
- while(current)
- {
- hash_element<K,T,H,E>* next = current->m_next;
- delete current;
- current = next;
- }
- m_values[i] = 0;
- }
- m_size = 0;
- }
-
- // test for whether a key is present in the table
-
- template<typename K, typename T, class H, class E>
- bool hash<K,T,H,E>::present(const K& key) const
- {
- return find(key) != end();
- }
-
- template<typename K, typename T, class H, class E>
- TYPENAME hash<K,T,H,E>::size_type hash<K,T,H,E>::count(const K& key) const
- {
- return present() ? 1 : 0;
- }
-
- // add a key and data element to the table - defined in terms of the general-purpose pair insert function
-
- template<typename K, typename T, class H, class E>
- TYPENAME hash<K,T,H,E>::iterator hash<K,T,H,E>::insert(const K& key, const T& data)
- {
- return insert(std::pair<const K,T>(key,data)).first;
- }
-
- // insert a key/data pair into the table
- // this removes any old value with the same key since there is no multihash functionality
-
- template<typename K, typename T, class H, class E>
- std::pair<TYPENAME hash<K,T,H,E>::iterator, bool> hash<K,T,H,E>::insert(const std::pair<const K,T>& value)
- {
- // if auto-rehash is enabled, implement the auto-rehash before inserting the new value
- // the table is rehashed if this insertion makes the loading exceed 1.0
- if (m_rehash && (m_size >= m_rehash)) rehash();
- // calculate the new hash value
- unsigned hash_value_full = H()(value.first);
- unsigned bin = hash_value_full % m_bins;
- bool inserted = true;
- // unhook any previous value with this key
- // this has been inlined from erase(key) so that the hash value is not calculated twice
- hash_element<K,T,H,E>* previous = 0;
- for (hash_element<K,T,H,E>* current = m_values[bin]; current; previous = current, current = current->m_next)
- {
- // first check the full stored hash value
- if (current->m_hash != hash_value_full) continue;
-
- // next try the equality operator
- if (!E()(current->m_value.first, value.first)) continue;
-
- // unhook this value and destroy it
- if (previous)
- previous->m_next = current->m_next;
- else
- m_values[bin] = current->m_next;
- delete current;
- m_size--;
-
- // we've overwritten a previous value
- inserted = false;
-
- // assume there can only be one match so we can give up now
- break;
- }
- // now hook in a new list element at the start of the list for this hash value
- hash_element<K,T,H,E>* new_item = new hash_element<K,T,H,E>(this, value, hash_value_full);
- new_item->m_next = m_values[bin];
- m_values[bin] = new_item;
- // increment the size count
- m_size++;
- // construct an iterator from the list node, and return whether inserted
- return std::make_pair(hash_iterator<K,T,H,E,std::pair<const K,T> >(new_item), inserted);
- }
-
- // insert a key with an empty data field ready to be filled in later
-
- template<typename K, typename T, class H, class E>
- TYPENAME hash<K,T,H,E>::iterator hash<K,T,H,E>::insert(const K& key)
- {
- return insert(key,T());
- }
-
- // remove a key from the table - return true if the key was found and removed, false if it wasn't present
-
- template<typename K, typename T, class H, class E>
- unsigned hash<K,T,H,E>::erase(const K& key)
- {
- unsigned hash_value_full = H()(key);
- unsigned bin = hash_value_full % m_bins;
- // scan the list for an element with this key
- // need to keep a previous pointer because the lists are single-linked
- hash_element<K,T,H,E>* previous = 0;
- for (hash_element<K,T,H,E>* current = m_values[bin]; current; previous = current, current = current->m_next)
- {
- // first check the full stored hash value
- if (current->m_hash != hash_value_full) continue;
-
- // next try the equality operator
- if (!E()(current->m_value.first, key)) continue;
-
- // found this key, so unhook the element from the list
- if (previous)
- previous->m_next = current->m_next;
- else
- m_values[bin] = current->m_next;
- // destroy it
- delete current;
- // remember to maintain the size count
- m_size--;
- return 1;
- }
- return 0;
- }
-
- // remove an element from the hash table using an iterator (std::map equivalent)
- template<typename K, typename T, class H, class E>
- TYPENAME hash<K,T,H,E>::iterator hash<K,T,H,E>::erase(TYPENAME hash<K,T,H,E>::iterator it)
- {
- // work out what the next iterator is in order to return it later
- TYPENAME hash<K,T,H,E>::iterator next(it);
- ++next;
- // we now need to find where this item is - made difficult by the use of
- // single-linked lists which means I have to search through the bin from
- // the top in order to unlink from the list.
- unsigned hash_value_full = it.node()->m_hash;
- unsigned bin = hash_value_full % m_bins;
- // scan the list for this element
- // need to keep a previous pointer because the lists are single-linked
- hash_element<K,T,H,E>* previous = 0;
- for (hash_element<K,T,H,E>* current = m_values[bin]; current; previous = current, current = current->m_next)
- {
- // direct test on the address of the element
- if (current != it.node()) continue;
- // found this iterator, so unhook the element from the list
- if (previous)
- previous->m_next = current->m_next;
- else
- m_values[bin] = current->m_next;
- // destroy it
- delete current;
- current = 0;
- // remember to maintain the size count
- m_size--;
- break;
- }
- return next;
- }
-
- template<typename K, typename T, class H, class E>
- void hash<K,T,H,E>::clear(void)
- {
- erase();
- }
-
- // search for a key in the table and return an iterator to it
- // if the search fails, returns an end() iterator
-
- template<typename K, typename T, class H, class E>
- TYPENAME hash<K,T,H,E>::const_iterator hash<K,T,H,E>::find(const K& key) const
- {
- // scan the list for this key's hash value for the element with a matching key
- unsigned hash_value_full = H()(key);
- unsigned bin = hash_value_full % m_bins;
- for (hash_element<K,T,H,E>* current = m_values[bin]; current; current = current->m_next)
- {
- if (current->m_hash == hash_value_full && E()(current->m_value.first, key))
- return hash_iterator<K,T,H,E,const std::pair<const K,T> >(current);
- }
- return end();
- }
-
- template<typename K, typename T, class H, class E>
- TYPENAME hash<K,T,H,E>::iterator hash<K,T,H,E>::find(const K& key)
- {
- // scan the list for this key's hash value for the element with a matching key
- unsigned hash_value_full = H()(key);
- unsigned bin = hash_value_full % m_bins;
- for (hash_element<K,T,H,E>* current = m_values[bin]; current; current = current->m_next)
- {
- if (current->m_hash == hash_value_full && E()(current->m_value.first, key))
- return hash_iterator<K,T,H,E,std::pair<const K,T> >(current);
- }
- return end();
- }
-
- // table lookup by key using the index operator[], returning a reference to the data field, not an iterator
- // this is rather like the std::map's [] operator
- // the difference is that I have a const and non-const version
- // the const version will not create the element if not present already, but the non-const version will
- // the non-const version is compatible with the behaviour of the map
-
- template<typename K, typename T, class H, class E>
- const T& hash<K,T,H,E>::operator[] (const K& key) const throw(std::out_of_range)
- {
- // this const version cannot change the hash, so has to raise an exception if the key is missing
- hash_iterator<K,T,H,E,const std::pair<const K,T> > found = find(key);
- if (found == end())
- throw std::out_of_range("key not found in stlplus::hash::operator[]");
- return found->second;
- }
-
- template<typename K, typename T, class H, class E>
- T& hash<K,T,H,E>::operator[] (const K& key)
- {
- // this non-const version can change the hash, so creates a new element if the key is missing
- hash_iterator<K,T,H,E,std::pair<const K,T> > found = find(key);
- if (found == end())
- found = insert(key);
- return found->second;
- }
-
- // iterators
-
- template<typename K, typename T, class H, class E>
- TYPENAME hash<K,T,H,E>::const_iterator hash<K,T,H,E>::begin(void) const
- {
- // find the first element
- for (unsigned bin = 0; bin < m_bins; bin++)
- if (m_values[bin])
- return hash_iterator<K,T,H,E,const std::pair<const K,T> >(m_values[bin]);
- // if the hash is empty, return the end iterator
- return end();
- }
-
- template<typename K, typename T, class H, class E>
- TYPENAME hash<K,T,H,E>::iterator hash<K,T,H,E>::begin(void)
- {
- // find the first element
- for (unsigned bin = 0; bin < m_bins; bin++)
- if (m_values[bin])
- return hash_iterator<K,T,H,E,std::pair<const K,T> >(m_values[bin]);
- // if the hash is empty, return the end iterator
- return end();
- }
-
- template<typename K, typename T, class H, class E>
- TYPENAME hash<K,T,H,E>::const_iterator hash<K,T,H,E>::end(void) const
- {
- return hash_iterator<K,T,H,E,const std::pair<const K,T> >(this);
- }
-
- template<typename K, typename T, class H, class E>
- TYPENAME hash<K,T,H,E>::iterator hash<K,T,H,E>::end(void)
- {
- return hash_iterator<K,T,H,E,std::pair<const K,T> >(this);
- }
-
- template<typename K, typename T, class H, class E>
- void hash<K,T,H,E>::debug_report(std::ostream& str) const
- {
- // calculate some stats first
- unsigned occupied = 0;
- unsigned min_in_bin = m_size;
- unsigned max_in_bin = 0;
- for (unsigned i = 0; i < m_bins; i++)
- {
- if (m_values[i]) occupied++;
- unsigned count = 0;
- for (hash_element<K,T,H,E>* item = m_values[i]; item; item = item->m_next) count++;
- if (count > max_in_bin) max_in_bin = count;
- if (count < min_in_bin) min_in_bin = count;
- }
- // now print the table
- str << "------------------------------------------------------------------------" << std::endl;
- str << "| size: " << m_size << std::endl;
- str << "| bins: " << m_bins << std::endl;
- str << "| loading: " << loading() << " ";
- if (m_rehash)
- str << "auto-rehash at " << m_rehash << std::endl;
- else
- str << "manual rehash" << std::endl;
- str << "| occupied: " << occupied
- << std::fixed << " (" << (100.0*(float)occupied/(float)m_bins) << "%)" << std::scientific
- << ", min = " << min_in_bin << ", max = " << max_in_bin << std::endl;
- str << "|-----------------------------------------------------------------------" << std::endl;
- str << "| bin 0 1 2 3 4 5 6 7 8 9" << std::endl;
- str << "| ---------------------------------------------------------------";
- for (unsigned j = 0; j < m_bins; j++)
- {
- if (j % 10 == 0)
- {
- str << std::endl;
- str << "| " << std::setw(6) << std::right << (j/10*10) << std::left << " |";
- }
- unsigned count = 0;
- for (hash_element<K,T,H,E>* item = m_values[j]; item; item = item->m_next) count++;
- if (!count)
- str << " .";
- else
- str << std::setw(6) << std::right << count << std::left;
- }
- str << std::endl;
- str << "------------------------------------------------------------------------" << std::endl;
- }
-
- ////////////////////////////////////////////////////////////////////////////////
-
-} // end namespace stlplus
-
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+// Author: Andy Rushton\r
+// Copyright: (c) Southampton University 1999-2004\r
+// (c) Andy Rushton 2004 onwards\r
+// License: BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include <iomanip>\r
+\r
+namespace stlplus\r
+{\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // the element stored in the hash\r
+\r
+ template<typename K, typename T, typename H, typename E>\r
+ class hash_element\r
+ {\r
+ public:\r
+ master_iterator<hash<K,T,H,E>, hash_element<K,T,H,E> > m_master;\r
+ std::pair<const K, T> m_value;\r
+ hash_element<K,T,H,E>* m_next;\r
+ unsigned m_hash;\r
+\r
+ hash_element(const hash<K,T,H,E>* owner, const K& key, const T& data, unsigned hash) :\r
+ m_master(owner,this), m_value(key,data), m_next(0), m_hash(hash)\r
+ {\r
+ }\r
+\r
+ hash_element(const hash<K,T,H,E>* owner, const std::pair<const K,T>& value, unsigned hash) :\r
+ m_master(owner,this), m_value(value), m_next(0), m_hash(hash)\r
+ {\r
+ }\r
+\r
+ ~hash_element(void)\r
+ {\r
+ m_next = 0;\r
+ m_hash = 0;\r
+ }\r
+\r
+ const hash<K,T,H,E>* owner(void) const\r
+ {\r
+ return m_master.owner();\r
+ }\r
+\r
+ // generate the bin number from the hash value and the owner's number of bins\r
+ unsigned bin(void) const\r
+ {\r
+ return m_hash % (owner()->m_bins);\r
+ }\r
+ };\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // iterator\r
+\r
+ // null constructor\r
+ template<typename K, typename T, class H, class E, typename V>\r
+ hash_iterator<K,T,H,E,V>::hash_iterator(void)\r
+ {\r
+ }\r
+\r
+ // non-null constructor used from within the hash to construct a valid iterator\r
+ template<typename K, typename T, class H, class E, typename V>\r
+ hash_iterator<K,T,H,E,V>::hash_iterator(hash_element<K,T,H,E>* element) :\r
+ safe_iterator<hash<K,T,H,E>,hash_element<K,T,H,E> >(element->m_master)\r
+ {\r
+ }\r
+\r
+ // constructor used to create an end iterator\r
+ template<typename K, typename T, class H, class E, typename V>\r
+ hash_iterator<K,T,H,E,V>::hash_iterator(const hash<K,T,H,E>* owner) :\r
+ safe_iterator<hash<K,T,H,E>,hash_element<K,T,H,E> >(owner)\r
+ {\r
+ }\r
+\r
+ template<typename K, typename T, class H, class E, typename V>\r
+ hash_iterator<K,T,H,E,V>::hash_iterator(const safe_iterator<hash<K,T,H,E>, hash_element<K,T,H,E> >& iterator) :\r
+ safe_iterator<hash<K,T,H,E>,hash_element<K,T,H,E> >(iterator)\r
+ {\r
+ }\r
+\r
+ // destructor\r
+\r
+ template<typename K, typename T, class H, class E, typename V>\r
+ hash_iterator<K,T,H,E,V>::~hash_iterator(void)\r
+ {\r
+ }\r
+\r
+ // mode conversions\r
+\r
+ template<typename K, typename T, class H, class E, typename V>\r
+ TYPENAME hash_iterator<K,T,H,E,V>::const_iterator hash_iterator<K,T,H,E,V>::constify(void) const\r
+ {\r
+ return hash_iterator<K,T,H,E,const std::pair<const K,T> >(*this);\r
+ }\r
+\r
+ template<typename K, typename T, class H, class E, typename V>\r
+ TYPENAME hash_iterator<K,T,H,E,V>::iterator hash_iterator<K,T,H,E,V>::deconstify(void) const\r
+ {\r
+ return hash_iterator<K,T,H,E,std::pair<const K,T> >(*this);\r
+ }\r
+\r
+ // increment operator looks for the next element in the table\r
+ // if there isn't one, then this becomes an end() iterator with m_bin = m_bins\r
+ template<typename K, typename T, class H, class E, typename V>\r
+ TYPENAME hash_iterator<K,T,H,E,V>::this_iterator& hash_iterator<K,T,H,E,V>::operator ++ (void)\r
+ throw(null_dereference,end_dereference)\r
+ {\r
+ this->assert_valid();\r
+ if (this->node()->m_next)\r
+ set(this->node()->m_next->m_master);\r
+ else\r
+ {\r
+ // failing that, subsequent hash values are tried until either an element is found or there are no more bins\r
+ // in which case it becomes an end() iterator\r
+ hash_element<K,T,H,E>* element = 0;\r
+ unsigned current_bin = this->node()->bin();\r
+ for(current_bin++; !element && (current_bin < this->owner()->m_bins); current_bin++)\r
+ element = this->owner()->m_values[current_bin];\r
+ if (element)\r
+ set(element->m_master);\r
+ else\r
+ this->set_end();\r
+ }\r
+ return *this;\r
+ }\r
+\r
+ // post-increment is defined in terms of pre-increment\r
+ template<typename K, typename T, class H, class E, typename V>\r
+ TYPENAME hash_iterator<K,T,H,E,V>::this_iterator hash_iterator<K,T,H,E,V>::operator ++ (int)\r
+ throw(null_dereference,end_dereference)\r
+ {\r
+ hash_iterator<K,T,H,E,V> old(*this);\r
+ ++(*this);\r
+ return old;\r
+ }\r
+\r
+ // two iterators are equal if they point to the same element\r
+ // both iterators must be non-null and belong to the same table\r
+ template<typename K, typename T, class H, class E, typename V>\r
+ bool hash_iterator<K,T,H,E,V>::operator == (const hash_iterator<K,T,H,E,V>& r) const\r
+ {\r
+ return equal(r);\r
+ }\r
+\r
+ template<typename K, typename T, class H, class E, typename V>\r
+ bool hash_iterator<K,T,H,E,V>::operator != (const hash_iterator<K,T,H,E,V>& r) const\r
+ {\r
+ return !operator==(r);\r
+ }\r
+\r
+ template<typename K, typename T, class H, class E, typename V>\r
+ bool hash_iterator<K,T,H,E,V>::operator < (const hash_iterator<K,T,H,E,V>& r) const\r
+ {\r
+ return compare(r) < 0;\r
+ }\r
+\r
+ // iterator dereferencing is only legal on a non-null iterator\r
+ template<typename K, typename T, class H, class E, typename V>\r
+ V& hash_iterator<K,T,H,E,V>::operator*(void) const\r
+ throw(null_dereference,end_dereference)\r
+ {\r
+ this->assert_valid();\r
+ return this->node()->m_value;\r
+ }\r
+\r
+ template<typename K, typename T, class H, class E, typename V>\r
+ V* hash_iterator<K,T,H,E,V>::operator->(void) const\r
+ throw(null_dereference,end_dereference)\r
+ {\r
+ return &(operator*());\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // hash\r
+\r
+ // totally arbitrary initial size used for auto-rehashed tables\r
+ static unsigned hash_default_bins = 127;\r
+\r
+ // constructor\r
+ // tests whether the user wants auto-rehash\r
+ // sets the rehash point to be a loading of 1.0 by setting it to the number of bins\r
+ // uses the user's size unless this is zero, in which case implement the default\r
+\r
+ template<typename K, typename T, class H, class E>\r
+ hash<K,T,H,E>::hash(unsigned bins) :\r
+ m_rehash(bins), m_bins(bins > 0 ? bins : hash_default_bins), m_size(0), m_values(0)\r
+ {\r
+ m_values = new hash_element<K,T,H,E>*[m_bins];\r
+ for (unsigned i = 0; i < m_bins; i++)\r
+ m_values[i] = 0;\r
+ }\r
+\r
+ template<typename K, typename T, class H, class E>\r
+ hash<K,T,H,E>::~hash(void)\r
+ {\r
+ // delete all the elements\r
+ clear();\r
+ // and delete the data structure\r
+ delete[] m_values;\r
+ m_values = 0;\r
+ }\r
+\r
+ // as usual, implement the copy constructor i.t.o. the assignment operator\r
+\r
+ template<typename K, typename T, class H, class E>\r
+ hash<K,T,H,E>::hash(const hash<K,T,H,E>& right) :\r
+ m_rehash(right.m_rehash), m_bins(right.m_bins), m_size(0), m_values(0)\r
+ {\r
+ m_values = new hash_element<K,T,H,E>*[right.m_bins];\r
+ // copy the rehash behaviour as well as the size\r
+ for (unsigned i = 0; i < m_bins; i++)\r
+ m_values[i] = 0;\r
+ *this = right;\r
+ }\r
+\r
+ // assignment operator\r
+ // this is done by copying the elements\r
+ // the source and target hashes can be different sizes\r
+ // the hash is self-copy safe, i.e. it is legal to say x = x;\r
+\r
+ template<typename K, typename T, class H, class E>\r
+ hash<K,T,H,E>& hash<K,T,H,E>::operator = (const hash<K,T,H,E>& r)\r
+ {\r
+ // make self-copy safe\r
+ if (&r == this) return *this;\r
+ // remove all the existing elements\r
+ clear();\r
+ // copy the elements across - remember that this is rehashing because the two\r
+ // tables can be different sizes so there is no quick way of doing this by\r
+ // copying the lists\r
+ for (hash_iterator<K,T,H,E,const std::pair<const K,T> > i = r.begin(); i != r.end(); ++i)\r
+ insert(i->first, i->second);\r
+ return *this;\r
+ }\r
+\r
+ // number of values in the hash\r
+ template<typename K, typename T, class H, class E>\r
+ bool hash<K,T,H,E>::empty(void) const\r
+ {\r
+ return m_size == 0;\r
+ }\r
+\r
+ template<typename K, typename T, class H, class E>\r
+ unsigned hash<K,T,H,E>::size(void) const\r
+ {\r
+ return m_size;\r
+ }\r
+\r
+ // equality\r
+ template<typename K, typename T, class H, class E>\r
+ bool hash<K,T,H,E>::operator == (const hash<K,T,H,E>& right) const\r
+ {\r
+ // this table is the same as the right table if they are the same table!\r
+ if (&right == this) return true;\r
+ // they must be the same size to be equal\r
+ if (m_size != right.m_size) return false;\r
+ // now every key in this must be in right and have the same data\r
+ for (hash_iterator<K,T,H,E,const std::pair<const K,T> > i = begin(); i != end(); i++)\r
+ {\r
+ hash_iterator<K,T,H,E,const std::pair<const K,T> > found = right.find(i->first);\r
+ if (found == right.end()) return false;\r
+ if (!(i->second == found->second)) return false;\r
+ }\r
+ return true;\r
+ }\r
+\r
+ // set up the hash to auto-rehash at a specific size\r
+ // setting the rehash size to 0 forces manual rehashing\r
+ template<typename K, typename T, class H, class E>\r
+ void hash<K,T,H,E>::auto_rehash(void)\r
+ {\r
+ m_rehash = m_bins;\r
+ }\r
+\r
+ template<typename K, typename T, class H, class E>\r
+ void hash<K,T,H,E>::manual_rehash(void)\r
+ {\r
+ m_rehash = 0;\r
+ }\r
+\r
+ // the rehash function\r
+ // builds a new hash table and moves the elements (without copying) from the old to the new\r
+ // I store the un-modulused hash value in the element for more efficient rehashing\r
+ // passing 0 to the bins parameter does auto-rehashing\r
+ // passing any other value forces the number of bins\r
+\r
+ template<typename K, typename T, class H, class E>\r
+ void hash<K,T,H,E>::rehash(unsigned bins)\r
+ {\r
+ // user specified size: just take the user's value\r
+ // auto calculate: if the load is high, increase the size; else do nothing\r
+ unsigned new_bins = bins ? bins : m_bins;\r
+ if (bins == 0 && m_size > 0)\r
+ {\r
+ // these numbers are pretty arbitrary\r
+ // TODO - make them user-customisable?\r
+ float load = loading();\r
+ if (load > 2.0)\r
+ new_bins = (unsigned)(m_bins * load);\r
+ else if (load > 1.0)\r
+ new_bins = m_bins * 2;\r
+ }\r
+ if (new_bins == m_bins) return;\r
+ // set the new rehashing point if auto-rehashing is on\r
+ if (m_rehash) m_rehash = new_bins;\r
+ // move aside the old structure\r
+ hash_element<K,T,H,E>** old_values = m_values;\r
+ unsigned old_bins = m_bins;\r
+ // create a replacement structure\r
+ m_values = new hash_element<K,T,H,E>*[new_bins];\r
+ for (unsigned i = 0; i < new_bins; i++)\r
+ m_values[i] = 0;\r
+ m_bins = new_bins;\r
+ // move all the old elements across, rehashing each one\r
+ for (unsigned j = 0; j < old_bins; j++)\r
+ {\r
+ while(old_values[j])\r
+ {\r
+ // unhook from the old structure\r
+ hash_element<K,T,H,E>* current = old_values[j];\r
+ old_values[j] = current->m_next;\r
+ // rehash using the stored hash value\r
+ unsigned bin = current->bin();\r
+ // hook it into the new structure\r
+ current->m_next = m_values[bin];\r
+ m_values[bin] = current;\r
+ }\r
+ }\r
+ // now delete the old structure\r
+ delete[] old_values;\r
+ }\r
+\r
+ // the loading is the average number of elements per bin\r
+ // this simplifies to the total elements divided by the number of bins\r
+\r
+ template<typename K, typename T, class H, class E>\r
+ float hash<K,T,H,E>::loading(void) const\r
+ {\r
+ return (float)m_size / (float)m_bins;\r
+ }\r
+\r
+ // remove all elements from the table\r
+\r
+ template<typename K, typename T, class H, class E>\r
+ void hash<K,T,H,E>::erase(void)\r
+ {\r
+ // unhook the list elements and destroy them\r
+ for (unsigned i = 0; i < m_bins; i++)\r
+ {\r
+ hash_element<K,T,H,E>* current = m_values[i];\r
+ while(current)\r
+ {\r
+ hash_element<K,T,H,E>* next = current->m_next;\r
+ delete current;\r
+ current = next;\r
+ }\r
+ m_values[i] = 0;\r
+ }\r
+ m_size = 0;\r
+ }\r
+\r
+ // test for whether a key is present in the table\r
+\r
+ template<typename K, typename T, class H, class E>\r
+ bool hash<K,T,H,E>::present(const K& key) const\r
+ {\r
+ return find(key) != end();\r
+ }\r
+\r
+ template<typename K, typename T, class H, class E>\r
+ TYPENAME hash<K,T,H,E>::size_type hash<K,T,H,E>::count(const K& key) const\r
+ {\r
+ return present() ? 1 : 0;\r
+ }\r
+\r
+ // add a key and data element to the table - defined in terms of the general-purpose pair insert function\r
+\r
+ template<typename K, typename T, class H, class E>\r
+ TYPENAME hash<K,T,H,E>::iterator hash<K,T,H,E>::insert(const K& key, const T& data)\r
+ {\r
+ return insert(std::pair<const K,T>(key,data)).first;\r
+ }\r
+\r
+ // insert a key/data pair into the table\r
+ // this removes any old value with the same key since there is no multihash functionality\r
+\r
+ template<typename K, typename T, class H, class E>\r
+ std::pair<TYPENAME hash<K,T,H,E>::iterator, bool> hash<K,T,H,E>::insert(const std::pair<const K,T>& value)\r
+ {\r
+ // if auto-rehash is enabled, implement the auto-rehash before inserting the new value\r
+ // the table is rehashed if this insertion makes the loading exceed 1.0\r
+ if (m_rehash && (m_size >= m_rehash)) rehash();\r
+ // calculate the new hash value\r
+ unsigned hash_value_full = H()(value.first);\r
+ unsigned bin = hash_value_full % m_bins;\r
+ bool inserted = true;\r
+ // unhook any previous value with this key\r
+ // this has been inlined from erase(key) so that the hash value is not calculated twice\r
+ hash_element<K,T,H,E>* previous = 0;\r
+ for (hash_element<K,T,H,E>* current = m_values[bin]; current; previous = current, current = current->m_next)\r
+ {\r
+ // first check the full stored hash value\r
+ if (current->m_hash != hash_value_full) continue;\r
+\r
+ // next try the equality operator\r
+ if (!E()(current->m_value.first, value.first)) continue;\r
+\r
+ // unhook this value and destroy it\r
+ if (previous)\r
+ previous->m_next = current->m_next;\r
+ else\r
+ m_values[bin] = current->m_next;\r
+ delete current;\r
+ m_size--;\r
+\r
+ // we've overwritten a previous value\r
+ inserted = false;\r
+\r
+ // assume there can only be one match so we can give up now\r
+ break;\r
+ }\r
+ // now hook in a new list element at the start of the list for this hash value\r
+ hash_element<K,T,H,E>* new_item = new hash_element<K,T,H,E>(this, value, hash_value_full);\r
+ new_item->m_next = m_values[bin];\r
+ m_values[bin] = new_item;\r
+ // increment the size count\r
+ m_size++;\r
+ // construct an iterator from the list node, and return whether inserted\r
+ return std::make_pair(hash_iterator<K,T,H,E,std::pair<const K,T> >(new_item), inserted);\r
+ }\r
+\r
+ // insert a key with an empty data field ready to be filled in later\r
+\r
+ template<typename K, typename T, class H, class E>\r
+ TYPENAME hash<K,T,H,E>::iterator hash<K,T,H,E>::insert(const K& key)\r
+ {\r
+ return insert(key,T());\r
+ }\r
+\r
+ // remove a key from the table - return true if the key was found and removed, false if it wasn't present\r
+\r
+ template<typename K, typename T, class H, class E>\r
+ unsigned hash<K,T,H,E>::erase(const K& key)\r
+ {\r
+ unsigned hash_value_full = H()(key);\r
+ unsigned bin = hash_value_full % m_bins;\r
+ // scan the list for an element with this key\r
+ // need to keep a previous pointer because the lists are single-linked\r
+ hash_element<K,T,H,E>* previous = 0;\r
+ for (hash_element<K,T,H,E>* current = m_values[bin]; current; previous = current, current = current->m_next)\r
+ {\r
+ // first check the full stored hash value\r
+ if (current->m_hash != hash_value_full) continue;\r
+\r
+ // next try the equality operator\r
+ if (!E()(current->m_value.first, key)) continue;\r
+\r
+ // found this key, so unhook the element from the list\r
+ if (previous)\r
+ previous->m_next = current->m_next;\r
+ else\r
+ m_values[bin] = current->m_next;\r
+ // destroy it\r
+ delete current;\r
+ // remember to maintain the size count\r
+ m_size--;\r
+ return 1;\r
+ }\r
+ return 0;\r
+ }\r
+\r
+ // remove an element from the hash table using an iterator (std::map equivalent)\r
+ template<typename K, typename T, class H, class E>\r
+ TYPENAME hash<K,T,H,E>::iterator hash<K,T,H,E>::erase(TYPENAME hash<K,T,H,E>::iterator it)\r
+ {\r
+ // work out what the next iterator is in order to return it later\r
+ TYPENAME hash<K,T,H,E>::iterator next(it);\r
+ ++next;\r
+ // we now need to find where this item is - made difficult by the use of\r
+ // single-linked lists which means I have to search through the bin from\r
+ // the top in order to unlink from the list.\r
+ unsigned hash_value_full = it.node()->m_hash;\r
+ unsigned bin = hash_value_full % m_bins;\r
+ // scan the list for this element\r
+ // need to keep a previous pointer because the lists are single-linked\r
+ hash_element<K,T,H,E>* previous = 0;\r
+ for (hash_element<K,T,H,E>* current = m_values[bin]; current; previous = current, current = current->m_next)\r
+ {\r
+ // direct test on the address of the element\r
+ if (current != it.node()) continue;\r
+ // found this iterator, so unhook the element from the list\r
+ if (previous)\r
+ previous->m_next = current->m_next;\r
+ else\r
+ m_values[bin] = current->m_next;\r
+ // destroy it\r
+ delete current;\r
+ current = 0;\r
+ // remember to maintain the size count\r
+ m_size--;\r
+ break;\r
+ }\r
+ return next;\r
+ }\r
+\r
+ template<typename K, typename T, class H, class E>\r
+ void hash<K,T,H,E>::clear(void)\r
+ {\r
+ erase();\r
+ }\r
+\r
+ // search for a key in the table and return an iterator to it\r
+ // if the search fails, returns an end() iterator\r
+\r
+ template<typename K, typename T, class H, class E>\r
+ TYPENAME hash<K,T,H,E>::const_iterator hash<K,T,H,E>::find(const K& key) const\r
+ {\r
+ // scan the list for this key's hash value for the element with a matching key\r
+ unsigned hash_value_full = H()(key);\r
+ unsigned bin = hash_value_full % m_bins;\r
+ for (hash_element<K,T,H,E>* current = m_values[bin]; current; current = current->m_next)\r
+ {\r
+ if (current->m_hash == hash_value_full && E()(current->m_value.first, key))\r
+ return hash_iterator<K,T,H,E,const std::pair<const K,T> >(current);\r
+ }\r
+ return end();\r
+ }\r
+\r
+ template<typename K, typename T, class H, class E>\r
+ TYPENAME hash<K,T,H,E>::iterator hash<K,T,H,E>::find(const K& key)\r
+ {\r
+ // scan the list for this key's hash value for the element with a matching key\r
+ unsigned hash_value_full = H()(key);\r
+ unsigned bin = hash_value_full % m_bins;\r
+ for (hash_element<K,T,H,E>* current = m_values[bin]; current; current = current->m_next)\r
+ {\r
+ if (current->m_hash == hash_value_full && E()(current->m_value.first, key))\r
+ return hash_iterator<K,T,H,E,std::pair<const K,T> >(current);\r
+ }\r
+ return end();\r
+ }\r
+\r
+ // table lookup by key using the index operator[], returning a reference to the data field, not an iterator\r
+ // this is rather like the std::map's [] operator\r
+ // the difference is that I have a const and non-const version\r
+ // the const version will not create the element if not present already, but the non-const version will\r
+ // the non-const version is compatible with the behaviour of the map\r
+\r
+ template<typename K, typename T, class H, class E>\r
+ const T& hash<K,T,H,E>::operator[] (const K& key) const throw(std::out_of_range)\r
+ {\r
+ // this const version cannot change the hash, so has to raise an exception if the key is missing\r
+ hash_iterator<K,T,H,E,const std::pair<const K,T> > found = find(key);\r
+ if (found == end())\r
+ throw std::out_of_range("key not found in stlplus::hash::operator[]");\r
+ return found->second;\r
+ }\r
+\r
+ template<typename K, typename T, class H, class E>\r
+ T& hash<K,T,H,E>::operator[] (const K& key)\r
+ {\r
+ // this non-const version can change the hash, so creates a new element if the key is missing\r
+ hash_iterator<K,T,H,E,std::pair<const K,T> > found = find(key);\r
+ if (found == end())\r
+ found = insert(key);\r
+ return found->second;\r
+ }\r
+\r
+ // iterators\r
+\r
+ template<typename K, typename T, class H, class E>\r
+ TYPENAME hash<K,T,H,E>::const_iterator hash<K,T,H,E>::begin(void) const\r
+ {\r
+ // find the first element\r
+ for (unsigned bin = 0; bin < m_bins; bin++)\r
+ if (m_values[bin])\r
+ return hash_iterator<K,T,H,E,const std::pair<const K,T> >(m_values[bin]);\r
+ // if the hash is empty, return the end iterator\r
+ return end();\r
+ }\r
+\r
+ template<typename K, typename T, class H, class E>\r
+ TYPENAME hash<K,T,H,E>::iterator hash<K,T,H,E>::begin(void)\r
+ {\r
+ // find the first element\r
+ for (unsigned bin = 0; bin < m_bins; bin++)\r
+ if (m_values[bin])\r
+ return hash_iterator<K,T,H,E,std::pair<const K,T> >(m_values[bin]);\r
+ // if the hash is empty, return the end iterator\r
+ return end();\r
+ }\r
+\r
+ template<typename K, typename T, class H, class E>\r
+ TYPENAME hash<K,T,H,E>::const_iterator hash<K,T,H,E>::end(void) const\r
+ {\r
+ return hash_iterator<K,T,H,E,const std::pair<const K,T> >(this);\r
+ }\r
+\r
+ template<typename K, typename T, class H, class E>\r
+ TYPENAME hash<K,T,H,E>::iterator hash<K,T,H,E>::end(void)\r
+ {\r
+ return hash_iterator<K,T,H,E,std::pair<const K,T> >(this);\r
+ }\r
+\r
+ template<typename K, typename T, class H, class E>\r
+ void hash<K,T,H,E>::debug_report(std::ostream& str) const\r
+ {\r
+ // calculate some stats first\r
+ unsigned occupied = 0;\r
+ unsigned min_in_bin = m_size;\r
+ unsigned max_in_bin = 0;\r
+ for (unsigned i = 0; i < m_bins; i++)\r
+ {\r
+ if (m_values[i]) occupied++;\r
+ unsigned count = 0;\r
+ for (hash_element<K,T,H,E>* item = m_values[i]; item; item = item->m_next) count++;\r
+ if (count > max_in_bin) max_in_bin = count;\r
+ if (count < min_in_bin) min_in_bin = count;\r
+ }\r
+ // now print the table\r
+ str << "------------------------------------------------------------------------" << std::endl;\r
+ str << "| size: " << m_size << std::endl;\r
+ str << "| bins: " << m_bins << std::endl;\r
+ str << "| loading: " << loading() << " ";\r
+ if (m_rehash)\r
+ str << "auto-rehash at " << m_rehash << std::endl;\r
+ else\r
+ str << "manual rehash" << std::endl;\r
+ str << "| occupied: " << occupied \r
+ << std::fixed << " (" << (100.0*(float)occupied/(float)m_bins) << "%)" << std::scientific\r
+ << ", min = " << min_in_bin << ", max = " << max_in_bin << std::endl;\r
+ str << "|-----------------------------------------------------------------------" << std::endl;\r
+ str << "| bin 0 1 2 3 4 5 6 7 8 9" << std::endl;\r
+ str << "| ---------------------------------------------------------------";\r
+ for (unsigned j = 0; j < m_bins; j++)\r
+ {\r
+ if (j % 10 == 0)\r
+ {\r
+ str << std::endl;\r
+ str << "| " << std::setw(6) << std::right << (j/10*10) << std::left << " |";\r
+ }\r
+ unsigned count = 0;\r
+ for (hash_element<K,T,H,E>* item = m_values[j]; item; item = item->m_next) count++;\r
+ if (!count)\r
+ str << " .";\r
+ else\r
+ str << std::setw(6) << std::right << count << std::left;\r
+ }\r
+ str << std::endl;\r
+ str << "------------------------------------------------------------------------" << std::endl;\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
+\r
-#ifndef STLPLUS_MATRIX
-#define STLPLUS_MATRIX
-////////////////////////////////////////////////////////////////////////////////
-
-// Author: Andy Rushton
-// Copyright: (c) Southampton University 1999-2004
-// (c) Andy Rushton 2004-2009
-// License: BSD License, see ../docs/license.html
-
-// General-purpose 2D matrix data structure
-
-////////////////////////////////////////////////////////////////////////////////
-#include "containers_fixes.hpp"
-#include <stdexcept>
-
-namespace stlplus
-{
-
- ////////////////////////////////////////////////////////////////////////////////
-
- template<typename T> class matrix
- {
- public:
- matrix(unsigned rows = 0, unsigned cols = 0, const T& fill = T()) throw();
- ~matrix(void) throw();
-
- matrix(const matrix&) throw();
- matrix& operator =(const matrix&) throw();
-
- void resize(unsigned rows, unsigned cols, const T& fill = T()) throw();
-
- unsigned rows(void) const throw();
- unsigned columns(void) const throw();
-
- void erase(const T& fill = T()) throw();
- void erase(unsigned row, unsigned col, const T& fill = T()) throw(std::out_of_range);
- void insert(unsigned row, unsigned col, const T&) throw(std::out_of_range);
- const T& item(unsigned row, unsigned col) const throw(std::out_of_range);
- T& item(unsigned row, unsigned col) throw(std::out_of_range);
- const T& operator()(unsigned row, unsigned col) const throw(std::out_of_range);
- T& operator()(unsigned row, unsigned col) throw(std::out_of_range);
-
- void fill(const T& item = T()) throw();
- void fill_column(unsigned col, const T& item = T()) throw(std::out_of_range);
- void fill_row(unsigned row, const T& item = T()) throw(std::out_of_range);
- void fill_leading_diagonal(const T& item = T()) throw();
- void fill_trailing_diagonal(const T& item = T()) throw();
- void make_identity(const T& one, const T& zero = T()) throw();
-
- void transpose(void) throw();
-
- private:
- unsigned m_rows;
- unsigned m_cols;
- T** m_data;
- };
-
- ////////////////////////////////////////////////////////////////////////////////
-
-} // end namespace stlplus
-
-#include "matrix.tpp"
-#endif
+#ifndef STLPLUS_MATRIX\r
+#define STLPLUS_MATRIX\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+// Author: Andy Rushton\r
+// Copyright: (c) Southampton University 1999-2004\r
+// (c) Andy Rushton 2004 onwards\r
+// License: BSD License, see ../docs/license.html\r
+\r
+// General-purpose 2D matrix data structure \r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "containers_fixes.hpp"\r
+#include <stdexcept>\r
+\r
+namespace stlplus\r
+{\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+\r
+ template<typename T> class matrix\r
+ {\r
+ public:\r
+ matrix(unsigned rows = 0, unsigned cols = 0, const T& fill = T()) throw();\r
+ ~matrix(void) throw();\r
+\r
+ matrix(const matrix&) throw();\r
+ matrix& operator =(const matrix&) throw();\r
+\r
+ void resize(unsigned rows, unsigned cols, const T& fill = T()) throw();\r
+\r
+ unsigned rows(void) const throw();\r
+ unsigned columns(void) const throw();\r
+\r
+ void erase(const T& fill = T()) throw();\r
+ void erase(unsigned row, unsigned col, const T& fill = T()) throw(std::out_of_range);\r
+ void insert(unsigned row, unsigned col, const T&) throw(std::out_of_range);\r
+ const T& item(unsigned row, unsigned col) const throw(std::out_of_range);\r
+ T& item(unsigned row, unsigned col) throw(std::out_of_range);\r
+ const T& operator()(unsigned row, unsigned col) const throw(std::out_of_range);\r
+ T& operator()(unsigned row, unsigned col) throw(std::out_of_range);\r
+\r
+ void fill(const T& item = T()) throw();\r
+ void fill_column(unsigned col, const T& item = T()) throw(std::out_of_range);\r
+ void fill_row(unsigned row, const T& item = T()) throw(std::out_of_range);\r
+ void fill_leading_diagonal(const T& item = T()) throw();\r
+ void fill_trailing_diagonal(const T& item = T()) throw();\r
+ void make_identity(const T& one, const T& zero = T()) throw();\r
+\r
+ void transpose(void) throw();\r
+\r
+ private:\r
+ unsigned m_rows;\r
+ unsigned m_cols;\r
+ T** m_data;\r
+ };\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
+\r
+#include "matrix.tpp"\r
+#endif\r
-////////////////////////////////////////////////////////////////////////////////
-
-// Author: Andy Rushton
-// Copyright: (c) Southampton University 1999-2004
-// (c) Andy Rushton 2004-2009
-// License: BSD License, see ../docs/license.html
-
-////////////////////////////////////////////////////////////////////////////////
-
-namespace stlplus
-{
-
- ////////////////////////////////////////////////////////////////////////////////
-
- template<typename T>
- matrix<T>::matrix(unsigned rows, unsigned cols, const T& fill) throw()
- {
- m_rows = 0;
- m_cols = 0;
- m_data = 0;
- resize(rows,cols,fill);
- }
-
- template<typename T>
- matrix<T>::~matrix(void) throw()
- {
- for (unsigned row = 0; row < m_rows; row++)
- delete[] m_data[row];
- delete[] m_data;
- }
-
- template<typename T>
- matrix<T>::matrix(const matrix<T>& r) throw()
- {
- m_rows = 0;
- m_cols = 0;
- m_data = 0;
- *this = r;
- }
-
- template<typename T>
- matrix<T>& matrix<T>::operator =(const matrix<T>& right) throw()
- {
- // clear the old values
- for (unsigned row = 0; row < m_rows; row++)
- delete[] m_data[row];
- delete[] m_data;
- m_rows = 0;
- m_cols = 0;
- m_data = 0;
- // now reconstruct with the new
- resize(right.m_rows, right.m_cols);
- for (unsigned row = 0; row < m_rows; row++)
- for (unsigned col = 0; col < m_cols; col++)
- m_data[row][col] = right.m_data[row][col];
- return *this;
- }
-
- template<typename T>
- void matrix<T>::resize(unsigned rows, unsigned cols, const T& fill) throw()
- {
- // a grid is an array of rows, where each row is an array of T
- // a zero-row or zero-column matrix has a null grid
- // TODO - make this exception-safe - new could throw here and that would cause a memory leak
- T** new_grid = 0;
- if (rows && cols)
- {
- new_grid = new T*[rows];
- for (unsigned row = 0; row < rows; row++)
- {
- new_grid[row] = new T[cols];
- // copy old items to the new grid but only within the bounds of the intersection of the old and new grids
- // fill the rest of the grid with the initial value
- for (unsigned col = 0; col < cols; col++)
- if (row < m_rows && col < m_cols)
- new_grid[row][col] = m_data[row][col];
- else
- new_grid[row][col] = fill;
- }
- }
- // destroy the old grid
- for (unsigned row = 0; row < m_rows; row++)
- delete[] m_data[row];
- delete[] m_data;
- // move the new data into the matrix
- m_data = new_grid;
- m_rows = rows;
- m_cols = cols;
- }
-
- template<typename T>
- unsigned matrix<T>::rows(void) const throw()
- {
- return m_rows;
- }
-
- template<typename T>
- unsigned matrix<T>::columns(void) const throw()
- {
- return m_cols;
- }
-
- template<typename T>
- void matrix<T>::erase(const T& fill) throw()
- {
- for (unsigned row = 0; row < m_rows; row++)
- for (unsigned col = 0; col < m_cols; col++)
- insert(row,col,fill);
- }
-
- template<typename T>
- void matrix<T>::erase(unsigned row, unsigned col, const T& fill) throw(std::out_of_range)
- {
- insert(row,col,fill);
- }
-
- template<typename T>
- void matrix<T>::insert(unsigned row, unsigned col, const T& element) throw(std::out_of_range)
- {
- if (row >= m_rows) throw std::out_of_range("matrix::insert row");
- if (col >= m_cols) throw std::out_of_range("matrix::insert col");
- m_data[row][col] = element;
- }
-
- template<typename T>
- const T& matrix<T>::item(unsigned row, unsigned col) const throw(std::out_of_range)
- {
- if (row >= m_rows) throw std::out_of_range("matrix::item row");
- if (col >= m_cols) throw std::out_of_range("matrix::item col");
- return m_data[row][col];
- }
-
- template<typename T>
- T& matrix<T>::item(unsigned row, unsigned col) throw(std::out_of_range)
- {
- if (row >= m_rows) throw std::out_of_range("matrix::item row");
- if (col >= m_cols) throw std::out_of_range("matrix::item col");
- return m_data[row][col];
- }
-
- template<typename T>
- const T& matrix<T>::operator()(unsigned row, unsigned col) const throw(std::out_of_range)
- {
- if (row >= m_rows) throw std::out_of_range("matrix::operator() row");
- if (col >= m_cols) throw std::out_of_range("matrix::operator() col");
- return m_data[row][col];
- }
-
- template<typename T>
- T& matrix<T>::operator()(unsigned row, unsigned col) throw(std::out_of_range)
- {
- if (row >= m_rows) throw std::out_of_range("matrix::operator() row");
- if (col >= m_cols) throw std::out_of_range("matrix::operator() col");
- return m_data[row][col];
- }
-
- template<typename T>
- void matrix<T>::fill(const T& item) throw()
- {
- erase(item);
- }
-
- template<typename T>
- void matrix<T>::fill_column(unsigned col, const T& item) throw (std::out_of_range)
- {
- if (col >= m_cols) throw std::out_of_range("matrix::fill_column");
- for (unsigned row = 0; row < m_rows; row++)
- insert(row, col, item);
- }
-
- template<typename T>
- void matrix<T>::fill_row(unsigned row, const T& item) throw (std::out_of_range)
- {
- if (row >= m_rows) throw std::out_of_range("matrix::fill_row");
- for (unsigned col = 0; col < m_cols; col++)
- insert(row, col, item);
- }
-
- template<typename T>
- void matrix<T>::fill_leading_diagonal(const T& item) throw()
- {
- for (unsigned i = 0; i < m_cols && i < m_rows; i++)
- insert(i, i, item);
- }
-
- template<typename T>
- void matrix<T>::fill_trailing_diagonal(const T& item) throw()
- {
- for (unsigned i = 0; i < m_cols && i < m_rows; i++)
- insert(i, m_cols-i-1, item);
- }
-
- template<typename T>
- void matrix<T>::make_identity(const T& one, const T& zero) throw()
- {
- fill(zero);
- fill_leading_diagonal(one);
- }
-
- template<typename T>
- void matrix<T>::transpose(void) throw()
- {
- // no gain in manipulating this, since building a new matrix is no less efficient
- matrix<T> transposed(columns(), rows());
- for (unsigned row = 0; row < rows(); row++)
- for (unsigned col = 0; col < columns(); col++)
- transposed.insert(col,row,item(row,col));
- // TODO - avoid an extra copy by swapping the member data here
- *this = transposed;
- }
-
- ////////////////////////////////////////////////////////////////////////////////
-
-} // end namespace stlplus
-
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+// Author: Andy Rushton\r
+// Copyright: (c) Southampton University 1999-2004\r
+// (c) Andy Rushton 2004 onwards\r
+// License: BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+\r
+ template<typename T>\r
+ matrix<T>::matrix(unsigned rows, unsigned cols, const T& fill) throw()\r
+ {\r
+ m_rows = 0;\r
+ m_cols = 0;\r
+ m_data = 0;\r
+ resize(rows,cols,fill);\r
+ }\r
+\r
+ template<typename T>\r
+ matrix<T>::~matrix(void) throw()\r
+ {\r
+ for (unsigned row = 0; row < m_rows; row++)\r
+ delete[] m_data[row];\r
+ delete[] m_data;\r
+ }\r
+\r
+ template<typename T>\r
+ matrix<T>::matrix(const matrix<T>& r) throw()\r
+ {\r
+ m_rows = 0;\r
+ m_cols = 0;\r
+ m_data = 0;\r
+ *this = r;\r
+ }\r
+\r
+ template<typename T>\r
+ matrix<T>& matrix<T>::operator =(const matrix<T>& right) throw()\r
+ {\r
+ // clear the old values\r
+ for (unsigned row = 0; row < m_rows; row++)\r
+ delete[] m_data[row];\r
+ delete[] m_data;\r
+ m_rows = 0;\r
+ m_cols = 0;\r
+ m_data = 0;\r
+ // now reconstruct with the new\r
+ resize(right.m_rows, right.m_cols);\r
+ for (unsigned row = 0; row < m_rows; row++)\r
+ for (unsigned col = 0; col < m_cols; col++)\r
+ m_data[row][col] = right.m_data[row][col];\r
+ return *this;\r
+ }\r
+\r
+ template<typename T>\r
+ void matrix<T>::resize(unsigned rows, unsigned cols, const T& fill) throw()\r
+ {\r
+ // a grid is an array of rows, where each row is an array of T\r
+ // a zero-row or zero-column matrix has a null grid\r
+ // TODO - make this exception-safe - new could throw here and that would cause a memory leak\r
+ T** new_grid = 0;\r
+ if (rows && cols)\r
+ {\r
+ new_grid = new T*[rows];\r
+ for (unsigned row = 0; row < rows; row++)\r
+ {\r
+ new_grid[row] = new T[cols];\r
+ // copy old items to the new grid but only within the bounds of the intersection of the old and new grids\r
+ // fill the rest of the grid with the initial value\r
+ for (unsigned col = 0; col < cols; col++)\r
+ if (row < m_rows && col < m_cols)\r
+ new_grid[row][col] = m_data[row][col];\r
+ else\r
+ new_grid[row][col] = fill;\r
+ }\r
+ }\r
+ // destroy the old grid\r
+ for (unsigned row = 0; row < m_rows; row++)\r
+ delete[] m_data[row];\r
+ delete[] m_data;\r
+ // move the new data into the matrix\r
+ m_data = new_grid;\r
+ m_rows = rows;\r
+ m_cols = cols;\r
+ }\r
+\r
+ template<typename T>\r
+ unsigned matrix<T>::rows(void) const throw()\r
+ {\r
+ return m_rows;\r
+ }\r
+\r
+ template<typename T>\r
+ unsigned matrix<T>::columns(void) const throw()\r
+ {\r
+ return m_cols;\r
+ }\r
+\r
+ template<typename T>\r
+ void matrix<T>::erase(const T& fill) throw()\r
+ {\r
+ for (unsigned row = 0; row < m_rows; row++)\r
+ for (unsigned col = 0; col < m_cols; col++)\r
+ insert(row,col,fill);\r
+ }\r
+\r
+ template<typename T>\r
+ void matrix<T>::erase(unsigned row, unsigned col, const T& fill) throw(std::out_of_range)\r
+ {\r
+ insert(row,col,fill);\r
+ }\r
+\r
+ template<typename T>\r
+ void matrix<T>::insert(unsigned row, unsigned col, const T& element) throw(std::out_of_range)\r
+ {\r
+ if (row >= m_rows) throw std::out_of_range("matrix::insert row");\r
+ if (col >= m_cols) throw std::out_of_range("matrix::insert col");\r
+ m_data[row][col] = element;\r
+ }\r
+\r
+ template<typename T>\r
+ const T& matrix<T>::item(unsigned row, unsigned col) const throw(std::out_of_range)\r
+ {\r
+ if (row >= m_rows) throw std::out_of_range("matrix::item row");\r
+ if (col >= m_cols) throw std::out_of_range("matrix::item col");\r
+ return m_data[row][col];\r
+ }\r
+\r
+ template<typename T>\r
+ T& matrix<T>::item(unsigned row, unsigned col) throw(std::out_of_range)\r
+ {\r
+ if (row >= m_rows) throw std::out_of_range("matrix::item row");\r
+ if (col >= m_cols) throw std::out_of_range("matrix::item col");\r
+ return m_data[row][col];\r
+ }\r
+\r
+ template<typename T>\r
+ const T& matrix<T>::operator()(unsigned row, unsigned col) const throw(std::out_of_range)\r
+ {\r
+ if (row >= m_rows) throw std::out_of_range("matrix::operator() row");\r
+ if (col >= m_cols) throw std::out_of_range("matrix::operator() col");\r
+ return m_data[row][col];\r
+ }\r
+\r
+ template<typename T>\r
+ T& matrix<T>::operator()(unsigned row, unsigned col) throw(std::out_of_range)\r
+ {\r
+ if (row >= m_rows) throw std::out_of_range("matrix::operator() row");\r
+ if (col >= m_cols) throw std::out_of_range("matrix::operator() col");\r
+ return m_data[row][col];\r
+ }\r
+\r
+ template<typename T>\r
+ void matrix<T>::fill(const T& item) throw()\r
+ {\r
+ erase(item);\r
+ }\r
+\r
+ template<typename T>\r
+ void matrix<T>::fill_column(unsigned col, const T& item) throw (std::out_of_range)\r
+ {\r
+ if (col >= m_cols) throw std::out_of_range("matrix::fill_column");\r
+ for (unsigned row = 0; row < m_rows; row++)\r
+ insert(row, col, item);\r
+ }\r
+\r
+ template<typename T>\r
+ void matrix<T>::fill_row(unsigned row, const T& item) throw (std::out_of_range)\r
+ {\r
+ if (row >= m_rows) throw std::out_of_range("matrix::fill_row");\r
+ for (unsigned col = 0; col < m_cols; col++)\r
+ insert(row, col, item);\r
+ }\r
+\r
+ template<typename T>\r
+ void matrix<T>::fill_leading_diagonal(const T& item) throw()\r
+ {\r
+ for (unsigned i = 0; i < m_cols && i < m_rows; i++)\r
+ insert(i, i, item);\r
+ }\r
+\r
+ template<typename T>\r
+ void matrix<T>::fill_trailing_diagonal(const T& item) throw()\r
+ {\r
+ for (unsigned i = 0; i < m_cols && i < m_rows; i++)\r
+ insert(i, m_cols-i-1, item);\r
+ }\r
+\r
+ template<typename T>\r
+ void matrix<T>::make_identity(const T& one, const T& zero) throw()\r
+ {\r
+ fill(zero);\r
+ fill_leading_diagonal(one);\r
+ }\r
+\r
+ template<typename T>\r
+ void matrix<T>::transpose(void) throw()\r
+ {\r
+ // no gain in manipulating this, since building a new matrix is no less efficient\r
+ matrix<T> transposed(columns(), rows());\r
+ for (unsigned row = 0; row < rows(); row++)\r
+ for (unsigned col = 0; col < columns(); col++)\r
+ transposed.insert(col,row,item(row,col));\r
+ // TODO - avoid an extra copy by swapping the member data here\r
+ *this = transposed;\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
+\r
-#ifndef STLPLUS_NTREE
-#define STLPLUS_NTREE
-////////////////////////////////////////////////////////////////////////////////
-
-// Author: Andy Rushton
-// Copyright: (c) Southampton University 1999-2004
-// (c) Andy Rushton 2004-2009
-// License: BSD License, see ../docs/license.html
-
-// A templated n-ary tree data structure. STL-like but the definition of
-// iterators is really only applicable to one-dimensional structures. I use
-// iterators to access tree nodes, but there is no increment or decrement
-// operators for them. I also define prefix and postfix traversal iterators
-// which do have increment.
-
-////////////////////////////////////////////////////////////////////////////////
-#include "containers_fixes.hpp"
-#include "exceptions.hpp"
-#include "safe_iterator.hpp"
-
-namespace stlplus
-{
-
- ////////////////////////////////////////////////////////////////////////////////
- // Internals
-
- template<typename T> class ntree_node;
- template<typename T> class ntree;
- template<typename T, typename TRef, typename TPtr> class ntree_iterator;
- template<typename T, typename TRef, typename TPtr> class ntree_prefix_iterator;
- template<typename T, typename TRef, typename TPtr> class ntree_postfix_iterator;
-
- ////////////////////////////////////////////////////////////////////////////////
- // Iterators
-
- // Simple iterators which are just used as pointers to tree nodes. These have
- // no increment or decrement operations defined. An uninitialised iterator is
- // null - similarly, if you ask for the root of an empty tree or the parent of
- // the root node then you get a null iterator.
-
- template<typename T, typename TRef, typename TPtr>
- class ntree_iterator : public safe_iterator<ntree<T>,ntree_node<T> >
- {
- public:
- // local type definitions
- // an iterator points to an object whilst a const_iterator points to a const object
- typedef ntree_iterator<T,T&,T*> iterator;
- typedef ntree_iterator<T,const T&,const T*> const_iterator;
- typedef ntree_iterator<T,TRef,TPtr> this_iterator;
- typedef TRef reference;
- typedef TPtr pointer;
-
- // constructor to create a null iterator - you must assign a valid value to this iterator before using it
- ntree_iterator(void);
- ~ntree_iterator(void);
-
- // Type conversion methods allow const_iterator and iterator to be converted
- const_iterator constify(void) const;
- iterator deconstify(void) const;
-
- // tests useful for putting iterators into other STL structures and for testing whether iteration has completed
- bool operator == (const this_iterator& r) const;
- bool operator != (const this_iterator& r) const;
- bool operator < (const this_iterator& r) const;
-
- // access the node data - a const_iterator gives you a const element, an iterator a non-const element
- // it is illegal to dereference an invalid (i.e. null or end) iterator
- reference operator*(void) const
- throw(null_dereference,end_dereference);
- pointer operator->(void) const
- throw(null_dereference,end_dereference);
-
- friend class ntree<T>;
- friend class ntree_prefix_iterator<T,TRef,TPtr>;
- friend class ntree_postfix_iterator<T,TRef,TPtr>;
-
- public:
- // Note: I had to make this public to get round a compiler problem - it should be private
- // you cannot create a valid iterator except by calling an ntree method that returns one
- // constructor used by ntree to create a non-null iterator
- explicit ntree_iterator(ntree_node<T>* node);
- // constructor used by ntree to create an end iterator
- explicit ntree_iterator(const ntree<T>* owner);
- // used to create an alias of an iterator
- explicit ntree_iterator(const safe_iterator<ntree<T>, ntree_node<T> >& iterator);
- };
-
- // Traversal iterators are like iterators but they have increment operators (++)
- // - prefix_iterator visits the nodes of the tree in prefix order
- // - postfix_iterator visits the nodes of the tree in postfix order.
- // There is no such thing as infix order for an n-ary tree and you cannot
- // traverse backwards with these iterators. These follow the STL convention in
- // that you iterate from a begin to an end - in this case ntree exports
- // prefix_begin()/prefix_end() and postfix_begin()/postfix_end(). You can
- // simplify these iterators to the basic iterator above for functions that
- // require a simple iterator.
-
- template<typename T, typename TRef, typename TPtr>
- class ntree_prefix_iterator
- {
- public:
- typedef ntree_prefix_iterator<T,T&,T*> iterator;
- typedef ntree_prefix_iterator<T,const T&,const T*> const_iterator;
- typedef ntree_prefix_iterator<T,TRef,TPtr> this_iterator;
- typedef ntree_iterator<T,TRef,TPtr> simple_iterator;
- typedef TRef reference;
- typedef TPtr pointer;
-
- // constructor to create a null iterator - you must assign a valid value to this iterator before using it
- ntree_prefix_iterator(void);
- ~ntree_prefix_iterator(void);
-
- // tests
- // a null iterator is one that has not been initialised with a value yet
- // i.e. you just declared it but didn't assign to it
- bool null(void) const;
- // an end iterator is one that points to the end element of the list of nodes
- // in STL conventions this is one past the last valid element and must not be dereferenced
- bool end(void) const;
- // a valid iterator is one that can be dereferenced
- // i.e. non-null and non-end
- bool valid(void) const;
-
- // Type conversion methods allow const_iterator and iterator to be converted
- // convert an iterator/const_iterator to a const_iterator
- const_iterator constify(void) const;
- iterator deconstify(void) const;
-
- // generate a simple iterator from a traversal iterator
- ntree_iterator<T,TRef,TPtr> simplify(void) const;
-
- // tests useful for putting iterators into other STL structures and for testing whether iteration has completed
- bool operator == (const this_iterator& r) const;
- bool operator != (const this_iterator& r) const;
- bool operator < (const this_iterator& r) const;
-
- // increment/decrement operators used to step through the set of all nodes in a graph
- // it is only legal to increment a valid iterator
- // pre-increment
- this_iterator& operator ++ (void)
- throw(null_dereference,end_dereference);
- // post-increment
- this_iterator operator ++ (int)
- throw(null_dereference,end_dereference);
-
- // access the node data - a const_iterator gives you a const element, an iterator a non-const element
- // it is illegal to dereference an invalid (i.e. null or end) iterator
- reference operator*(void) const
- throw(null_dereference,end_dereference);
- pointer operator->(void) const
- throw(null_dereference,end_dereference);
-
- friend class ntree<T>;
- friend class ntree_iterator<T,TRef,TPtr>;
-
- private:
- ntree_iterator<T,TRef,TPtr> m_iterator;
-
- explicit ntree_prefix_iterator(const ntree_iterator<T,TRef,TPtr>& i);
- const ntree_iterator<T,TRef,TPtr>& get_iterator(void) const;
- ntree_iterator<T,TRef,TPtr>& get_iterator(void);
- };
-
- ////////////////////////////////////////////////////////////////////////////////
-
- template<typename T, typename TRef, typename TPtr>
- class ntree_postfix_iterator
- {
- public:
- typedef ntree_postfix_iterator<T,T&,T*> iterator;
- typedef ntree_postfix_iterator<T,const T&,const T*> const_iterator;
- typedef ntree_postfix_iterator<T,TRef,TPtr> this_iterator;
- typedef ntree_iterator<T,TRef,TPtr> simple_iterator;
- typedef TRef reference;
- typedef TPtr pointer;
-
- // constructor to create a null iterator - you must assign a valid value to this iterator before using it
- ntree_postfix_iterator(void);
- ~ntree_postfix_iterator(void);
-
- // tests
- // a null iterator is one that has not been initialised with a value yet
- // i.e. you just declared it but didn't assign to it
- bool null(void) const;
- // an end iterator is one that points to the end element of the list of nodes
- // in STL conventions this is one past the last valid element and must not be dereferenced
- bool end(void) const;
- // a valid iterator is one that can be dereferenced
- // i.e. non-null and non-end
- bool valid(void) const;
-
- // Type conversion methods allow const_iterator and iterator to be converted
- // convert an iterator/const_iterator to a const_iterator
- const_iterator constify(void) const;
- iterator deconstify(void) const;
-
- // generate a simple iterator from a traversal iterator
- ntree_iterator<T,TRef,TPtr> simplify(void) const;
-
- // tests useful for putting iterators into other STL structures and for testing whether iteration has completed
- bool operator == (const this_iterator& r) const;
- bool operator != (const this_iterator& r) const;
- bool operator < (const this_iterator& r) const;
-
- // increment/decrement operators used to step through the set of all nodes in a graph
- // it is only legal to increment a valid iterator
- // pre-increment
- this_iterator& operator ++ (void)
- throw(null_dereference,end_dereference);
- // post-increment
- this_iterator operator ++ (int)
- throw(null_dereference,end_dereference);
-
- // access the node data - a const_iterator gives you a const element, an iterator a non-const element
- // it is illegal to dereference an invalid (i.e. null or end) iterator
- reference operator*(void) const
- throw(null_dereference,end_dereference);
- pointer operator->(void) const
- throw(null_dereference,end_dereference);
-
- friend class ntree<T>;
- friend class ntree_iterator<T,TRef,TPtr>;
-
- private:
- ntree_iterator<T,TRef,TPtr> m_iterator;
-
- explicit ntree_postfix_iterator(const ntree_iterator<T,TRef,TPtr>& i);
- const ntree_iterator<T,TRef,TPtr>& get_iterator(void) const;
- ntree_iterator<T,TRef,TPtr>& get_iterator(void);
- };
-
- ////////////////////////////////////////////////////////////////////////////////
- // The Ntree class
- ////////////////////////////////////////////////////////////////////////////////
-
- template<typename T>
- class ntree
- {
- public:
- // STL-like typedefs for the types and iterators
- typedef T value_type;
-
- typedef ntree_iterator<T,T&,T*> iterator;
- typedef ntree_iterator<T,const T&,const T*> const_iterator;
-
- typedef ntree_prefix_iterator<T,T&,T*> prefix_iterator;
- typedef ntree_prefix_iterator<T,const T&,const T*> const_prefix_iterator;
-
- typedef ntree_postfix_iterator<T,T&,T*> postfix_iterator;
- typedef ntree_postfix_iterator<T,const T&,const T*> const_postfix_iterator;
-
- //////////////////////////////////////////////////////////////////////////////
- // Constructors, destructors and copies
-
- ntree(void);
- ~ntree(void);
-
- // copy constructor and assignment both copy the tree
- ntree(const ntree<T>&);
- ntree<T>& operator=(const ntree<T>&);
-
- //////////////////////////////////////////////////////////////////////////////
- // size tests
-
- // tests on whole tree
- bool empty(void) const;
- unsigned size(void) const;
-
- // tests for number of nodes in subtree starting at node
- unsigned size(const const_iterator& node) const
- throw(wrong_object,null_dereference,end_dereference);
- unsigned size(const iterator& node)
- throw(wrong_object,null_dereference,end_dereference);
-
- // test for depth of tree from root to node
- unsigned depth(const const_iterator& node) const
- throw(wrong_object,null_dereference,end_dereference);
- unsigned depth(const iterator& node)
- throw(wrong_object,null_dereference,end_dereference);
-
- //////////////////////////////////////////////////////////////////////////////
- // direct traversal
-
- const_iterator root(void) const;
- iterator root(void);
-
- unsigned children(const const_iterator& node) const
- throw(wrong_object,null_dereference,end_dereference);
- unsigned children(const iterator& node)
- throw(wrong_object,null_dereference,end_dereference);
-
- const_iterator child(const const_iterator& node, unsigned child) const
- throw(wrong_object,null_dereference,end_dereference,std::out_of_range);
- iterator child(const iterator& node, unsigned child)
- throw(wrong_object,null_dereference,end_dereference,std::out_of_range);
-
- const_iterator parent(const const_iterator& node) const
- throw(wrong_object,null_dereference,end_dereference);
- iterator parent(const iterator& node)
- throw(wrong_object,null_dereference,end_dereference);
-
- //////////////////////////////////////////////////////////////////////////////
- // iterator traversal
-
- const_prefix_iterator prefix_begin(void) const;
- prefix_iterator prefix_begin(void);
- const_prefix_iterator prefix_end(void) const;
- prefix_iterator prefix_end(void);
-
- const_postfix_iterator postfix_begin(void) const;
- postfix_iterator postfix_begin(void);
- const_postfix_iterator postfix_end(void) const;
- postfix_iterator postfix_end(void);
-
- //////////////////////////////////////////////////////////////////////////////
- // modification
-
- iterator insert(const T&);
-
- iterator insert(const iterator& node, unsigned child, const T&)
- throw(wrong_object,null_dereference,end_dereference,std::out_of_range);
- iterator append(const iterator& node, const T&)
- throw(wrong_object,null_dereference,end_dereference);
-
- iterator insert(const iterator& node, unsigned child, const ntree<T>&)
- throw(wrong_object,null_dereference,end_dereference,std::out_of_range);
- iterator append(const iterator& node, const ntree<T>&)
- throw(wrong_object,null_dereference,end_dereference);
-
- iterator push(const iterator& node, const T&)
- throw(wrong_object,null_dereference,end_dereference);
- void pop(const iterator& node, unsigned child)
- throw(wrong_object,null_dereference,end_dereference);
-
- void erase(void);
- void erase(const iterator& node)
- throw(wrong_object,null_dereference,end_dereference);
- void erase(const iterator& node, unsigned child)
- throw(wrong_object,null_dereference,end_dereference,std::out_of_range);
-
- ntree<T> subtree(void);
- ntree<T> subtree(const iterator& node)
- throw(wrong_object,null_dereference,end_dereference);
- ntree<T> subtree(const iterator& node, unsigned child)
- throw(wrong_object,null_dereference,end_dereference,std::out_of_range);
-
- ntree<T> cut(void);
- ntree<T> cut(const iterator& node)
- throw(wrong_object,null_dereference,end_dereference);
- ntree<T> cut(const iterator& node, unsigned child)
- throw(wrong_object,null_dereference,end_dereference,std::out_of_range);
-
- //////////////////////////////////////////////////////////////////////////////
-
- private:
- ntree_node<T>* m_root;
- };
-
- ////////////////////////////////////////////////////////////////////////////////
-
-} // end namespace stlplus
-
-#include "ntree.tpp"
-#endif
+#ifndef STLPLUS_NTREE\r
+#define STLPLUS_NTREE\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+// Author: Andy Rushton\r
+// Copyright: (c) Southampton University 1999-2004\r
+// (c) Andy Rushton 2004 onwards\r
+// License: BSD License, see ../docs/license.html\r
+\r
+// A templated n-ary tree data structure. STL-like but the definition of\r
+// iterators is really only applicable to one-dimensional structures. I use\r
+// iterators to access tree nodes, but there is no increment or decrement\r
+// operators for them. I also define prefix and postfix traversal iterators\r
+// which do have increment.\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "containers_fixes.hpp"\r
+#include "exceptions.hpp"\r
+#include "safe_iterator.hpp"\r
+\r
+namespace stlplus\r
+{\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // Internals\r
+\r
+ template<typename T> class ntree_node;\r
+ template<typename T> class ntree;\r
+ template<typename T, typename TRef, typename TPtr> class ntree_iterator;\r
+ template<typename T, typename TRef, typename TPtr> class ntree_prefix_iterator;\r
+ template<typename T, typename TRef, typename TPtr> class ntree_postfix_iterator;\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // Iterators\r
+\r
+ // Simple iterators which are just used as pointers to tree nodes. These have\r
+ // no increment or decrement operations defined. An uninitialised iterator is\r
+ // null - similarly, if you ask for the root of an empty tree or the parent of\r
+ // the root node then you get a null iterator.\r
+\r
+ template<typename T, typename TRef, typename TPtr>\r
+ class ntree_iterator : public safe_iterator<ntree<T>,ntree_node<T> >\r
+ {\r
+ public:\r
+ // local type definitions\r
+ // an iterator points to an object whilst a const_iterator points to a const object\r
+ typedef ntree_iterator<T,T&,T*> iterator;\r
+ typedef ntree_iterator<T,const T&,const T*> const_iterator;\r
+ typedef ntree_iterator<T,TRef,TPtr> this_iterator;\r
+ typedef TRef reference;\r
+ typedef TPtr pointer;\r
+\r
+ // constructor to create a null iterator - you must assign a valid value to this iterator before using it\r
+ ntree_iterator(void);\r
+ ~ntree_iterator(void);\r
+\r
+ // Type conversion methods allow const_iterator and iterator to be converted\r
+ const_iterator constify(void) const;\r
+ iterator deconstify(void) const;\r
+\r
+ // tests useful for putting iterators into other STL structures and for testing whether iteration has completed\r
+ bool operator == (const this_iterator& r) const;\r
+ bool operator != (const this_iterator& r) const;\r
+ bool operator < (const this_iterator& r) const;\r
+\r
+ // access the node data - a const_iterator gives you a const element, an iterator a non-const element\r
+ // it is illegal to dereference an invalid (i.e. null or end) iterator\r
+ reference operator*(void) const\r
+ throw(null_dereference,end_dereference);\r
+ pointer operator->(void) const\r
+ throw(null_dereference,end_dereference);\r
+\r
+ friend class ntree<T>;\r
+ friend class ntree_prefix_iterator<T,TRef,TPtr>;\r
+ friend class ntree_postfix_iterator<T,TRef,TPtr>;\r
+\r
+ public:\r
+ // Note: I had to make this public to get round a problem implementing persistence - it should be private\r
+ // you cannot create a valid iterator except by calling an ntree method that returns one\r
+ // constructor used by ntree to create a non-null iterator\r
+ explicit ntree_iterator(ntree_node<T>* node);\r
+ // constructor used by ntree to create an end iterator\r
+ explicit ntree_iterator(const ntree<T>* owner);\r
+ // used to create an alias of an iterator\r
+ explicit ntree_iterator(const safe_iterator<ntree<T>, ntree_node<T> >& iterator);\r
+ };\r
+\r
+ // Traversal iterators are like iterators but they have increment operators (++)\r
+ // - prefix_iterator visits the nodes of the tree in prefix order\r
+ // - postfix_iterator visits the nodes of the tree in postfix order.\r
+ // There is no such thing as infix order for an n-ary tree and you cannot\r
+ // traverse backwards with these iterators. These follow the STL convention in\r
+ // that you iterate from a begin to an end - in this case ntree exports\r
+ // prefix_begin()/prefix_end() and postfix_begin()/postfix_end(). You can\r
+ // simplify these iterators to the basic iterator above for functions that\r
+ // require a simple iterator.\r
+\r
+ template<typename T, typename TRef, typename TPtr>\r
+ class ntree_prefix_iterator\r
+ {\r
+ public:\r
+ typedef ntree_prefix_iterator<T,T&,T*> iterator;\r
+ typedef ntree_prefix_iterator<T,const T&,const T*> const_iterator;\r
+ typedef ntree_prefix_iterator<T,TRef,TPtr> this_iterator;\r
+ typedef ntree_iterator<T,TRef,TPtr> simple_iterator;\r
+ typedef TRef reference;\r
+ typedef TPtr pointer;\r
+\r
+ // constructor to create a null iterator - you must assign a valid value to this iterator before using it\r
+ ntree_prefix_iterator(void);\r
+ ~ntree_prefix_iterator(void);\r
+\r
+ // tests\r
+ // a null iterator is one that has not been initialised with a value yet\r
+ // i.e. you just declared it but didn't assign to it\r
+ bool null(void) const;\r
+ // an end iterator is one that points to the end element of the list of nodes\r
+ // in STL conventions this is one past the last valid element and must not be dereferenced\r
+ bool end(void) const;\r
+ // a valid iterator is one that can be dereferenced\r
+ // i.e. non-null and non-end\r
+ bool valid(void) const;\r
+\r
+ // Type conversion methods allow const_iterator and iterator to be converted\r
+ // convert an iterator/const_iterator to a const_iterator\r
+ const_iterator constify(void) const;\r
+ iterator deconstify(void) const;\r
+\r
+ // generate a simple iterator from a traversal iterator\r
+ ntree_iterator<T,TRef,TPtr> simplify(void) const;\r
+\r
+ // tests useful for putting iterators into other STL structures and for testing whether iteration has completed\r
+ bool operator == (const this_iterator& r) const;\r
+ bool operator != (const this_iterator& r) const;\r
+ bool operator < (const this_iterator& r) const;\r
+\r
+ // increment/decrement operators used to step through the set of all nodes in a graph\r
+ // it is only legal to increment a valid iterator\r
+ // pre-increment\r
+ this_iterator& operator ++ (void)\r
+ throw(null_dereference,end_dereference);\r
+ // post-increment\r
+ this_iterator operator ++ (int)\r
+ throw(null_dereference,end_dereference);\r
+\r
+ // access the node data - a const_iterator gives you a const element, an iterator a non-const element\r
+ // it is illegal to dereference an invalid (i.e. null or end) iterator\r
+ reference operator*(void) const\r
+ throw(null_dereference,end_dereference);\r
+ pointer operator->(void) const\r
+ throw(null_dereference,end_dereference);\r
+\r
+ friend class ntree<T>;\r
+ friend class ntree_iterator<T,TRef,TPtr>;\r
+\r
+ private:\r
+ ntree_iterator<T,TRef,TPtr> m_iterator;\r
+\r
+ explicit ntree_prefix_iterator(const ntree_iterator<T,TRef,TPtr>& i);\r
+ const ntree_iterator<T,TRef,TPtr>& get_iterator(void) const;\r
+ ntree_iterator<T,TRef,TPtr>& get_iterator(void);\r
+ };\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+\r
+ template<typename T, typename TRef, typename TPtr>\r
+ class ntree_postfix_iterator\r
+ {\r
+ public:\r
+ typedef ntree_postfix_iterator<T,T&,T*> iterator;\r
+ typedef ntree_postfix_iterator<T,const T&,const T*> const_iterator;\r
+ typedef ntree_postfix_iterator<T,TRef,TPtr> this_iterator;\r
+ typedef ntree_iterator<T,TRef,TPtr> simple_iterator;\r
+ typedef TRef reference;\r
+ typedef TPtr pointer;\r
+\r
+ // constructor to create a null iterator - you must assign a valid value to this iterator before using it\r
+ ntree_postfix_iterator(void);\r
+ ~ntree_postfix_iterator(void);\r
+\r
+ // tests\r
+ // a null iterator is one that has not been initialised with a value yet\r
+ // i.e. you just declared it but didn't assign to it\r
+ bool null(void) const;\r
+ // an end iterator is one that points to the end element of the list of nodes\r
+ // in STL conventions this is one past the last valid element and must not be dereferenced\r
+ bool end(void) const;\r
+ // a valid iterator is one that can be dereferenced\r
+ // i.e. non-null and non-end\r
+ bool valid(void) const;\r
+\r
+ // Type conversion methods allow const_iterator and iterator to be converted\r
+ // convert an iterator/const_iterator to a const_iterator\r
+ const_iterator constify(void) const;\r
+ iterator deconstify(void) const;\r
+\r
+ // generate a simple iterator from a traversal iterator\r
+ ntree_iterator<T,TRef,TPtr> simplify(void) const;\r
+\r
+ // tests useful for putting iterators into other STL structures and for testing whether iteration has completed\r
+ bool operator == (const this_iterator& r) const;\r
+ bool operator != (const this_iterator& r) const;\r
+ bool operator < (const this_iterator& r) const;\r
+\r
+ // increment/decrement operators used to step through the set of all nodes in a graph\r
+ // it is only legal to increment a valid iterator\r
+ // pre-increment\r
+ this_iterator& operator ++ (void)\r
+ throw(null_dereference,end_dereference);\r
+ // post-increment\r
+ this_iterator operator ++ (int)\r
+ throw(null_dereference,end_dereference);\r
+\r
+ // access the node data - a const_iterator gives you a const element, an iterator a non-const element\r
+ // it is illegal to dereference an invalid (i.e. null or end) iterator\r
+ reference operator*(void) const\r
+ throw(null_dereference,end_dereference);\r
+ pointer operator->(void) const\r
+ throw(null_dereference,end_dereference);\r
+\r
+ friend class ntree<T>;\r
+ friend class ntree_iterator<T,TRef,TPtr>;\r
+\r
+ private:\r
+ ntree_iterator<T,TRef,TPtr> m_iterator;\r
+\r
+ explicit ntree_postfix_iterator(const ntree_iterator<T,TRef,TPtr>& i);\r
+ const ntree_iterator<T,TRef,TPtr>& get_iterator(void) const;\r
+ ntree_iterator<T,TRef,TPtr>& get_iterator(void);\r
+ };\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // The Ntree class\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+\r
+ template<typename T>\r
+ class ntree\r
+ {\r
+ public:\r
+ // STL-like typedefs for the types and iterators\r
+ typedef T value_type;\r
+\r
+ typedef ntree_iterator<T,T&,T*> iterator;\r
+ typedef ntree_iterator<T,const T&,const T*> const_iterator;\r
+\r
+ typedef ntree_prefix_iterator<T,T&,T*> prefix_iterator;\r
+ typedef ntree_prefix_iterator<T,const T&,const T*> const_prefix_iterator;\r
+\r
+ typedef ntree_postfix_iterator<T,T&,T*> postfix_iterator;\r
+ typedef ntree_postfix_iterator<T,const T&,const T*> const_postfix_iterator;\r
+\r
+ //////////////////////////////////////////////////////////////////////////////\r
+ // Constructors, destructors and copies\r
+\r
+ ntree(void);\r
+ ~ntree(void);\r
+\r
+ // copy constructor and assignment both copy the tree\r
+ ntree(const ntree<T>&);\r
+ ntree<T>& operator=(const ntree<T>&);\r
+\r
+ //////////////////////////////////////////////////////////////////////////////\r
+ // size tests\r
+\r
+ // tests on whole tree\r
+ bool empty(void) const;\r
+ unsigned size(void) const;\r
+\r
+ // tests for number of nodes in subtree starting at node\r
+ unsigned size(const const_iterator& node) const\r
+ throw(wrong_object,null_dereference,end_dereference);\r
+ unsigned size(const iterator& node)\r
+ throw(wrong_object,null_dereference,end_dereference);\r
+\r
+ // test for depth of tree from root to node\r
+ unsigned depth(const const_iterator& node) const\r
+ throw(wrong_object,null_dereference,end_dereference);\r
+ unsigned depth(const iterator& node)\r
+ throw(wrong_object,null_dereference,end_dereference);\r
+\r
+ //////////////////////////////////////////////////////////////////////////////\r
+ // direct traversal\r
+\r
+ const_iterator root(void) const;\r
+ iterator root(void);\r
+\r
+ unsigned children(const const_iterator& node) const\r
+ throw(wrong_object,null_dereference,end_dereference);\r
+ unsigned children(const iterator& node)\r
+ throw(wrong_object,null_dereference,end_dereference);\r
+\r
+ const_iterator child(const const_iterator& node, unsigned child) const\r
+ throw(wrong_object,null_dereference,end_dereference,std::out_of_range);\r
+ iterator child(const iterator& node, unsigned child)\r
+ throw(wrong_object,null_dereference,end_dereference,std::out_of_range);\r
+\r
+ const_iterator parent(const const_iterator& node) const\r
+ throw(wrong_object,null_dereference,end_dereference);\r
+ iterator parent(const iterator& node)\r
+ throw(wrong_object,null_dereference,end_dereference);\r
+\r
+ //////////////////////////////////////////////////////////////////////////////\r
+ // iterator traversal\r
+\r
+ const_prefix_iterator prefix_begin(void) const;\r
+ prefix_iterator prefix_begin(void);\r
+ const_prefix_iterator prefix_end(void) const;\r
+ prefix_iterator prefix_end(void);\r
+\r
+ const_postfix_iterator postfix_begin(void) const;\r
+ postfix_iterator postfix_begin(void);\r
+ const_postfix_iterator postfix_end(void) const;\r
+ postfix_iterator postfix_end(void);\r
+\r
+ //////////////////////////////////////////////////////////////////////////////\r
+ // modification\r
+\r
+ // discard previous contents and create a new root node\r
+ iterator insert(const T&);\r
+ // add a new child inserted into the node's children at the specified place\r
+ iterator insert(const iterator& node, unsigned child, const T&)\r
+ throw(wrong_object,null_dereference,end_dereference,std::out_of_range);\r
+ // shortcut for insert at the end i.e. tree.insert(node, node.children(), value)\r
+ iterator insert(const iterator& node, const T&) \r
+ throw(wrong_object,null_dereference,end_dereference);\r
+ // old name for the above\r
+ iterator append(const iterator& node, const T&) \r
+ throw(wrong_object,null_dereference,end_dereference);\r
+\r
+ // discard previous contents and copy the tree\r
+ iterator insert(const ntree<T>&);\r
+ // add a copy of the tree as a new child inserted into the node's children at the specified place\r
+ iterator insert(const iterator& node, unsigned child, const ntree<T>&)\r
+ throw(wrong_object,null_dereference,end_dereference,std::out_of_range);\r
+ // shortcut for insert at the end i.e. tree.insert(node, node.children(), value)\r
+ iterator insert(const iterator& node, const ntree<T>&)\r
+ throw(wrong_object,null_dereference,end_dereference);\r
+ // old name for the above\r
+ iterator append(const iterator& node, const ntree<T>&)\r
+ throw(wrong_object,null_dereference,end_dereference);\r
+\r
+ // discard previous contents and move the tree without copying\r
+ // invalidates all iterators to the old tree\r
+ iterator move(ntree<T>&);\r
+ // move the tree to become the designated child\r
+ // invalidates all iterators to the old tree\r
+ iterator move(const iterator& node, unsigned child, ntree<T>&)\r
+ throw(wrong_object,null_dereference,end_dereference,std::out_of_range);\r
+ // shortcut for move to the last child i.e. node.move(node, node.children(), value)\r
+ iterator move(const iterator& node, ntree<T>&)\r
+ throw(wrong_object,null_dereference,end_dereference);\r
+\r
+ // replace the node with the new value, pushing the old node down to make it the child\r
+ // returns the iterator to the new, pushed node\r
+ iterator push(const iterator& node, const T&) \r
+ throw(wrong_object,null_dereference,end_dereference);\r
+ // erases the specified child, moving its children up to become the node's children\r
+ void pop(const iterator& node, unsigned child) \r
+ throw(wrong_object,null_dereference,end_dereference);\r
+\r
+ // erase the whole tree\r
+ void erase(void);\r
+ // erase the node and all its children\r
+ void erase(const iterator& node)\r
+ throw(wrong_object,null_dereference,end_dereference);\r
+ // erase the specified child\r
+ void erase(const iterator& node, unsigned child)\r
+ throw(wrong_object,null_dereference,end_dereference,std::out_of_range);\r
+\r
+ // get a copy of the tree as a tree\r
+ ntree<T> subtree(void);\r
+ // get a copy of the subtree as a tree with the specified node as root\r
+ ntree<T> subtree(const iterator& node)\r
+ throw(wrong_object,null_dereference,end_dereference);\r
+ // get a copy of the subtree as a tree with the specified child as root\r
+ ntree<T> subtree(const iterator& node, unsigned child)\r
+ throw(wrong_object,null_dereference,end_dereference,std::out_of_range);\r
+\r
+ // move the whole tree to make a new tree\r
+ ntree<T> cut(void);\r
+ // move the subtree to make a new tree with the specified node as root\r
+ ntree<T> cut(const iterator& node)\r
+ throw(wrong_object,null_dereference,end_dereference);\r
+ // move the subtree to make a new tree with the specified child as root\r
+ ntree<T> cut(const iterator& node, unsigned child)\r
+ throw(wrong_object,null_dereference,end_dereference,std::out_of_range);\r
+\r
+ //////////////////////////////////////////////////////////////////////////////\r
+\r
+ private:\r
+ ntree_node<T>* m_root;\r
+ };\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
+\r
+#include "ntree.tpp"\r
+#endif\r
-////////////////////////////////////////////////////////////////////////////////
-
-// Author: Andy Rushton
-// Copyright: (c) Southampton University 1999-2004
-// (c) Andy Rushton 2004-2009
-// License: BSD License, see ../docs/license.html
-
-////////////////////////////////////////////////////////////////////////////////
-#include <vector>
-#include <algorithm>
-
-namespace stlplus
-{
-
- ////////////////////////////////////////////////////////////////////////////////
- // ntree_node
-
- template<typename T>
- class ntree_node
- {
- public:
- master_iterator<ntree<T>, ntree_node<T> > m_master;
- T m_data;
- ntree_node<T>* m_parent;
- std::vector<ntree_node<T>*> m_children;
-
- public:
- ntree_node(const ntree<T>* owner, const T& data = T()) :
- m_master(owner,this), m_data(data), m_parent(0)
- {
- }
-
- void change_owner(const ntree<T>* owner)
- {
- m_master.change_owner(owner);
- for (TYPENAME std::vector<ntree_node<T>*>::iterator i = m_children.begin(); i != m_children.end(); i++)
- (*i)->change_owner(owner);
- }
-
- ~ntree_node(void)
- {
- m_parent = 0;
- for (TYPENAME std::vector<ntree_node<T>*>::iterator i = m_children.begin(); i != m_children.end(); i++)
- delete *i;
- }
-
- };
-
- template<typename T>
- static ntree_node<T>* ntree_copy(const ntree<T>* new_owner, ntree_node<T>* root)
- {
- if (!root) return 0;
- ntree_node<T>* new_tree = new ntree_node<T>(new_owner, root->m_data);
- for (TYPENAME std::vector<ntree_node<T>*>::iterator i = root->m_children.begin(); i != root->m_children.end(); i++)
- {
- ntree_node<T>* new_child = ntree_copy(new_owner, *i);
- new_tree->m_children.push_back(new_child);
- new_child->m_parent = new_tree;
- }
- return new_tree;
- }
-
- template<typename T>
- static unsigned ntree_size(ntree_node<T>* root)
- {
- if (!root) return 0;
- unsigned result = 1;
- for (TYPENAME std::vector<ntree_node<T>*>::iterator i = root->m_children.begin(); i != root->m_children.end(); i++)
- result += ntree_size(*i);
- return result;
- }
-
- template<typename T>
- static unsigned ntree_depth(ntree_node<T>* root)
- {
- unsigned depth = 0;
- for (ntree_node<T>* i = root; i; i = i->m_parent)
- depth++;
- return depth;
- }
-
- ////////////////////////////////////////////////////////////////////////////////
- // ntree_iterator
-
- // constructor to create a null iterator - you must assign a valid value to this iterator before using it
- template<typename T, typename TRef, typename TPtr>
- ntree_iterator<T,TRef,TPtr>::ntree_iterator(void)
- {
- }
-
- // used to create an alias of an iterator
- template<typename T, typename TRef, typename TPtr>
- ntree_iterator<T,TRef,TPtr>::ntree_iterator(const safe_iterator<ntree<T>, ntree_node<T> >& iterator) :
- safe_iterator<ntree<T>,ntree_node<T> >(iterator)
- {
- }
-
- // constructor used by ntree to create a non-null iterator
- template<typename T, typename TRef, typename TPtr>
- ntree_iterator<T,TRef,TPtr>::ntree_iterator(ntree_node<T>* node) :
- safe_iterator<ntree<T>,ntree_node<T> >(node->m_master)
- {
- }
-
- // constructor used by ntree to create an end iterator
- template<typename T, typename TRef, typename TPtr>
- ntree_iterator<T,TRef,TPtr>::ntree_iterator(const ntree<T>* owner) :
- safe_iterator<ntree<T>,ntree_node<T> >(owner)
- {
- }
-
- // destructor
- template<typename T, typename TRef, typename TPtr>
- ntree_iterator<T,TRef,TPtr>::~ntree_iterator(void)
- {
- }
-
- template<typename T, typename TRef, typename TPtr>
- TYPENAME ntree_iterator<T,TRef,TPtr>::const_iterator ntree_iterator<T,TRef,TPtr>::constify(void) const
- {
- return ntree_iterator<T,const T&,const T*>(*this);
- }
-
- template<typename T, typename TRef, typename TPtr>
- TYPENAME ntree_iterator<T,TRef,TPtr>::iterator ntree_iterator<T,TRef,TPtr>::deconstify(void) const
- {
- return ntree_iterator<T,T&,T*>(*this);
- }
-
- template<typename T, typename TRef, typename TPtr>
- bool ntree_iterator<T,TRef,TPtr>::operator == (const TYPENAME ntree_iterator<T,TRef,TPtr>::this_iterator& r) const
- {
- return equal(r);
- }
-
- template<typename T, typename TRef, typename TPtr>
- bool ntree_iterator<T,TRef,TPtr>::operator != (const TYPENAME ntree_iterator<T,TRef,TPtr>::this_iterator& r) const
- {
- return !operator==(r);
- }
-
- template<typename T, typename TRef, typename TPtr>
- bool ntree_iterator<T,TRef,TPtr>::operator < (const TYPENAME ntree_iterator<T,TRef,TPtr>::this_iterator& r) const
- {
- return compare(r) < 0;
- }
-
- template<typename T, typename TRef, typename TPtr>
- TYPENAME ntree_iterator<T,TRef,TPtr>::reference ntree_iterator<T,TRef,TPtr>::operator*(void) const
- throw(null_dereference,end_dereference)
- {
- this->assert_valid();
- return this->node()->m_data;
- }
-
- template<typename T, typename TRef, typename TPtr>
- TYPENAME ntree_iterator<T,TRef,TPtr>::pointer ntree_iterator<T,TRef,TPtr>::operator->(void) const
- throw(null_dereference,end_dereference)
- {
- return &(operator*());
- }
-
- ////////////////////////////////////////////////////////////////////////////////
- // ntree_prefix_iterator
-
- template<typename T, typename TRef, typename TPtr>
- ntree_prefix_iterator<T,TRef,TPtr>::ntree_prefix_iterator(void)
- {
- }
-
- template<typename T, typename TRef, typename TPtr>
- ntree_prefix_iterator<T,TRef,TPtr>::~ntree_prefix_iterator(void)
- {
- }
-
- template<typename T, typename TRef, typename TPtr>
- ntree_prefix_iterator<T,TRef,TPtr>::ntree_prefix_iterator(const ntree_iterator<T,TRef,TPtr>& i) :
- m_iterator(i)
- {
- // this is initialised with the root node
- // which is also the first node in prefix traversal order
- }
-
- template<typename T, typename TRef, typename TPtr>
- bool ntree_prefix_iterator<T,TRef,TPtr>::null(void) const
- {
- return m_iterator.null();
- }
-
- template<typename T, typename TRef, typename TPtr>
- bool ntree_prefix_iterator<T,TRef,TPtr>::end(void) const
- {
- return m_iterator.end();
- }
-
- template<typename T, typename TRef, typename TPtr>
- bool ntree_prefix_iterator<T,TRef,TPtr>::valid(void) const
- {
- return m_iterator.valid();
- }
-
- template<typename T, typename TRef, typename TPtr>
- TYPENAME ntree_prefix_iterator<T,TRef,TPtr>::const_iterator ntree_prefix_iterator<T,TRef,TPtr>::constify(void) const
- {
- return ntree_prefix_iterator<T,const T&,const T*>(m_iterator);
- }
-
- template<typename T, typename TRef, typename TPtr>
- TYPENAME ntree_prefix_iterator<T,TRef,TPtr>::iterator ntree_prefix_iterator<T,TRef,TPtr>::deconstify(void) const
- {
- return ntree_prefix_iterator<T,T&,T*>(m_iterator);
- }
-
- template<typename T, typename TRef, typename TPtr>
- ntree_iterator<T,TRef,TPtr> ntree_prefix_iterator<T,TRef,TPtr>::simplify(void) const
- {
- return m_iterator;
- }
-
- template<typename T, typename TRef, typename TPtr>
- bool ntree_prefix_iterator<T,TRef,TPtr>::operator == (const TYPENAME ntree_prefix_iterator<T,TRef,TPtr>::this_iterator& r) const
- {
- return m_iterator == r.m_iterator;
- }
-
- template<typename T, typename TRef, typename TPtr>
- bool ntree_prefix_iterator<T,TRef,TPtr>::operator != (const TYPENAME ntree_prefix_iterator<T,TRef,TPtr>::this_iterator& r) const
- {
- return m_iterator != r.m_iterator;
- }
-
- template<typename T, typename TRef, typename TPtr>
- bool ntree_prefix_iterator<T,TRef,TPtr>::operator < (const TYPENAME ntree_prefix_iterator<T,TRef,TPtr>::this_iterator& r) const
- {
- return m_iterator < r.m_iterator;
- }
-
- template<typename T, typename TRef, typename TPtr>
- TYPENAME ntree_prefix_iterator<T,TRef,TPtr>::this_iterator& ntree_prefix_iterator<T,TRef,TPtr>::operator ++ (void)
- throw(null_dereference,end_dereference)
- {
- // pre-increment operator
- // algorithm: if there are any children, visit child 0, otherwise, go to
- // parent and deduce which child the start node was of that parent - if
- // there are further children, go into the next one. Otherwise, go up the
- // tree and test again for further children. Return null if there are no
- // further nodes
- m_iterator.assert_valid();
- ntree_node<T>* old_node = m_iterator.node();
- if (!old_node->m_children.empty())
- {
- // simply take the first child of this node
- m_iterator.set(old_node->m_children[0]->m_master);
- }
- else
- {
- // this loop walks up the parent pointers
- // either it will walk off the top and exit or a new node will be found and the loop will exit
- for (;;)
- {
- // go up a level
- ntree_node<T>* parent = old_node->m_parent;
- if (!parent)
- {
- // we've walked off the top of the tree, so return end
- m_iterator.set_end();
- break;
- }
- else
- {
- // otherwise walk down the next child - if there is one
- // find which index the old node was relative to this node
- TYPENAME std::vector<ntree_node<T>*>::iterator found =
- std::find(parent->m_children.begin(), parent->m_children.end(), old_node);
- // if this was found, then see if there is another and if so return that
- found++;
- if (found != parent->m_children.end())
- {
- // visit the next child
- m_iterator.set((*found)->m_master);
- break;
- }
- else
- {
- // keep going up
- old_node = parent;
- }
- }
- }
- }
- return *this;
- }
-
- template<typename T, typename TRef, typename TPtr>
- TYPENAME ntree_prefix_iterator<T,TRef,TPtr>::this_iterator ntree_prefix_iterator<T,TRef,TPtr>::operator ++ (int)
- throw(null_dereference,end_dereference)
- {
- // post-increment is defined in terms of the pre-increment
- ntree_prefix_iterator<T,TRef,TPtr> result(*this);
- ++(*this);
- return result;
- }
-
- template<typename T, typename TRef, typename TPtr>
- TYPENAME ntree_prefix_iterator<T,TRef,TPtr>::reference ntree_prefix_iterator<T,TRef,TPtr>::operator*(void) const
- throw(null_dereference,end_dereference)
- {
- return m_iterator.operator*();
- }
-
- template<typename T, typename TRef, typename TPtr>
- TYPENAME ntree_prefix_iterator<T,TRef,TPtr>::pointer ntree_prefix_iterator<T,TRef,TPtr>::operator->(void) const
- throw(null_dereference,end_dereference)
- {
- return m_iterator.operator->();
- }
-
- template<typename T, typename TRef, typename TPtr>
- const ntree_iterator<T,TRef,TPtr>& ntree_prefix_iterator<T,TRef,TPtr>::get_iterator(void) const
- {
- return m_iterator;
- }
-
- template<typename T, typename TRef, typename TPtr>
- ntree_iterator<T,TRef,TPtr>& ntree_prefix_iterator<T,TRef,TPtr>::get_iterator(void)
- {
- return m_iterator;
- }
-
- ////////////////////////////////////////////////////////////////////////////////
- // ntree_postfix_iterator
-
- template<typename T, typename TRef, typename TPtr>
- ntree_postfix_iterator<T,TRef,TPtr>::ntree_postfix_iterator(void)
- {
- }
-
- template<typename T, typename TRef, typename TPtr>
- ntree_postfix_iterator<T,TRef,TPtr>::~ntree_postfix_iterator(void)
- {
- }
-
- template<typename T, typename TRef, typename TPtr>
- ntree_postfix_iterator<T,TRef,TPtr>::ntree_postfix_iterator(const ntree_iterator<T,TRef,TPtr>& i) :
- m_iterator(i)
- {
- // this is initialised with the root node
- // initially traverse to the first node to be visited
- if (m_iterator.valid())
- {
- ntree_node<T>* node = m_iterator.node();
- while (!node->m_children.empty())
- node = node->m_children[0];
- m_iterator.set(node->m_master);
- }
- }
-
- template<typename T, typename TRef, typename TPtr>
- bool ntree_postfix_iterator<T,TRef,TPtr>::null(void) const
- {
- return m_iterator.null();
- }
-
- template<typename T, typename TRef, typename TPtr>
- bool ntree_postfix_iterator<T,TRef,TPtr>::end(void) const
- {
- return m_iterator.end();
- }
-
- template<typename T, typename TRef, typename TPtr>
- bool ntree_postfix_iterator<T,TRef,TPtr>::valid(void) const
- {
- return m_iterator.valid();
- }
-
- template<typename T, typename TRef, typename TPtr>
- TYPENAME ntree_postfix_iterator<T,TRef,TPtr>::const_iterator ntree_postfix_iterator<T,TRef,TPtr>::constify(void) const
- {
- return ntree_postfix_iterator<T,const T&,const T*>(m_iterator);
- }
-
- template<typename T, typename TRef, typename TPtr>
- TYPENAME ntree_postfix_iterator<T,TRef,TPtr>::iterator ntree_postfix_iterator<T,TRef,TPtr>::deconstify(void) const
- {
- return ntree_postfix_iterator<T,T&,T*>(m_iterator);
- }
-
- template<typename T, typename TRef, typename TPtr>
- ntree_iterator<T,TRef,TPtr> ntree_postfix_iterator<T,TRef,TPtr>::simplify(void) const
- {
- return m_iterator;
- }
-
- template<typename T, typename TRef, typename TPtr>
- bool ntree_postfix_iterator<T,TRef,TPtr>::operator == (const TYPENAME ntree_postfix_iterator<T,TRef,TPtr>::this_iterator& r) const
- {
- return m_iterator == r.m_iterator;
- }
-
- template<typename T, typename TRef, typename TPtr>
- bool ntree_postfix_iterator<T,TRef,TPtr>::operator != (const TYPENAME ntree_postfix_iterator<T,TRef,TPtr>::this_iterator& r) const
- {
- return m_iterator != r.m_iterator;
- }
-
- template<typename T, typename TRef, typename TPtr>
- bool ntree_postfix_iterator<T,TRef,TPtr>::operator < (const TYPENAME ntree_postfix_iterator<T,TRef,TPtr>::this_iterator& r) const
- {
- return m_iterator < r.m_iterator;
- }
-
- template<typename T, typename TRef, typename TPtr>
- TYPENAME ntree_postfix_iterator<T,TRef,TPtr>::this_iterator& ntree_postfix_iterator<T,TRef,TPtr>::operator ++ (void)
- throw(null_dereference,end_dereference)
- {
- // pre-increment operator
- // algorithm: this node has been visited, therefore all children must have
- // already been visited. So go to parent. Return null if the parent is null.
- // Otherwise deduce which child the start node was of that parent - if there
- // are further children, go into the next one and then walk down any
- // subsequent first-child pointers to the bottom. Otherwise, if there are no
- // children then the parent node is the next in the traversal.
- m_iterator.assert_valid();
- // go up a level
- ntree_node<T>* old_node = m_iterator.node();
- ntree_node<T>* parent = old_node->m_parent;
- if (!parent)
- {
- // we've walked off the top of the tree, so return end
- m_iterator.set_end();
- }
- else
- {
- // otherwise find which index the old node was relative to this node
- TYPENAME std::vector<ntree_node<T>*>::iterator found =
- std::find(parent->m_children.begin(), parent->m_children.end(), old_node);
- // if this was found, then see if there is another
- found++;
- if (found != parent->m_children.end())
- {
- // if so traverse to it and walk down the leftmost child pointers to the bottom of the new sub-tree
- ntree_node<T>* new_node = *found;
- while (!new_node->m_children.empty())
- new_node = new_node->m_children[0];
- m_iterator.set(new_node->m_master);
- }
- else
- {
- // the parent's children have all been visited - so the parent is visited
- m_iterator.set(parent->m_master);
- }
- }
- return *this;
- }
-
- template<typename T, typename TRef, typename TPtr>
- TYPENAME ntree_postfix_iterator<T,TRef,TPtr>::this_iterator ntree_postfix_iterator<T,TRef,TPtr>::operator ++ (int)
- throw(null_dereference,end_dereference)
- {
- // post-increment is defined in terms of the pre-increment
- ntree_postfix_iterator<T,TRef,TPtr> result(*this);
- ++(*this);
- return result;
- }
-
- template<typename T, typename TRef, typename TPtr>
- TYPENAME ntree_postfix_iterator<T,TRef,TPtr>::reference ntree_postfix_iterator<T,TRef,TPtr>::operator*(void) const
- throw(null_dereference,end_dereference)
- {
- return m_iterator.operator*();
- }
-
- template<typename T, typename TRef, typename TPtr>
- TYPENAME ntree_postfix_iterator<T,TRef,TPtr>::pointer ntree_postfix_iterator<T,TRef,TPtr>::operator->(void) const
- throw(null_dereference,end_dereference)
- {
- return m_iterator.operator->();
- }
-
- template<typename T, typename TRef, typename TPtr>
- const ntree_iterator<T,TRef,TPtr>& ntree_postfix_iterator<T,TRef,TPtr>::get_iterator(void) const
- {
- return m_iterator;
- }
-
- template<typename T, typename TRef, typename TPtr>
- ntree_iterator<T,TRef,TPtr>& ntree_postfix_iterator<T,TRef,TPtr>::get_iterator(void)
- {
- return m_iterator;
- }
-
- ////////////////////////////////////////////////////////////////////////////////
- ////////////////////////////////////////////////////////////////////////////////
- // ntree
- ////////////////////////////////////////////////////////////////////////////////
- ////////////////////////////////////////////////////////////////////////////////
-
- template<typename T>
- ntree<T>::ntree(void) : m_root(0)
- {
- }
-
- template<typename T>
- ntree<T>::~ntree(void)
- {
- if (m_root) delete m_root;
- }
-
- template<typename T>
- ntree<T>::ntree(const ntree<T>& r) : m_root(0)
- {
- *this = r;
- }
-
- template<typename T>
- ntree<T>& ntree<T>::operator=(const ntree<T>& r)
- {
- if (m_root) delete m_root;
- m_root = ntree_copy(this, r.m_root);
- return *this;
- }
-
- template<typename T>
- bool ntree<T>::empty(void) const
- {
- return m_root == 0;
- }
-
- template<typename T>
- unsigned ntree<T>::size(void) const
- {
- return ntree_size(m_root);
- }
-
- template<typename T>
- unsigned ntree<T>::size(const TYPENAME ntree<T>::const_iterator& i) const
- throw(wrong_object,null_dereference,end_dereference)
- {
- i.assert_valid(this);
- return ntree_size(i.node());
- }
-
- template<typename T>
- unsigned ntree<T>::size(const TYPENAME ntree<T>::iterator& i)
- throw(wrong_object,null_dereference,end_dereference)
- {
- i.assert_valid(this);
- return ntree_size(i.node());
- }
-
- template<typename T>
- unsigned ntree<T>::depth(const TYPENAME ntree<T>::const_iterator& i) const
- throw(wrong_object,null_dereference,end_dereference)
- {
- i.assert_valid(this);
- return ntree_depth(i.node());
- }
-
- template<typename T>
- unsigned ntree<T>::depth(const TYPENAME ntree<T>::iterator& i)
- throw(wrong_object,null_dereference,end_dereference)
- {
- i.assert_valid(this);
- return ntree_depth(i.node());
- }
-
- template<typename T>
- TYPENAME ntree<T>::const_iterator ntree<T>::root(void) const
- {
- if (!m_root) return ntree_iterator<T,const T&,const T*>(this);
- return ntree_iterator<T,const T&,const T*>(m_root);
- }
-
- template<typename T>
- TYPENAME ntree<T>::iterator ntree<T>::root(void)
- {
- if (!m_root) return ntree_iterator<T,T&,T*>(this);
- return ntree_iterator<T,T&,T*>(m_root);
- }
-
- template<typename T>
- unsigned ntree<T>::children(const TYPENAME ntree<T>::const_iterator& i) const
- throw(wrong_object,null_dereference,end_dereference)
- {
- i.assert_valid(this);
- return i.node()->m_children.size();
- }
-
- template<typename T>
- unsigned ntree<T>::children(const ntree_iterator<T,T&,T*>& i)
- throw(wrong_object,null_dereference,end_dereference)
- {
- i.assert_valid(this);
- return i.node()->m_children.size();
- }
-
- template<typename T>
- TYPENAME ntree<T>::const_iterator ntree<T>::child(const TYPENAME ntree<T>::const_iterator& i, unsigned child) const
- throw(wrong_object,null_dereference,end_dereference,std::out_of_range)
- {
- i.assert_valid(this);
- if (child >= children(i)) throw std::out_of_range("stlplus::ntree");
- return ntree_iterator<T,const T&,const T*>(i.node()->m_children[child]);
- }
-
- template<typename T>
- TYPENAME ntree<T>::iterator ntree<T>::child(const TYPENAME ntree<T>::iterator& i, unsigned child)
- throw(wrong_object,null_dereference,end_dereference,std::out_of_range)
- {
- i.assert_valid(this);
- if (child >= children(i)) throw std::out_of_range("stlplus::ntree");
- return ntree_iterator<T,T&,T*>(i.node()->m_children[child]);
- }
-
- template<typename T>
- TYPENAME ntree<T>::const_iterator ntree<T>::parent(const TYPENAME ntree<T>::const_iterator& i) const
- throw(wrong_object,null_dereference,end_dereference)
- {
- i.assert_valid(this);
- ntree_node<T>* parent = i.node()->m_parent;
- if (!parent) return ntree_iterator<T,const T&,const T*>(this);
- return ntree_iterator<T,const T&,const T*>(parent);
- }
-
- template<typename T>
- TYPENAME ntree<T>::iterator ntree<T>::parent(const TYPENAME ntree<T>::iterator& i)
- throw(wrong_object,null_dereference,end_dereference)
- {
- i.assert_valid(this);
- ntree_node<T>* parent = i.node()->m_parent;
- if (!parent) return ntree_iterator<T,T&,T*>(this);
- return ntree_iterator<T,T&,T*>(parent);
- }
-
- template<typename T>
- TYPENAME ntree<T>::const_prefix_iterator ntree<T>::prefix_begin(void) const
- {
- return ntree_prefix_iterator<T,const T&,const T*>(root());
- }
-
- template<typename T>
- TYPENAME ntree<T>::prefix_iterator ntree<T>::prefix_begin(void)
- {
- return ntree_prefix_iterator<T,T&,T*>(root());
- }
-
- template<typename T>
- TYPENAME ntree<T>::const_prefix_iterator ntree<T>::prefix_end(void) const
- {
- return ntree_prefix_iterator<T,const T&,const T*>(ntree_iterator<T,const T&,const T*>(this));
- }
-
- template<typename T>
- TYPENAME ntree<T>::prefix_iterator ntree<T>::prefix_end(void)
- {
- return ntree_prefix_iterator<T,T&,T*>(ntree_iterator<T,T&,T*>(this));
- }
-
- template<typename T>
- TYPENAME ntree<T>::const_postfix_iterator ntree<T>::postfix_begin(void) const
- {
- return ntree_postfix_iterator<T,const T&,const T*>(root());
- }
-
- template<typename T>
- TYPENAME ntree<T>::postfix_iterator ntree<T>::postfix_begin(void)
- {
- return ntree_postfix_iterator<T,T&,T*>(root());
- }
-
- template<typename T>
- TYPENAME ntree<T>::const_postfix_iterator ntree<T>::postfix_end(void) const
- {
- return ntree_postfix_iterator<T,const T&,const T*>(ntree_iterator<T,const T&,const T*>(this));
- }
-
- template<typename T>
- TYPENAME ntree<T>::postfix_iterator ntree<T>::postfix_end(void)
- {
- return ntree_postfix_iterator<T,T&,T*>(ntree_iterator<T,T&,T*>(this));
- }
-
- template<typename T>
- TYPENAME ntree<T>::iterator ntree<T>::insert(const T& data)
- {
- // insert a new node as the root
- return insert(ntree_iterator<T,T&,T*>(this), 0, data);
- }
-
- template<typename T>
- TYPENAME ntree<T>::iterator ntree<T>::insert(const TYPENAME ntree<T>::iterator& i, unsigned offset, const T& data)
- throw(wrong_object,null_dereference,end_dereference,std::out_of_range)
- {
- // if i is the end iterator, this means insert a new root
- if (i.end())
- erase();
- else
- {
- i.assert_valid(this);
- if (offset > children(i)) throw std::out_of_range("stlplus::ntree");
- }
- ntree_node<T>* new_node = new ntree_node<T>(this,data);
- if (i.end())
- {
- m_root = new_node;
- }
- else
- {
- i.node()->m_children.insert(i.node()->m_children.begin()+offset,new_node);
- new_node->m_parent = i.node();
- }
- return ntree_iterator<T,T&,T*>(new_node);
- }
-
- template<typename T>
- TYPENAME ntree<T>::iterator ntree<T>::append(const TYPENAME ntree<T>::iterator& i, const T& data)
- throw(wrong_object,null_dereference,end_dereference)
- {
- return insert(i, i.node()->m_children.size(), data);
- }
-
- template<typename T>
- TYPENAME ntree<T>::iterator ntree<T>::insert(const TYPENAME ntree<T>::iterator& i, unsigned offset, const ntree<T>& tree)
- throw(wrong_object,null_dereference,end_dereference,std::out_of_range)
- {
- // insert a whole tree as a child of i
- i.assert_valid(this);
- if (offset > children(i)) throw std::out_of_range("stlplus::ntree");
- ntree_node<T>* new_node = ntree_copy(this, tree.m_root);
- i.node()->m_children.insert(i.node()->m_children.begin()+offset,new_node);
- new_node->m_parent = i.node();
- return ntree_iterator<T,T&,T*>(new_node);
- }
-
- template<typename T>
- TYPENAME ntree<T>::iterator ntree<T>::append(const TYPENAME ntree<T>::iterator& i, const ntree<T>& tree)
- throw(wrong_object,null_dereference,end_dereference)
- {
- return insert(i, children(i), tree);
- }
-
- template<typename T>
- TYPENAME ntree<T>::iterator ntree<T>::push(const TYPENAME ntree<T>::iterator& node, const T& data)
- throw(wrong_object,null_dereference,end_dereference)
- {
- // insert a new node to replace the existing node in the tree
- // making the original node the child of the new node
- // i.e. (node) becomes (new)->(node)
- // afterwards, the iterator still points to the old node, now the child
- // returns the iterator to the new node
- node.assert_valid(this);
- ntree_node<T>* new_node = new ntree_node<T>(this,data);
- if (node.node() == m_root)
- {
- // pushing the root node
- m_root = new_node;
- new_node->m_parent = 0;
- }
- else
- {
- // pushing a sub-node
- *(std::find(node.node()->m_parent->m_children.begin(), node.node()->m_parent->m_children.end(), node.node())) = new_node;
- new_node->m_parent = node.node()->m_parent;
- }
- // link up the old node as the child of the new node
- new_node->m_children.insert(new_node->m_children.begin(),node.node());
- node.node()->m_parent = new_node;
- return ntree_iterator<T,T&,T*>(new_node);
- }
-
- template<typename T>
- void ntree<T>::pop(const TYPENAME ntree<T>::iterator& parent, unsigned offset)
- throw(wrong_object,null_dereference,end_dereference)
- {
- // inverse of push
- // removes the specified child of the parent node, adding its children to the parent node at the same offset
- parent.assert_valid(this);
- ntree_node<T>* node = parent.node();
- if (offset >= node->m_children.size()) throw std::out_of_range("stlplus::ntree");
- // move the grandchildren first
- ntree_node<T>* child = parent.node()->m_children[offset];
- while (!child->m_children.empty())
- {
- // remove the last grandchild and insert into node just after the child to be removed
- ntree_node<T>* grandchild = child->m_children[child->m_children.size()-1];
- child->m_children.pop_back();
- node->m_children.insert(node->m_children.begin()+offset+1, grandchild);
- grandchild->m_parent = node;
- }
- // now remove the child
- node->m_children.erase(node->m_children.begin()+offset);
- delete child;
- }
-
- template<typename T>
- void ntree<T>::erase(void)
- {
- // erase the whole tree
- erase(root());
- }
-
- template<typename T>
- void ntree<T>::erase(const TYPENAME ntree<T>::iterator& i)
- throw(wrong_object,null_dereference,end_dereference)
- {
- if (!i.end())
- {
- // erase this node and its subtree
- // do this by erasing this child of its parent
- // handle the case of erasing the root
- i.assert_valid(this);
- ntree_node<T>* node = i.node();
- if (node == m_root)
- {
- delete m_root;
- m_root = 0;
- }
- else
- {
- ntree_node<T>* parent = node->m_parent;
- // impossible for parent to be null - should assert this
- TYPENAME std::vector<ntree_node<T>*>::iterator found =
- std::find(parent->m_children.begin(), parent->m_children.end(), node);
- // impossible for find to fail - should assert this
- parent->m_children.erase(found);
- delete node;
- }
- }
- }
-
- template<typename T>
- void ntree<T>::erase(const TYPENAME ntree<T>::iterator& i, unsigned offset)
- throw(wrong_object,null_dereference,end_dereference,std::out_of_range)
- {
- erase(child(i, offset));
- }
-
- template<typename T>
- ntree<T> ntree<T>::subtree(void)
- {
- return subtree(root());
- }
-
- template<typename T>
- ntree<T> ntree<T>::subtree(const TYPENAME ntree<T>::iterator& i)
- throw(wrong_object,null_dereference,end_dereference)
- {
- ntree<T> result;
- if (!i.end())
- {
- i.assert_valid(this);
- result.m_root = ntree_copy(&result, i.node());
- }
- return result;
- }
-
- template<typename T>
- ntree<T> ntree<T>::subtree(const TYPENAME ntree<T>::iterator& i, unsigned offset)
- throw(wrong_object,null_dereference,end_dereference,std::out_of_range)
- {
- return subtree(child(i, offset));
- }
-
- template<typename T>
- ntree<T> ntree<T>::cut(void)
- {
- return cut(root());
- }
-
- template<typename T>
- ntree<T> ntree<T>::cut(const TYPENAME ntree<T>::iterator& i)
- throw(wrong_object,null_dereference,end_dereference)
- {
- ntree<T> result;
- if (!i.end())
- {
- i.assert_valid(this);
- ntree_node<T>* node = i.node();
- if (node == m_root)
- {
- result.m_root = m_root;
- m_root = 0;
- }
- else
- {
- ntree_node<T>* parent = node->m_parent;
- // impossible for parent to be null - should assert this
- TYPENAME std::vector<ntree_node<T>*>::iterator found =
- std::find(parent->m_children.begin(), parent->m_children.end(), node);
- // impossible for find to fail - should assert this
- result.m_root = *found;
- parent->m_children.erase(found);
- }
- if (result.m_root)
- {
- result.m_root->m_parent = 0;
- result.m_root->set_new_owner(&result);
- }
- }
- return result;
- }
-
- template<typename T>
- ntree<T> ntree<T>::cut(const TYPENAME ntree<T>::iterator& i, unsigned offset)
- throw(wrong_object,null_dereference,end_dereference,std::out_of_range)
- {
- return cut(child(i, offset));
- }
-
- ////////////////////////////////////////////////////////////////////////////////
-
-} // end namespace stlplus
-
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+// Author: Andy Rushton\r
+// Copyright: (c) Southampton University 1999-2004\r
+// (c) Andy Rushton 2004 onwards\r
+// License: BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include <vector>\r
+#include <algorithm>\r
+\r
+namespace stlplus \r
+{\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // ntree_node\r
+\r
+ template<typename T>\r
+ class ntree_node\r
+ {\r
+ public:\r
+ master_iterator<ntree<T>, ntree_node<T> > m_master;\r
+ T m_data;\r
+ ntree_node<T>* m_parent;\r
+ std::vector<ntree_node<T>*> m_children;\r
+\r
+ public:\r
+ ntree_node(const ntree<T>* owner, const T& data = T()) :\r
+ m_master(owner,this), m_data(data), m_parent(0)\r
+ {\r
+ }\r
+\r
+ void change_owner(const ntree<T>* owner)\r
+ {\r
+ m_master.change_owner(owner);\r
+ for (TYPENAME std::vector<ntree_node<T>*>::iterator i = m_children.begin(); i != m_children.end(); i++)\r
+ (*i)->change_owner(owner);\r
+ }\r
+\r
+ ~ntree_node(void)\r
+ {\r
+ m_parent = 0;\r
+ for (TYPENAME std::vector<ntree_node<T>*>::iterator i = m_children.begin(); i != m_children.end(); i++)\r
+ delete *i;\r
+ }\r
+\r
+ };\r
+\r
+ template<typename T>\r
+ static ntree_node<T>* ntree_copy(const ntree<T>* new_owner, ntree_node<T>* root)\r
+ {\r
+ if (!root) return 0;\r
+ ntree_node<T>* new_tree = new ntree_node<T>(new_owner, root->m_data);\r
+ for (TYPENAME std::vector<ntree_node<T>*>::iterator i = root->m_children.begin(); i != root->m_children.end(); i++)\r
+ {\r
+ ntree_node<T>* new_child = ntree_copy(new_owner, *i);\r
+ new_tree->m_children.push_back(new_child);\r
+ new_child->m_parent = new_tree;\r
+ }\r
+ return new_tree;\r
+ }\r
+\r
+ template<typename T>\r
+ static unsigned ntree_size(ntree_node<T>* root)\r
+ {\r
+ if (!root) return 0;\r
+ unsigned result = 1;\r
+ for (TYPENAME std::vector<ntree_node<T>*>::iterator i = root->m_children.begin(); i != root->m_children.end(); i++)\r
+ result += ntree_size(*i);\r
+ return result;\r
+ }\r
+\r
+ template<typename T>\r
+ static unsigned ntree_depth(ntree_node<T>* root)\r
+ {\r
+ unsigned depth = 0;\r
+ for (ntree_node<T>* i = root; i; i = i->m_parent)\r
+ depth++;\r
+ return depth;\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // ntree_iterator\r
+\r
+ // constructor to create a null iterator - you must assign a valid value to this iterator before using it\r
+ template<typename T, typename TRef, typename TPtr>\r
+ ntree_iterator<T,TRef,TPtr>::ntree_iterator(void)\r
+ {\r
+ }\r
+\r
+ // used to create an alias of an iterator\r
+ template<typename T, typename TRef, typename TPtr>\r
+ ntree_iterator<T,TRef,TPtr>::ntree_iterator(const safe_iterator<ntree<T>, ntree_node<T> >& iterator) :\r
+ safe_iterator<ntree<T>,ntree_node<T> >(iterator)\r
+ {\r
+ }\r
+\r
+ // constructor used by ntree to create a non-null iterator\r
+ template<typename T, typename TRef, typename TPtr>\r
+ ntree_iterator<T,TRef,TPtr>::ntree_iterator(ntree_node<T>* node) :\r
+ safe_iterator<ntree<T>,ntree_node<T> >(node->m_master)\r
+ {\r
+ }\r
+\r
+ // constructor used by ntree to create an end iterator\r
+ template<typename T, typename TRef, typename TPtr>\r
+ ntree_iterator<T,TRef,TPtr>::ntree_iterator(const ntree<T>* owner) :\r
+ safe_iterator<ntree<T>,ntree_node<T> >(owner)\r
+ {\r
+ }\r
+\r
+ // destructor\r
+ template<typename T, typename TRef, typename TPtr>\r
+ ntree_iterator<T,TRef,TPtr>::~ntree_iterator(void)\r
+ {\r
+ }\r
+\r
+ template<typename T, typename TRef, typename TPtr>\r
+ TYPENAME ntree_iterator<T,TRef,TPtr>::const_iterator ntree_iterator<T,TRef,TPtr>::constify(void) const\r
+ {\r
+ return ntree_iterator<T,const T&,const T*>(*this);\r
+ }\r
+\r
+ template<typename T, typename TRef, typename TPtr>\r
+ TYPENAME ntree_iterator<T,TRef,TPtr>::iterator ntree_iterator<T,TRef,TPtr>::deconstify(void) const\r
+ {\r
+ return ntree_iterator<T,T&,T*>(*this);\r
+ }\r
+\r
+ template<typename T, typename TRef, typename TPtr>\r
+ bool ntree_iterator<T,TRef,TPtr>::operator == (const TYPENAME ntree_iterator<T,TRef,TPtr>::this_iterator& r) const\r
+ {\r
+ return equal(r);\r
+ }\r
+\r
+ template<typename T, typename TRef, typename TPtr>\r
+ bool ntree_iterator<T,TRef,TPtr>::operator != (const TYPENAME ntree_iterator<T,TRef,TPtr>::this_iterator& r) const\r
+ {\r
+ return !operator==(r);\r
+ }\r
+\r
+ template<typename T, typename TRef, typename TPtr>\r
+ bool ntree_iterator<T,TRef,TPtr>::operator < (const TYPENAME ntree_iterator<T,TRef,TPtr>::this_iterator& r) const\r
+ {\r
+ return compare(r) < 0;\r
+ }\r
+\r
+ template<typename T, typename TRef, typename TPtr>\r
+ TYPENAME ntree_iterator<T,TRef,TPtr>::reference ntree_iterator<T,TRef,TPtr>::operator*(void) const\r
+ throw(null_dereference,end_dereference)\r
+ {\r
+ this->assert_valid();\r
+ return this->node()->m_data;\r
+ }\r
+\r
+ template<typename T, typename TRef, typename TPtr>\r
+ TYPENAME ntree_iterator<T,TRef,TPtr>::pointer ntree_iterator<T,TRef,TPtr>::operator->(void) const\r
+ throw(null_dereference,end_dereference)\r
+ {\r
+ return &(operator*());\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // ntree_prefix_iterator\r
+\r
+ template<typename T, typename TRef, typename TPtr>\r
+ ntree_prefix_iterator<T,TRef,TPtr>::ntree_prefix_iterator(void)\r
+ {\r
+ }\r
+\r
+ template<typename T, typename TRef, typename TPtr>\r
+ ntree_prefix_iterator<T,TRef,TPtr>::~ntree_prefix_iterator(void)\r
+ {\r
+ }\r
+\r
+ template<typename T, typename TRef, typename TPtr>\r
+ ntree_prefix_iterator<T,TRef,TPtr>::ntree_prefix_iterator(const ntree_iterator<T,TRef,TPtr>& i) :\r
+ m_iterator(i)\r
+ {\r
+ // this is initialised with the root node\r
+ // which is also the first node in prefix traversal order\r
+ }\r
+\r
+ template<typename T, typename TRef, typename TPtr>\r
+ bool ntree_prefix_iterator<T,TRef,TPtr>::null(void) const\r
+ {\r
+ return m_iterator.null();\r
+ }\r
+\r
+ template<typename T, typename TRef, typename TPtr>\r
+ bool ntree_prefix_iterator<T,TRef,TPtr>::end(void) const\r
+ {\r
+ return m_iterator.end();\r
+ }\r
+\r
+ template<typename T, typename TRef, typename TPtr>\r
+ bool ntree_prefix_iterator<T,TRef,TPtr>::valid(void) const\r
+ {\r
+ return m_iterator.valid();\r
+ }\r
+\r
+ template<typename T, typename TRef, typename TPtr>\r
+ TYPENAME ntree_prefix_iterator<T,TRef,TPtr>::const_iterator ntree_prefix_iterator<T,TRef,TPtr>::constify(void) const\r
+ {\r
+ return ntree_prefix_iterator<T,const T&,const T*>(m_iterator);\r
+ }\r
+\r
+ template<typename T, typename TRef, typename TPtr>\r
+ TYPENAME ntree_prefix_iterator<T,TRef,TPtr>::iterator ntree_prefix_iterator<T,TRef,TPtr>::deconstify(void) const\r
+ {\r
+ return ntree_prefix_iterator<T,T&,T*>(m_iterator);\r
+ }\r
+\r
+ template<typename T, typename TRef, typename TPtr>\r
+ ntree_iterator<T,TRef,TPtr> ntree_prefix_iterator<T,TRef,TPtr>::simplify(void) const\r
+ {\r
+ return m_iterator;\r
+ }\r
+\r
+ template<typename T, typename TRef, typename TPtr>\r
+ bool ntree_prefix_iterator<T,TRef,TPtr>::operator == (const TYPENAME ntree_prefix_iterator<T,TRef,TPtr>::this_iterator& r) const\r
+ {\r
+ return m_iterator == r.m_iterator;\r
+ }\r
+\r
+ template<typename T, typename TRef, typename TPtr>\r
+ bool ntree_prefix_iterator<T,TRef,TPtr>::operator != (const TYPENAME ntree_prefix_iterator<T,TRef,TPtr>::this_iterator& r) const\r
+ {\r
+ return m_iterator != r.m_iterator;\r
+ }\r
+\r
+ template<typename T, typename TRef, typename TPtr>\r
+ bool ntree_prefix_iterator<T,TRef,TPtr>::operator < (const TYPENAME ntree_prefix_iterator<T,TRef,TPtr>::this_iterator& r) const\r
+ {\r
+ return m_iterator < r.m_iterator;\r
+ }\r
+\r
+ template<typename T, typename TRef, typename TPtr>\r
+ TYPENAME ntree_prefix_iterator<T,TRef,TPtr>::this_iterator& ntree_prefix_iterator<T,TRef,TPtr>::operator ++ (void)\r
+ throw(null_dereference,end_dereference)\r
+ {\r
+ // pre-increment operator\r
+ // algorithm: if there are any children, visit child 0, otherwise, go to\r
+ // parent and deduce which child the start node was of that parent - if\r
+ // there are further children, go into the next one. Otherwise, go up the\r
+ // tree and test again for further children. Return null if there are no\r
+ // further nodes\r
+ m_iterator.assert_valid();\r
+ ntree_node<T>* old_node = m_iterator.node();\r
+ if (!old_node->m_children.empty())\r
+ {\r
+ // simply take the first child of this node\r
+ m_iterator.set(old_node->m_children[0]->m_master);\r
+ }\r
+ else\r
+ {\r
+ // this loop walks up the parent pointers\r
+ // either it will walk off the top and exit or a new node will be found and the loop will exit\r
+ for (;;)\r
+ {\r
+ // go up a level\r
+ ntree_node<T>* parent = old_node->m_parent;\r
+ if (!parent)\r
+ {\r
+ // we've walked off the top of the tree, so return end\r
+ m_iterator.set_end();\r
+ break;\r
+ }\r
+ else\r
+ {\r
+ // otherwise walk down the next child - if there is one\r
+ // find which index the old node was relative to this node\r
+ TYPENAME std::vector<ntree_node<T>*>::iterator found = \r
+ std::find(parent->m_children.begin(), parent->m_children.end(), old_node);\r
+ // if this was found, then see if there is another and if so return that\r
+ found++;\r
+ if (found != parent->m_children.end())\r
+ {\r
+ // visit the next child\r
+ m_iterator.set((*found)->m_master);\r
+ break;\r
+ }\r
+ else\r
+ {\r
+ // keep going up\r
+ old_node = parent;\r
+ }\r
+ }\r
+ }\r
+ }\r
+ return *this;\r
+ }\r
+\r
+ template<typename T, typename TRef, typename TPtr>\r
+ TYPENAME ntree_prefix_iterator<T,TRef,TPtr>::this_iterator ntree_prefix_iterator<T,TRef,TPtr>::operator ++ (int)\r
+ throw(null_dereference,end_dereference)\r
+ {\r
+ // post-increment is defined in terms of the pre-increment\r
+ ntree_prefix_iterator<T,TRef,TPtr> result(*this);\r
+ ++(*this);\r
+ return result;\r
+ }\r
+\r
+ template<typename T, typename TRef, typename TPtr>\r
+ TYPENAME ntree_prefix_iterator<T,TRef,TPtr>::reference ntree_prefix_iterator<T,TRef,TPtr>::operator*(void) const\r
+ throw(null_dereference,end_dereference)\r
+ {\r
+ return m_iterator.operator*();\r
+ }\r
+\r
+ template<typename T, typename TRef, typename TPtr>\r
+ TYPENAME ntree_prefix_iterator<T,TRef,TPtr>::pointer ntree_prefix_iterator<T,TRef,TPtr>::operator->(void) const\r
+ throw(null_dereference,end_dereference)\r
+ {\r
+ return m_iterator.operator->();\r
+ }\r
+\r
+ template<typename T, typename TRef, typename TPtr>\r
+ const ntree_iterator<T,TRef,TPtr>& ntree_prefix_iterator<T,TRef,TPtr>::get_iterator(void) const\r
+ {\r
+ return m_iterator;\r
+ }\r
+\r
+ template<typename T, typename TRef, typename TPtr>\r
+ ntree_iterator<T,TRef,TPtr>& ntree_prefix_iterator<T,TRef,TPtr>::get_iterator(void)\r
+ {\r
+ return m_iterator;\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // ntree_postfix_iterator\r
+\r
+ template<typename T, typename TRef, typename TPtr>\r
+ ntree_postfix_iterator<T,TRef,TPtr>::ntree_postfix_iterator(void)\r
+ {\r
+ }\r
+\r
+ template<typename T, typename TRef, typename TPtr>\r
+ ntree_postfix_iterator<T,TRef,TPtr>::~ntree_postfix_iterator(void)\r
+ {\r
+ }\r
+\r
+ template<typename T, typename TRef, typename TPtr>\r
+ ntree_postfix_iterator<T,TRef,TPtr>::ntree_postfix_iterator(const ntree_iterator<T,TRef,TPtr>& i) :\r
+ m_iterator(i)\r
+ {\r
+ // this is initialised with the root node\r
+ // initially traverse to the first node to be visited\r
+ if (m_iterator.valid())\r
+ {\r
+ ntree_node<T>* node = m_iterator.node();\r
+ while (!node->m_children.empty())\r
+ node = node->m_children[0];\r
+ m_iterator.set(node->m_master);\r
+ }\r
+ }\r
+\r
+ template<typename T, typename TRef, typename TPtr>\r
+ bool ntree_postfix_iterator<T,TRef,TPtr>::null(void) const\r
+ {\r
+ return m_iterator.null();\r
+ }\r
+\r
+ template<typename T, typename TRef, typename TPtr>\r
+ bool ntree_postfix_iterator<T,TRef,TPtr>::end(void) const\r
+ {\r
+ return m_iterator.end();\r
+ }\r
+\r
+ template<typename T, typename TRef, typename TPtr>\r
+ bool ntree_postfix_iterator<T,TRef,TPtr>::valid(void) const\r
+ {\r
+ return m_iterator.valid();\r
+ }\r
+\r
+ template<typename T, typename TRef, typename TPtr>\r
+ TYPENAME ntree_postfix_iterator<T,TRef,TPtr>::const_iterator ntree_postfix_iterator<T,TRef,TPtr>::constify(void) const\r
+ {\r
+ return ntree_postfix_iterator<T,const T&,const T*>(m_iterator);\r
+ }\r
+\r
+ template<typename T, typename TRef, typename TPtr>\r
+ TYPENAME ntree_postfix_iterator<T,TRef,TPtr>::iterator ntree_postfix_iterator<T,TRef,TPtr>::deconstify(void) const\r
+ {\r
+ return ntree_postfix_iterator<T,T&,T*>(m_iterator);\r
+ }\r
+\r
+ template<typename T, typename TRef, typename TPtr>\r
+ ntree_iterator<T,TRef,TPtr> ntree_postfix_iterator<T,TRef,TPtr>::simplify(void) const\r
+ {\r
+ return m_iterator;\r
+ }\r
+\r
+ template<typename T, typename TRef, typename TPtr>\r
+ bool ntree_postfix_iterator<T,TRef,TPtr>::operator == (const TYPENAME ntree_postfix_iterator<T,TRef,TPtr>::this_iterator& r) const\r
+ {\r
+ return m_iterator == r.m_iterator;\r
+ }\r
+\r
+ template<typename T, typename TRef, typename TPtr>\r
+ bool ntree_postfix_iterator<T,TRef,TPtr>::operator != (const TYPENAME ntree_postfix_iterator<T,TRef,TPtr>::this_iterator& r) const\r
+ {\r
+ return m_iterator != r.m_iterator;\r
+ }\r
+\r
+ template<typename T, typename TRef, typename TPtr>\r
+ bool ntree_postfix_iterator<T,TRef,TPtr>::operator < (const TYPENAME ntree_postfix_iterator<T,TRef,TPtr>::this_iterator& r) const\r
+ {\r
+ return m_iterator < r.m_iterator;\r
+ }\r
+\r
+ template<typename T, typename TRef, typename TPtr>\r
+ TYPENAME ntree_postfix_iterator<T,TRef,TPtr>::this_iterator& ntree_postfix_iterator<T,TRef,TPtr>::operator ++ (void)\r
+ throw(null_dereference,end_dereference)\r
+ {\r
+ // pre-increment operator\r
+ // algorithm: this node has been visited, therefore all children must have\r
+ // already been visited. So go to parent. Return null if the parent is null.\r
+ // Otherwise deduce which child the start node was of that parent - if there\r
+ // are further children, go into the next one and then walk down any\r
+ // subsequent first-child pointers to the bottom. Otherwise, if there are no\r
+ // children then the parent node is the next in the traversal.\r
+ m_iterator.assert_valid();\r
+ // go up a level\r
+ ntree_node<T>* old_node = m_iterator.node();\r
+ ntree_node<T>* parent = old_node->m_parent;\r
+ if (!parent)\r
+ {\r
+ // we've walked off the top of the tree, so return end\r
+ m_iterator.set_end();\r
+ }\r
+ else\r
+ {\r
+ // otherwise find which index the old node was relative to this node\r
+ TYPENAME std::vector<ntree_node<T>*>::iterator found =\r
+ std::find(parent->m_children.begin(), parent->m_children.end(), old_node);\r
+ // if this was found, then see if there is another\r
+ found++;\r
+ if (found != parent->m_children.end())\r
+ {\r
+ // if so traverse to it and walk down the leftmost child pointers to the bottom of the new sub-tree\r
+ ntree_node<T>* new_node = *found;\r
+ while (!new_node->m_children.empty())\r
+ new_node = new_node->m_children[0];\r
+ m_iterator.set(new_node->m_master);\r
+ }\r
+ else\r
+ {\r
+ // the parent's children have all been visited - so the parent is visited\r
+ m_iterator.set(parent->m_master);\r
+ }\r
+ }\r
+ return *this;\r
+ }\r
+\r
+ template<typename T, typename TRef, typename TPtr>\r
+ TYPENAME ntree_postfix_iterator<T,TRef,TPtr>::this_iterator ntree_postfix_iterator<T,TRef,TPtr>::operator ++ (int)\r
+ throw(null_dereference,end_dereference)\r
+ {\r
+ // post-increment is defined in terms of the pre-increment\r
+ ntree_postfix_iterator<T,TRef,TPtr> result(*this);\r
+ ++(*this);\r
+ return result;\r
+ }\r
+\r
+ template<typename T, typename TRef, typename TPtr>\r
+ TYPENAME ntree_postfix_iterator<T,TRef,TPtr>::reference ntree_postfix_iterator<T,TRef,TPtr>::operator*(void) const\r
+ throw(null_dereference,end_dereference)\r
+ {\r
+ return m_iterator.operator*();\r
+ }\r
+\r
+ template<typename T, typename TRef, typename TPtr>\r
+ TYPENAME ntree_postfix_iterator<T,TRef,TPtr>::pointer ntree_postfix_iterator<T,TRef,TPtr>::operator->(void) const\r
+ throw(null_dereference,end_dereference)\r
+ {\r
+ return m_iterator.operator->();\r
+ }\r
+\r
+ template<typename T, typename TRef, typename TPtr>\r
+ const ntree_iterator<T,TRef,TPtr>& ntree_postfix_iterator<T,TRef,TPtr>::get_iterator(void) const\r
+ {\r
+ return m_iterator;\r
+ }\r
+\r
+ template<typename T, typename TRef, typename TPtr>\r
+ ntree_iterator<T,TRef,TPtr>& ntree_postfix_iterator<T,TRef,TPtr>::get_iterator(void)\r
+ {\r
+ return m_iterator;\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // ntree\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+\r
+ template<typename T>\r
+ ntree<T>::ntree(void) : m_root(0)\r
+ {\r
+ }\r
+\r
+ template<typename T>\r
+ ntree<T>::~ntree(void)\r
+ {\r
+ if (m_root) delete m_root;\r
+ }\r
+\r
+ template<typename T>\r
+ ntree<T>::ntree(const ntree<T>& r) : m_root(0)\r
+ {\r
+ *this = r;\r
+ }\r
+\r
+ template<typename T>\r
+ ntree<T>& ntree<T>::operator=(const ntree<T>& r)\r
+ {\r
+ if (m_root) delete m_root;\r
+ m_root = ntree_copy(this, r.m_root);\r
+ return *this;\r
+ }\r
+\r
+ template<typename T>\r
+ bool ntree<T>::empty(void) const\r
+ {\r
+ return m_root == 0;\r
+ }\r
+\r
+ template<typename T>\r
+ unsigned ntree<T>::size(void) const\r
+ {\r
+ return ntree_size(m_root);\r
+ }\r
+\r
+ template<typename T>\r
+ unsigned ntree<T>::size(const TYPENAME ntree<T>::const_iterator& i) const\r
+ throw(wrong_object,null_dereference,end_dereference)\r
+ {\r
+ i.assert_valid(this);\r
+ return ntree_size(i.node());\r
+ }\r
+\r
+ template<typename T>\r
+ unsigned ntree<T>::size(const TYPENAME ntree<T>::iterator& i)\r
+ throw(wrong_object,null_dereference,end_dereference)\r
+ {\r
+ i.assert_valid(this);\r
+ return ntree_size(i.node());\r
+ }\r
+\r
+ template<typename T>\r
+ unsigned ntree<T>::depth(const TYPENAME ntree<T>::const_iterator& i) const\r
+ throw(wrong_object,null_dereference,end_dereference)\r
+ {\r
+ i.assert_valid(this);\r
+ return ntree_depth(i.node());\r
+ }\r
+\r
+ template<typename T>\r
+ unsigned ntree<T>::depth(const TYPENAME ntree<T>::iterator& i)\r
+ throw(wrong_object,null_dereference,end_dereference)\r
+ {\r
+ i.assert_valid(this);\r
+ return ntree_depth(i.node());\r
+ }\r
+\r
+ template<typename T>\r
+ TYPENAME ntree<T>::const_iterator ntree<T>::root(void) const\r
+ {\r
+ if (!m_root) return ntree_iterator<T,const T&,const T*>(this);\r
+ return ntree_iterator<T,const T&,const T*>(m_root);\r
+ }\r
+\r
+ template<typename T>\r
+ TYPENAME ntree<T>::iterator ntree<T>::root(void)\r
+ {\r
+ if (!m_root) return ntree_iterator<T,T&,T*>(this);\r
+ return ntree_iterator<T,T&,T*>(m_root);\r
+ }\r
+\r
+ template<typename T>\r
+ unsigned ntree<T>::children(const TYPENAME ntree<T>::const_iterator& i) const\r
+ throw(wrong_object,null_dereference,end_dereference)\r
+ {\r
+ i.assert_valid(this);\r
+ return i.node()->m_children.size();\r
+ }\r
+\r
+ template<typename T>\r
+ unsigned ntree<T>::children(const ntree_iterator<T,T&,T*>& i)\r
+ throw(wrong_object,null_dereference,end_dereference)\r
+ {\r
+ i.assert_valid(this);\r
+ return i.node()->m_children.size();\r
+ }\r
+\r
+ template<typename T>\r
+ TYPENAME ntree<T>::const_iterator ntree<T>::child(const TYPENAME ntree<T>::const_iterator& i, unsigned child) const\r
+ throw(wrong_object,null_dereference,end_dereference,std::out_of_range)\r
+ {\r
+ i.assert_valid(this);\r
+ if (child >= children(i)) throw std::out_of_range("stlplus::ntree");\r
+ return ntree_iterator<T,const T&,const T*>(i.node()->m_children[child]);\r
+ }\r
+\r
+ template<typename T>\r
+ TYPENAME ntree<T>::iterator ntree<T>::child(const TYPENAME ntree<T>::iterator& i, unsigned child)\r
+ throw(wrong_object,null_dereference,end_dereference,std::out_of_range)\r
+ {\r
+ i.assert_valid(this);\r
+ if (child >= children(i)) throw std::out_of_range("stlplus::ntree");\r
+ return ntree_iterator<T,T&,T*>(i.node()->m_children[child]);\r
+ }\r
+\r
+ template<typename T>\r
+ TYPENAME ntree<T>::const_iterator ntree<T>::parent(const TYPENAME ntree<T>::const_iterator& i) const\r
+ throw(wrong_object,null_dereference,end_dereference)\r
+ {\r
+ i.assert_valid(this);\r
+ ntree_node<T>* parent = i.node()->m_parent;\r
+ if (!parent) return ntree_iterator<T,const T&,const T*>(this);\r
+ return ntree_iterator<T,const T&,const T*>(parent);\r
+ }\r
+\r
+ template<typename T>\r
+ TYPENAME ntree<T>::iterator ntree<T>::parent(const TYPENAME ntree<T>::iterator& i)\r
+ throw(wrong_object,null_dereference,end_dereference)\r
+ {\r
+ i.assert_valid(this);\r
+ ntree_node<T>* parent = i.node()->m_parent;\r
+ if (!parent) return ntree_iterator<T,T&,T*>(this);\r
+ return ntree_iterator<T,T&,T*>(parent);\r
+ }\r
+\r
+ template<typename T>\r
+ TYPENAME ntree<T>::const_prefix_iterator ntree<T>::prefix_begin(void) const\r
+ {\r
+ return ntree_prefix_iterator<T,const T&,const T*>(root());\r
+ }\r
+\r
+ template<typename T>\r
+ TYPENAME ntree<T>::prefix_iterator ntree<T>::prefix_begin(void)\r
+ {\r
+ return ntree_prefix_iterator<T,T&,T*>(root());\r
+ }\r
+\r
+ template<typename T>\r
+ TYPENAME ntree<T>::const_prefix_iterator ntree<T>::prefix_end(void) const\r
+ {\r
+ return ntree_prefix_iterator<T,const T&,const T*>(ntree_iterator<T,const T&,const T*>(this));\r
+ }\r
+\r
+ template<typename T>\r
+ TYPENAME ntree<T>::prefix_iterator ntree<T>::prefix_end(void)\r
+ {\r
+ return ntree_prefix_iterator<T,T&,T*>(ntree_iterator<T,T&,T*>(this));\r
+ }\r
+\r
+ template<typename T>\r
+ TYPENAME ntree<T>::const_postfix_iterator ntree<T>::postfix_begin(void) const\r
+ {\r
+ return ntree_postfix_iterator<T,const T&,const T*>(root());\r
+ }\r
+\r
+ template<typename T>\r
+ TYPENAME ntree<T>::postfix_iterator ntree<T>::postfix_begin(void)\r
+ {\r
+ return ntree_postfix_iterator<T,T&,T*>(root());\r
+ }\r
+\r
+ template<typename T>\r
+ TYPENAME ntree<T>::const_postfix_iterator ntree<T>::postfix_end(void) const\r
+ {\r
+ return ntree_postfix_iterator<T,const T&,const T*>(ntree_iterator<T,const T&,const T*>(this));\r
+ }\r
+\r
+ template<typename T>\r
+ TYPENAME ntree<T>::postfix_iterator ntree<T>::postfix_end(void)\r
+ {\r
+ return ntree_postfix_iterator<T,T&,T*>(ntree_iterator<T,T&,T*>(this));\r
+ }\r
+\r
+ template<typename T>\r
+ TYPENAME ntree<T>::iterator ntree<T>::insert(const T& data)\r
+ {\r
+ // insert a new node as the root\r
+ erase();\r
+ m_root = new ntree_node<T>(this,data);\r
+ return ntree_iterator<T,T&,T*>(m_root);\r
+ }\r
+\r
+ template<typename T>\r
+ TYPENAME ntree<T>::iterator ntree<T>::insert(const TYPENAME ntree<T>::iterator& i, unsigned offset, const T& data)\r
+ throw(wrong_object,null_dereference,end_dereference,std::out_of_range)\r
+ {\r
+ // if i is the end iterator, this means insert a new root\r
+ // if (i.end())\r
+ // return insert(data);\r
+ // otherwise, insert a new child\r
+ i.assert_valid(this);\r
+ if (offset > children(i)) throw std::out_of_range("stlplus::ntree");\r
+ ntree_node<T>* new_node = new ntree_node<T>(this,data);\r
+ i.node()->m_children.insert(i.node()->m_children.begin()+offset,new_node);\r
+ new_node->m_parent = i.node();\r
+ return ntree_iterator<T,T&,T*>(new_node);\r
+ }\r
+\r
+ template<typename T>\r
+ TYPENAME ntree<T>::iterator ntree<T>::insert(const TYPENAME ntree<T>::iterator& i, const T& data)\r
+ throw(wrong_object,null_dereference,end_dereference)\r
+ {\r
+ return insert(i, children(i), data);\r
+ }\r
+\r
+ template<typename T>\r
+ TYPENAME ntree<T>::iterator ntree<T>::append(const TYPENAME ntree<T>::iterator& i, const T& data)\r
+ throw(wrong_object,null_dereference,end_dereference)\r
+ {\r
+ return insert(i, children(i), data);\r
+ }\r
+\r
+ template<typename T>\r
+ TYPENAME ntree<T>::iterator ntree<T>::insert(const ntree<T>& tree)\r
+ {\r
+ // insert a whole tree as root\r
+ erase();\r
+ m_root = ntree_copy(this, tree.m_root);\r
+ return ntree_iterator<T,T&,T*>(m_root);\r
+ }\r
+\r
+ template<typename T>\r
+ TYPENAME ntree<T>::iterator ntree<T>::insert(const TYPENAME ntree<T>::iterator& i, unsigned offset, const ntree<T>& tree)\r
+ throw(wrong_object,null_dereference,end_dereference,std::out_of_range)\r
+ {\r
+ // insert a whole tree as a child of i\r
+ i.assert_valid(this);\r
+ if (offset > children(i)) throw std::out_of_range("stlplus::ntree");\r
+ ntree_node<T>* new_node = ntree_copy(this, tree.m_root);\r
+ i.node()->m_children.insert(i.node()->m_children.begin()+offset,new_node);\r
+ new_node->m_parent = i.node();\r
+ return ntree_iterator<T,T&,T*>(new_node);\r
+ }\r
+\r
+ template<typename T>\r
+ TYPENAME ntree<T>::iterator ntree<T>::insert(const TYPENAME ntree<T>::iterator& i, const ntree<T>& tree)\r
+ throw(wrong_object,null_dereference,end_dereference)\r
+ {\r
+ return insert(i, children(i), tree);\r
+ }\r
+\r
+ template<typename T>\r
+ TYPENAME ntree<T>::iterator ntree<T>::append(const TYPENAME ntree<T>::iterator& i, const ntree<T>& tree)\r
+ throw(wrong_object,null_dereference,end_dereference)\r
+ {\r
+ return insert(i, children(i), tree);\r
+ }\r
+\r
+ template<typename T>\r
+ TYPENAME ntree<T>::iterator ntree<T>::move(ntree<T>& tree)\r
+ {\r
+ // insert a whole tree as root, removing it from source\r
+ erase();\r
+ m_root = tree.m_root;\r
+ tree.m_root = 0;\r
+ if (m_root) m_root->change_owner(this);\r
+ return ntree_iterator<T,T&,T*>(m_root);\r
+ }\r
+\r
+ template<typename T>\r
+ TYPENAME ntree<T>::iterator ntree<T>::move(const TYPENAME ntree<T>::iterator& i, unsigned offset, ntree<T>& tree)\r
+ throw(wrong_object,null_dereference,end_dereference,std::out_of_range)\r
+ {\r
+ // insert a whole tree as a child of i\r
+ i.assert_valid(this);\r
+ if (offset > children(i)) throw std::out_of_range("stlplus::ntree");\r
+ ntree_node<T>* new_node = tree.m_root;\r
+ tree.m_root = 0;\r
+ if (new_node) new_node->change_owner(this);\r
+ i.node()->m_children.insert(i.node()->m_children.begin()+offset,new_node);\r
+ new_node->m_parent = i.node();\r
+ return ntree_iterator<T,T&,T*>(new_node);\r
+ }\r
+\r
+ template<typename T>\r
+ TYPENAME ntree<T>::iterator ntree<T>::move(const TYPENAME ntree<T>::iterator& i, ntree<T>& tree)\r
+ throw(wrong_object,null_dereference,end_dereference)\r
+ {\r
+ return move(i, children(i), tree);\r
+ }\r
+\r
+ template<typename T>\r
+ TYPENAME ntree<T>::iterator ntree<T>::push(const TYPENAME ntree<T>::iterator& node, const T& data)\r
+ throw(wrong_object,null_dereference,end_dereference)\r
+ {\r
+ // insert a new node to replace the existing node in the tree\r
+ // making the original node the child of the new node\r
+ // i.e. (node) becomes (new)->(node)\r
+ // afterwards, the iterator still points to the old node, now the child\r
+ // returns the iterator to the new node\r
+ node.assert_valid(this);\r
+ ntree_node<T>* new_node = new ntree_node<T>(this,data);\r
+ if (node.node() == m_root)\r
+ {\r
+ // pushing the root node\r
+ m_root = new_node;\r
+ new_node->m_parent = 0;\r
+ }\r
+ else\r
+ {\r
+ // pushing a sub-node\r
+ *(std::find(node.node()->m_parent->m_children.begin(), node.node()->m_parent->m_children.end(), node.node())) = new_node;\r
+ new_node->m_parent = node.node()->m_parent;\r
+ }\r
+ // link up the old node as the child of the new node\r
+ new_node->m_children.insert(new_node->m_children.begin(),node.node());\r
+ node.node()->m_parent = new_node;\r
+ return ntree_iterator<T,T&,T*>(new_node);\r
+ }\r
+\r
+ template<typename T>\r
+ void ntree<T>::pop(const TYPENAME ntree<T>::iterator& parent, unsigned offset)\r
+ throw(wrong_object,null_dereference,end_dereference)\r
+ {\r
+ // inverse of push\r
+ // removes the specified child of the parent node, adding its children to the parent node at the same offset\r
+ parent.assert_valid(this);\r
+ ntree_node<T>* node = parent.node();\r
+ if (offset >= node->m_children.size()) throw std::out_of_range("stlplus::ntree");\r
+ // move the grandchildren first\r
+ ntree_node<T>* child = parent.node()->m_children[offset];\r
+ while (!child->m_children.empty())\r
+ {\r
+ // remove the last grandchild and insert into node just after the child to be removed\r
+ ntree_node<T>* grandchild = child->m_children[child->m_children.size()-1];\r
+ child->m_children.pop_back();\r
+ node->m_children.insert(node->m_children.begin()+offset+1, grandchild);\r
+ grandchild->m_parent = node;\r
+ }\r
+ // now remove the child\r
+ node->m_children.erase(node->m_children.begin()+offset);\r
+ delete child;\r
+ }\r
+\r
+ template<typename T>\r
+ void ntree<T>::erase(void)\r
+ {\r
+ // erase the whole tree\r
+ erase(root());\r
+ }\r
+\r
+ template<typename T>\r
+ void ntree<T>::erase(const TYPENAME ntree<T>::iterator& i)\r
+ throw(wrong_object,null_dereference,end_dereference)\r
+ {\r
+ if (!i.end())\r
+ {\r
+ // erase this node and its subtree\r
+ // do this by erasing this child of its parent\r
+ // handle the case of erasing the root\r
+ i.assert_valid(this);\r
+ ntree_node<T>* node = i.node();\r
+ if (node == m_root)\r
+ {\r
+ delete m_root;\r
+ m_root = 0;\r
+ }\r
+ else\r
+ {\r
+ ntree_node<T>* parent = node->m_parent;\r
+ // impossible for parent to be null - should assert this\r
+ TYPENAME std::vector<ntree_node<T>*>::iterator found = \r
+ std::find(parent->m_children.begin(), parent->m_children.end(), node);\r
+ // impossible for find to fail - should assert this\r
+ parent->m_children.erase(found);\r
+ delete node;\r
+ }\r
+ }\r
+ }\r
+\r
+ template<typename T>\r
+ void ntree<T>::erase(const TYPENAME ntree<T>::iterator& i, unsigned offset)\r
+ throw(wrong_object,null_dereference,end_dereference,std::out_of_range)\r
+ {\r
+ erase(child(i, offset));\r
+ }\r
+\r
+ template<typename T>\r
+ ntree<T> ntree<T>::subtree(void)\r
+ {\r
+ return subtree(root());\r
+ }\r
+\r
+ template<typename T>\r
+ ntree<T> ntree<T>::subtree(const TYPENAME ntree<T>::iterator& i)\r
+ throw(wrong_object,null_dereference,end_dereference)\r
+ {\r
+ ntree<T> result;\r
+ if (!i.end())\r
+ {\r
+ i.assert_valid(this);\r
+ result.m_root = ntree_copy(&result, i.node());\r
+ }\r
+ return result;\r
+ }\r
+\r
+ template<typename T>\r
+ ntree<T> ntree<T>::subtree(const TYPENAME ntree<T>::iterator& i, unsigned offset)\r
+ throw(wrong_object,null_dereference,end_dereference,std::out_of_range)\r
+ {\r
+ return subtree(child(i, offset));\r
+ }\r
+\r
+ template<typename T>\r
+ ntree<T> ntree<T>::cut(void)\r
+ {\r
+ return cut(root());\r
+ }\r
+\r
+ template<typename T>\r
+ ntree<T> ntree<T>::cut(const TYPENAME ntree<T>::iterator& i)\r
+ throw(wrong_object,null_dereference,end_dereference)\r
+ {\r
+ ntree<T> result;\r
+ if (!i.end())\r
+ {\r
+ i.assert_valid(this);\r
+ ntree_node<T>* node = i.node();\r
+ if (node == m_root)\r
+ {\r
+ result.m_root = m_root;\r
+ m_root = 0;\r
+ }\r
+ else\r
+ {\r
+ ntree_node<T>* parent = node->m_parent;\r
+ // impossible for parent to be null - should assert this\r
+ TYPENAME std::vector<ntree_node<T>*>::iterator found = \r
+ std::find(parent->m_children.begin(), parent->m_children.end(), node);\r
+ // impossible for find to fail - should assert this\r
+ result.m_root = *found;\r
+ parent->m_children.erase(found);\r
+ }\r
+ if (result.m_root)\r
+ {\r
+ result.m_root->m_parent = 0;\r
+ result.m_root->set_new_owner(&result);\r
+ }\r
+ }\r
+ return result;\r
+ }\r
+\r
+ template<typename T>\r
+ ntree<T> ntree<T>::cut(const TYPENAME ntree<T>::iterator& i, unsigned offset)\r
+ throw(wrong_object,null_dereference,end_dereference,std::out_of_range)\r
+ {\r
+ return cut(child(i, offset));\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
+\r
-#ifndef STLPLUS_SAFE_ITERATOR
-#define STLPLUS_SAFE_ITERATOR
-////////////////////////////////////////////////////////////////////////////////
-
-// Author: Andy Rushton
-// Copyright: (c) Southampton University 1999-2004
-// (c) Andy Rushton 2004-2009
-// License: BSD License, see ../docs/license.html
-
-// The STLplus safe_iterator superclasses. This implements the STLplus safe
-// iterator principles. Data structures can then be built using subclasses
-// of safe_iterator for their iterator objects and they will inherit the
-// safe iterator behaviour.
-
-// The data structure must contain a master iterator for each node in the
-// structure. When an iterator is returned to the user, it must be created
-// by the master iterator. When a node is removed from the data structure,
-// its master iterator is destroyed. This sets all iterators pointing to the
-// master iterator to end iterators.
-
-////////////////////////////////////////////////////////////////////////////////
-#include "containers_fixes.hpp"
-#include "exceptions.hpp"
-
-namespace stlplus
-{
-
- ////////////////////////////////////////////////////////////////////////////////
- // internals
-
- template<typename O, typename N>
- class safe_iterator_body;
-
- template<typename O, typename N>
- class safe_iterator;
-
- ////////////////////////////////////////////////////////////////////////////////
- // Master Iterator
- // Create one of these in each node in the data structure
- // Generate iterators by obtaining a safe-iterator object from the master iterator
- ////////////////////////////////////////////////////////////////////////////////
-
- template<typename O, typename N>
- class master_iterator
- {
- public:
-
- // construct a valid master iterator connected to the node
- master_iterator(const O* owner, N* node) throw();
-
- // destructor - disconnects all iterators from the node
- ~master_iterator(void) throw();
-
- // dereference
- N* node(void) const throw();
- const O* owner(void) const throw();
-
- // when you move a node from one owner to another, call this on the node's master iterator
- // this effectively moves all other iterators to the node so that they are owned by the new owner too
- void change_owner(const O* owner) throw();
-
- friend class safe_iterator<O,N>;
- private:
- master_iterator(const master_iterator&) throw();
- master_iterator& operator=(const master_iterator&) throw();
- safe_iterator_body<O,N>* m_body;
- };
-
- ////////////////////////////////////////////////////////////////////////////////
- // Safe Iterator
- ////////////////////////////////////////////////////////////////////////////////
-
- template<typename O, typename N>
- class safe_iterator
- {
- public:
-
- // construct a null iterator
- safe_iterator(void) throw();
-
- // construct a valid iterator by aliasing from the owner node's master iterator
- safe_iterator(const master_iterator<O,N>&) throw();
-
- // copy constructor does aliasing
- safe_iterator(const safe_iterator<O,N>&) throw();
-
- // alias an iterator by assignment
- safe_iterator<O,N>& operator=(const safe_iterator<O,N>&) throw();
-
- // destructor
- ~safe_iterator(void) throw();
-
- // reassignment to another node used in increment/decrement operation
- void set(const master_iterator<O,N>&) throw();
-
- // dereference
- N* node(void) const throw();
- const O* owner(void) const throw();
-
- // change to a null iterator - i.e. one that does not belong to any object
- // this does not affect any other iterators pointing to the same node
- void set_null(void) throw();
-
- ////////////////////////////////////////////////////////////////////////////////
- // operations for clients that do not have a master end iterator
- // alternatively, have a master end iterator as part of the container
- // and call constructor(master_end) or set(master_end)
-
- // construct an end iterator
- safe_iterator(const O* owner) throw();
-
- // change to an end iterator - e.g. as a result of incrementing off the end
- void set_end(void) throw();
-
- ////////////////////////////////////////////////////////////////////////////////
- // tests
-
- // comparison
- bool equal(const safe_iterator<O,N>& right) const throw();
- int compare(const safe_iterator<O,N>& right) const throw();
-
- // a null iterator is one that has not been initialised with a value yet
- // i.e. you just declared it but didn't assign to it
- bool null(void) const throw();
-
- // an end iterator is one that points to the end element of the list of nodes
- // in STL conventions this is one past the last valid element and must not be dereferenced
- bool end(void) const throw();
-
- // a valid iterator is one that can be dereferenced
- // i.e. non-null and non-end
- bool valid(void) const throw();
-
- // check the rules for a valid iterator that can be dereferenced
- // optionally also check that the iterator is owned by the owner
- void assert_valid(void) const throw(null_dereference,end_dereference);
- void assert_valid(const O* owner) const throw(wrong_object,null_dereference,end_dereference);
- // assert the rules for a non-null iterator - i.e. valid or end, values that occur in increment operations
- void assert_non_null(void) const throw(null_dereference);
- // assert that this iterator is owned by this container
- void assert_owner(const O* owner) const throw(wrong_object);
-
- ////////////////////////////////////////////////////////////////////////////////
-
- friend class master_iterator<O,N>;
- private:
- safe_iterator_body<O,N>* m_body;
- };
-
- ////////////////////////////////////////////////////////////////////////////////
-
-} // end namespace stlplus
-
-#include "safe_iterator.tpp"
-#endif
+#ifndef STLPLUS_SAFE_ITERATOR\r
+#define STLPLUS_SAFE_ITERATOR\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+// Author: Andy Rushton\r
+// Copyright: (c) Southampton University 1999-2004\r
+// (c) Andy Rushton 2004 onwards\r
+// License: BSD License, see ../docs/license.html\r
+\r
+// The STLplus safe_iterator superclasses. This implements the STLplus safe\r
+// iterator principles. Data structures can then be built using subclasses\r
+// of safe_iterator for their iterator objects and they will inherit the\r
+// safe iterator behaviour.\r
+\r
+// The data structure must contain a master iterator for each node in the\r
+// structure. When an iterator is returned to the user, it must be created\r
+// by the master iterator. When a node is removed from the data structure,\r
+// its master iterator is destroyed. This sets all iterators pointing to the\r
+// master iterator to end iterators.\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "containers_fixes.hpp"\r
+#include "exceptions.hpp"\r
+\r
+namespace stlplus\r
+{\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // internals\r
+\r
+ template<typename O, typename N>\r
+ class safe_iterator_body;\r
+\r
+ template<typename O, typename N>\r
+ class safe_iterator;\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // Master Iterator\r
+ // Create one of these in each node in the data structure\r
+ // Generate iterators by obtaining a safe-iterator object from the master iterator\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+\r
+ template<typename O, typename N>\r
+ class master_iterator\r
+ {\r
+ public:\r
+\r
+ // construct a valid master iterator connected to the node\r
+ master_iterator(const O* owner, N* node) throw();\r
+\r
+ // destructor - disconnects all iterators from the node\r
+ ~master_iterator(void) throw();\r
+\r
+ // dereference\r
+ N* node(void) const throw();\r
+ const O* owner(void) const throw();\r
+\r
+ // when you move a node from one owner to another, call this on the node's master iterator\r
+ // this effectively moves all other iterators to the node so that they are owned by the new owner too\r
+ void change_owner(const O* owner) throw();\r
+\r
+ friend class safe_iterator<O,N>;\r
+ private:\r
+ master_iterator(const master_iterator&) throw();\r
+ master_iterator& operator=(const master_iterator&) throw();\r
+ safe_iterator_body<O,N>* m_body;\r
+ };\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // Safe Iterator\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+\r
+ template<typename O, typename N>\r
+ class safe_iterator\r
+ {\r
+ public:\r
+\r
+ // construct a null iterator\r
+ safe_iterator(void) throw();\r
+\r
+ // construct a valid iterator by aliasing from the owner node's master iterator\r
+ safe_iterator(const master_iterator<O,N>&) throw();\r
+\r
+ // copy constructor does aliasing\r
+ safe_iterator(const safe_iterator<O,N>&) throw();\r
+\r
+ // alias an iterator by assignment\r
+ safe_iterator<O,N>& operator=(const safe_iterator<O,N>&) throw();\r
+\r
+ // destructor\r
+ ~safe_iterator(void) throw();\r
+\r
+ // reassignment to another node used in increment/decrement operation\r
+ void set(const master_iterator<O,N>&) throw();\r
+\r
+ // dereference\r
+ N* node(void) const throw();\r
+ const O* owner(void) const throw();\r
+\r
+ // change to a null iterator - i.e. one that does not belong to any object\r
+ // this does not affect any other iterators pointing to the same node\r
+ void set_null(void) throw();\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // operations for clients that do not have a master end iterator\r
+ // alternatively, have a master end iterator as part of the container\r
+ // and call constructor(master_end) or set(master_end)\r
+\r
+ // construct an end iterator\r
+ safe_iterator(const O* owner) throw();\r
+\r
+ // change to an end iterator - e.g. as a result of incrementing off the end\r
+ void set_end(void) throw();\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // tests\r
+\r
+ // comparison\r
+ bool equal(const safe_iterator<O,N>& right) const throw();\r
+ int compare(const safe_iterator<O,N>& right) const throw();\r
+\r
+ // a null iterator is one that has not been initialised with a value yet\r
+ // i.e. you just declared it but didn't assign to it\r
+ bool null(void) const throw();\r
+\r
+ // an end iterator is one that points to the end element of the list of nodes\r
+ // in STL conventions this is one past the last valid element and must not be dereferenced\r
+ bool end(void) const throw();\r
+\r
+ // a valid iterator is one that can be dereferenced\r
+ // i.e. non-null and non-end\r
+ bool valid(void) const throw();\r
+\r
+ // check the rules for a valid iterator that can be dereferenced\r
+ // optionally also check that the iterator is owned by the owner\r
+ void assert_valid(void) const throw(null_dereference,end_dereference);\r
+ void assert_valid(const O* owner) const throw(wrong_object,null_dereference,end_dereference);\r
+ // assert the rules for a non-null iterator - i.e. valid or end, values that occur in increment operations\r
+ void assert_non_null(void) const throw(null_dereference);\r
+ // assert that this iterator is owned by this container\r
+ void assert_owner(const O* owner) const throw(wrong_object);\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+\r
+ friend class master_iterator<O,N>;\r
+ private:\r
+ safe_iterator_body<O,N>* m_body;\r
+ };\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
+\r
+#include "safe_iterator.tpp"\r
+#endif\r
-namespace stlplus
-{
-
- ////////////////////////////////////////////////////////////////////////////////
- // body class implements the aliasing behaviour
-
- template<typename O, typename N>
- class safe_iterator_body
- {
- private:
- const O* m_owner;
- N* m_node;
- unsigned m_count;
-
- public:
-
- safe_iterator_body(const O* owner, N* node) throw() :
- m_owner(owner), m_node(node), m_count(1)
- {
-// std::cerr << "constructing "
-// << std::hex << ((unsigned long)this)
-// << " => " << ((unsigned long)m_owner) << ":" << ((unsigned long)m_node)
-// << ":" << std::dec << m_count << std::endl;
- }
-
- ~safe_iterator_body(void) throw()
- {
-// std::cerr << "destroying "
-// << std::hex << ((unsigned long)this)
-// << " => " << ((unsigned long)m_owner) << ":" << ((unsigned long)m_node)
-// << ":" << std::dec << m_count << std::endl;
- m_owner = 0;
- m_node = 0;
- }
-
- unsigned count(void) const
- {
- return m_count;
- }
-
- void increment(void)
- {
- ++m_count;
-// std::cerr << "incremented "
-// << std::hex << ((unsigned long)this)
-// << " => " << ((unsigned long)m_owner) << ":" << ((unsigned long)m_node)
-// << ":" << std::dec << m_count << std::endl;
- }
-
- bool decrement(void)
- {
- --m_count;
-// std::cerr << "decremented "
-// << std::hex << ((unsigned long)this)
-// << " => " << ((unsigned long)m_owner) << ":" << ((unsigned long)m_node)
-// << ":" << std::dec << m_count << std::endl;
- return m_count == 0;
- }
-
- N* node(void) const throw()
- {
- return m_node;
- }
-
- const O* owner(void) const throw()
- {
- return m_owner;
- }
-
- void change_owner(const O* owner)
- {
- m_owner = owner;
- }
-
- bool equal(const safe_iterator_body<O,N>* right) const throw()
- {
-// return m_node == right->m_node;
- return compare(right) == 0;
- }
-
- int compare(const safe_iterator_body<O,N>* right) const throw()
- {
- return ((long)m_node) - ((long)right->m_node);
- }
-
- bool null(void) const throw()
- {
- return m_owner == 0;
- }
-
- bool end(void) const throw()
- {
- return m_owner != 0 && m_node == 0;
- }
-
- bool valid(void) const throw()
- {
- return m_owner != 0 && m_node != 0;
- }
-
- void set_end(void) throw()
- {
- m_node = 0;
- }
-
- void set_null(void) throw()
- {
- m_owner = 0;
- m_node = 0;
- }
-
- void assert_valid(void) const throw(null_dereference,end_dereference)
- {
- if (null())
- throw null_dereference("stlplus::safe_iterator");
- if (end())
- throw end_dereference("stlplus::safe_iterator");
- }
-
- void assert_non_null(void) const throw(null_dereference)
- {
- if (null())
- throw null_dereference("stlplus::safe_iterator");
- }
-
- void assert_owner(const O* owner) const throw(wrong_object)
- {
- if (owner != m_owner)
- throw wrong_object("stlplus::safe_iterator");
- }
- };
-
-
- ////////////////////////////////////////////////////////////////////////////////
- // Master Iterator
- ////////////////////////////////////////////////////////////////////////////////
-
- // construct a valid iterator
- template<typename O, typename N>
- master_iterator<O,N>::master_iterator(const O* owner, N* node) throw() :
- m_body(new safe_iterator_body<O,N>(owner,node))
- {
- }
-
- // destructor - disconnect all iterators from the node
- // this usually happens when the node is deleted and must invalidate all aliases
- template<typename O, typename N>
- master_iterator<O,N>::~master_iterator(void) throw()
- {
- m_body->set_end();
- if(m_body->decrement())
- {
- delete m_body;
- m_body = 0;
- }
- }
-
- // dereference
- template<typename O, typename N>
- N* master_iterator<O,N>::node(void) const throw()
- {
- return m_body->node();
- }
-
- template<typename O, typename N>
- const O* master_iterator<O,N>::owner(void) const throw()
- {
- return m_body->owner();
- }
-
- // when you move a node from one owner to another, call this on the node's iterator
- // this effectively moves all iterators to the node so that they are owned by the new owner too
- template<typename O, typename N>
- void master_iterator<O,N>::change_owner(const O* owner) throw()
- {
- m_body->change_owner(owner);
- }
-
- ////////////////////////////////////////////////////////////////////////////////
- // Safe Iterator
- ////////////////////////////////////////////////////////////////////////////////
-
- // construct a null iterator
- // later assignment of a valid iterator to this is done by using step
- template<typename O, typename N>
- safe_iterator<O,N>::safe_iterator(void) throw() :
- m_body(new safe_iterator_body<O,N>(0,0))
- {
- }
-
- // construct a valid iterator by aliasing from the owner node's master iterator
- template<typename O, typename N>
- safe_iterator<O,N>::safe_iterator(const master_iterator<O,N>& r) throw() :
- m_body(0)
- {
- m_body = r.m_body;
- m_body->increment();
- }
-
- // construct a valid iterator by aliasing from the owner node's master iterator
- template<typename O, typename N>
- safe_iterator<O,N>::safe_iterator(const safe_iterator<O,N>& r) throw() :
- m_body(0)
- {
- m_body = r.m_body;
- m_body->increment();
- }
-
- // assignment implements dealiasing followed by aliasing
- template<typename O, typename N>
- safe_iterator<O,N>& safe_iterator<O,N>::operator=(const safe_iterator<O,N>& r) throw()
- {
- if (m_body != r.m_body)
- {
- if (m_body->decrement())
- delete m_body;
- m_body = r.m_body;
- m_body->increment();
- }
- return *this;
- }
-
- // destructor - implements dealiasing
- template<typename O, typename N>
- safe_iterator<O,N>::~safe_iterator(void) throw()
- {
- if(m_body->decrement())
- {
- delete m_body;
- m_body = 0;
- }
- }
-
-
- // increment/decrement operation
- // implements dealiasing followed by aliasing
- template<typename O, typename N>
- void safe_iterator<O,N>::set(const master_iterator<O,N>& r) throw()
- {
- if (m_body != r.m_body)
- {
- if (m_body->decrement())
- delete m_body;
- m_body = r.m_body;
- m_body->increment();
- }
- }
-
- // dereference
- template<typename O, typename N>
- N* safe_iterator<O,N>::node(void) const throw()
- {
- return m_body->node();
- }
-
- template<typename O, typename N>
- const O* safe_iterator<O,N>::owner(void) const throw()
- {
- return m_body->owner();
- }
-
- // change to a null iterator - i.e. one that doees not belong to any object
- // this does not affect any other iterators pointing to the same node
- template<typename O, typename N>
- void safe_iterator<O,N>::set_null(void) throw()
- {
- if (m_body->count() == 1)
- {
- // no aliases, so just make this null
- m_body->set_null();
- }
- else
- {
- // create a new body which is null so as not to affect any other aliases
- m_body->decrement();
- m_body = new safe_iterator_body<O,N>(0,0);
- }
- }
-
- ////////////////////////////////////////////////////////////////////////////////
- // operations for clients that do not have a master end iterator
- // alternatively, have a master end iterator as part of the container
- // and call constructor(master_end) or step(master_end)
-
- // construct an end iterator
- template<typename O, typename N>
- safe_iterator<O,N>::safe_iterator(const O* owner) throw() :
- m_body(new safe_iterator_body<O,N>(owner,0))
- {
- }
-
- // change to an end iterator - e.g. as a result of incrementing off the end
- template<typename O, typename N>
- void safe_iterator<O,N>::set_end(void) throw()
- {
- if (m_body->count() == 1)
- {
- // no aliases, so just make this an end iterator
- m_body->set_end();
- }
- else
- {
- // create a new body which is null so as not to affect any other aliases
- m_body->decrement();
- m_body = new safe_iterator_body<O,N>(owner(),0);
- }
- }
-
- ////////////////////////////////////////////////////////////////////////////////
- // tests
-
- // comparison
- template<typename O, typename N>
- bool safe_iterator<O,N>::equal(const safe_iterator<O,N>& right) const throw()
- {
- return compare(right) == 0;
- }
-
- template<typename O, typename N>
- int safe_iterator<O,N>::compare(const safe_iterator<O,N>& right) const throw()
- {
- if (m_body == right.m_body) return 0;
- return m_body->compare(right.m_body);
- }
-
- // a null iterator is one that has not been initialised with a value yet
- template<typename O, typename N>
- bool safe_iterator<O,N>::null(void) const throw()
- {
- return m_body->null();
- }
-
- // an end iterator is one that points to the end element of the list of nodes
- template<typename O, typename N>
- bool safe_iterator<O,N>::end(void) const throw()
- {
- return m_body->end();
- }
-
- // a valid iterator is one that can be dereferenced
- template<typename O, typename N>
- bool safe_iterator<O,N>::valid(void) const throw()
- {
- return m_body->valid();
- }
-
- // check the rules for a valid iterator that can be dereferenced
- template<typename O, typename N>
- void safe_iterator<O,N>::assert_valid(void) const throw(null_dereference,end_dereference)
- {
- m_body->assert_valid();
- }
-
- template<typename O, typename N>
- void safe_iterator<O,N>::assert_valid(const O* owner) const throw(wrong_object,null_dereference,end_dereference)
- {
- m_body->assert_valid();
- m_body->assert_owner(owner);
- }
-
- template<typename O, typename N>
- void safe_iterator<O,N>::assert_non_null(void) const throw(null_dereference)
- {
- m_body->assert_non_null();
- }
-
- template<typename O, typename N>
- void safe_iterator<O,N>::assert_owner(const O* owner) const throw(wrong_object)
- {
- m_body->assert_owner(owner);
- }
-
-} // end namespace stlplus
+namespace stlplus\r
+{\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // body class implements the aliasing behaviour\r
+\r
+ template<typename O, typename N>\r
+ class safe_iterator_body\r
+ {\r
+ private:\r
+ const O* m_owner;\r
+ N* m_node;\r
+ unsigned m_count;\r
+\r
+ public:\r
+\r
+ safe_iterator_body(const O* owner, N* node) throw() : \r
+ m_owner(owner), m_node(node), m_count(1)\r
+ {\r
+ }\r
+\r
+ ~safe_iterator_body(void) throw()\r
+ {\r
+ m_owner = 0;\r
+ m_node = 0;\r
+ }\r
+\r
+ unsigned count(void) const\r
+ {\r
+ return m_count;\r
+ }\r
+\r
+ void increment(void)\r
+ {\r
+ ++m_count;\r
+ }\r
+\r
+ bool decrement(void)\r
+ {\r
+ --m_count;\r
+ return m_count == 0;\r
+ }\r
+\r
+ N* node(void) const throw()\r
+ {\r
+ return m_node;\r
+ }\r
+\r
+ const O* owner(void) const throw()\r
+ {\r
+ return m_owner;\r
+ }\r
+\r
+ void change_owner(const O* owner)\r
+ {\r
+ m_owner = owner;\r
+ }\r
+\r
+ bool equal(const safe_iterator_body<O,N>* right) const throw()\r
+ {\r
+ return m_node == right->m_node;\r
+ }\r
+\r
+ int compare(const safe_iterator_body<O,N>* right) const throw()\r
+ {\r
+ if (m_node == right->m_node) return 0;\r
+ return (m_node < right->m_node) ? -1 : 1;\r
+ }\r
+\r
+ bool null(void) const throw()\r
+ {\r
+ return m_owner == 0;\r
+ }\r
+\r
+ bool end(void) const throw()\r
+ {\r
+ return m_owner != 0 && m_node == 0;\r
+ }\r
+\r
+ bool valid(void) const throw()\r
+ {\r
+ return m_owner != 0 && m_node != 0;\r
+ }\r
+\r
+ void set_end(void) throw()\r
+ {\r
+ m_node = 0;\r
+ }\r
+\r
+ void set_null(void) throw()\r
+ {\r
+ m_owner = 0;\r
+ m_node = 0;\r
+ }\r
+\r
+ void assert_valid(void) const throw(null_dereference,end_dereference)\r
+ {\r
+ if (null())\r
+ throw null_dereference("stlplus::safe_iterator: dereferencing null iterator");\r
+ if (end())\r
+ throw end_dereference("stlplus::safe_iterator: dereferencing end iterator");\r
+ }\r
+\r
+ void assert_non_null(void) const throw(null_dereference)\r
+ {\r
+ if (null())\r
+ throw null_dereference("stlplus::safe_iterator: dereferencing null iterator");\r
+ }\r
+\r
+ void assert_owner(const O* owner) const throw(wrong_object)\r
+ {\r
+ if (owner != m_owner)\r
+ throw wrong_object("stlplus::safe_iterator: using iterator with wrong object");\r
+ }\r
+ };\r
+\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // Master Iterator\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+\r
+ // construct a valid iterator\r
+ template<typename O, typename N>\r
+ master_iterator<O,N>::master_iterator(const O* owner, N* node) throw() :\r
+ m_body(new safe_iterator_body<O,N>(owner,node))\r
+ {\r
+ }\r
+\r
+ // destructor - disconnect all iterators from the node\r
+ // this usually happens when the node is deleted and must invalidate all aliases\r
+ template<typename O, typename N>\r
+ master_iterator<O,N>::~master_iterator(void) throw()\r
+ {\r
+ m_body->set_end();\r
+ if(m_body->decrement())\r
+ {\r
+ delete m_body;\r
+ m_body = 0;\r
+ }\r
+ }\r
+\r
+ // dereference\r
+ template<typename O, typename N>\r
+ N* master_iterator<O,N>::node(void) const throw()\r
+ {\r
+ return m_body->node();\r
+ }\r
+\r
+ template<typename O, typename N>\r
+ const O* master_iterator<O,N>::owner(void) const throw()\r
+ {\r
+ return m_body->owner();\r
+ }\r
+\r
+ // when you move a node from one owner to another, call this on the node's iterator\r
+ // this effectively moves all iterators to the node so that they are owned by the new owner too\r
+ template<typename O, typename N>\r
+ void master_iterator<O,N>::change_owner(const O* owner) throw()\r
+ {\r
+ m_body->change_owner(owner);\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // Safe Iterator\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+\r
+ // construct a null iterator\r
+ // later assignment of a valid iterator to this is done by using step\r
+ template<typename O, typename N>\r
+ safe_iterator<O,N>::safe_iterator(void) throw() : \r
+ m_body(new safe_iterator_body<O,N>(0,0))\r
+ {\r
+ }\r
+\r
+ // construct a valid iterator by aliasing from the owner node's master iterator\r
+ template<typename O, typename N>\r
+ safe_iterator<O,N>::safe_iterator(const master_iterator<O,N>& r) throw() :\r
+ m_body(0)\r
+ {\r
+ m_body = r.m_body;\r
+ m_body->increment();\r
+ }\r
+\r
+ // construct a valid iterator by aliasing from the owner node's master iterator\r
+ template<typename O, typename N>\r
+ safe_iterator<O,N>::safe_iterator(const safe_iterator<O,N>& r) throw() :\r
+ m_body(0)\r
+ {\r
+ m_body = r.m_body;\r
+ m_body->increment();\r
+ }\r
+\r
+ // assignment implements dealiasing followed by aliasing\r
+ template<typename O, typename N>\r
+ safe_iterator<O,N>& safe_iterator<O,N>::operator=(const safe_iterator<O,N>& r) throw()\r
+ {\r
+ if (m_body != r.m_body)\r
+ {\r
+ if (m_body->decrement())\r
+ delete m_body;\r
+ m_body = r.m_body;\r
+ m_body->increment();\r
+ }\r
+ return *this;\r
+ }\r
+\r
+ // destructor - implements dealiasing\r
+ template<typename O, typename N>\r
+ safe_iterator<O,N>::~safe_iterator(void) throw()\r
+ {\r
+ if(m_body->decrement())\r
+ {\r
+ delete m_body;\r
+ m_body = 0;\r
+ }\r
+ }\r
+\r
+\r
+ // increment/decrement operation\r
+ // implements dealiasing followed by aliasing\r
+ template<typename O, typename N>\r
+ void safe_iterator<O,N>::set(const master_iterator<O,N>& r) throw()\r
+ {\r
+ if (m_body != r.m_body)\r
+ {\r
+ if (m_body->decrement())\r
+ delete m_body;\r
+ m_body = r.m_body;\r
+ m_body->increment();\r
+ }\r
+ }\r
+\r
+ // dereference\r
+ template<typename O, typename N>\r
+ N* safe_iterator<O,N>::node(void) const throw()\r
+ {\r
+ return m_body->node();\r
+ }\r
+\r
+ template<typename O, typename N>\r
+ const O* safe_iterator<O,N>::owner(void) const throw()\r
+ {\r
+ return m_body->owner();\r
+ }\r
+\r
+ // change to a null iterator - i.e. one that doees not belong to any object\r
+ // this does not affect any other iterators pointing to the same node\r
+ template<typename O, typename N>\r
+ void safe_iterator<O,N>::set_null(void) throw()\r
+ {\r
+ if (m_body->count() == 1)\r
+ {\r
+ // no aliases, so just make this null\r
+ m_body->set_null();\r
+ }\r
+ else\r
+ {\r
+ // create a new body which is null so as not to affect any other aliases\r
+ m_body->decrement();\r
+ m_body = new safe_iterator_body<O,N>(0,0);\r
+ }\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // operations for clients that do not have a master end iterator\r
+ // alternatively, have a master end iterator as part of the container\r
+ // and call constructor(master_end) or step(master_end)\r
+\r
+ // construct an end iterator\r
+ template<typename O, typename N>\r
+ safe_iterator<O,N>::safe_iterator(const O* owner) throw() :\r
+ m_body(new safe_iterator_body<O,N>(owner,0))\r
+ {\r
+ }\r
+\r
+ // change to an end iterator - e.g. as a result of incrementing off the end\r
+ template<typename O, typename N>\r
+ void safe_iterator<O,N>::set_end(void) throw()\r
+ {\r
+ if (m_body->count() == 1)\r
+ {\r
+ // no aliases, so just make this an end iterator\r
+ m_body->set_end();\r
+ }\r
+ else\r
+ {\r
+ // create a new body which is null so as not to affect any other aliases\r
+ m_body->decrement();\r
+ m_body = new safe_iterator_body<O,N>(owner(),0);\r
+ }\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // tests\r
+\r
+ // comparison\r
+ template<typename O, typename N>\r
+ bool safe_iterator<O,N>::equal(const safe_iterator<O,N>& right) const throw()\r
+ {\r
+ if (m_body == right.m_body) return true;\r
+ return m_body->equal(right.m_body);\r
+ }\r
+\r
+ template<typename O, typename N>\r
+ int safe_iterator<O,N>::compare(const safe_iterator<O,N>& right) const throw()\r
+ {\r
+ return m_body->compare(right.m_body);\r
+ }\r
+\r
+ // a null iterator is one that has not been initialised with a value yet\r
+ template<typename O, typename N>\r
+ bool safe_iterator<O,N>::null(void) const throw()\r
+ {\r
+ return m_body->null();\r
+ }\r
+\r
+ // an end iterator is one that points to the end element of the list of nodes\r
+ template<typename O, typename N>\r
+ bool safe_iterator<O,N>::end(void) const throw()\r
+ {\r
+ return m_body->end();\r
+ }\r
+\r
+ // a valid iterator is one that can be dereferenced\r
+ template<typename O, typename N>\r
+ bool safe_iterator<O,N>::valid(void) const throw()\r
+ {\r
+ return m_body->valid();\r
+ }\r
+\r
+ // check the rules for a valid iterator that can be dereferenced\r
+ template<typename O, typename N>\r
+ void safe_iterator<O,N>::assert_valid(void) const throw(null_dereference,end_dereference)\r
+ {\r
+ m_body->assert_valid();\r
+ }\r
+\r
+ template<typename O, typename N>\r
+ void safe_iterator<O,N>::assert_valid(const O* owner) const throw(wrong_object,null_dereference,end_dereference)\r
+ {\r
+ m_body->assert_valid();\r
+ m_body->assert_owner(owner);\r
+ }\r
+\r
+ template<typename O, typename N>\r
+ void safe_iterator<O,N>::assert_non_null(void) const throw(null_dereference)\r
+ {\r
+ m_body->assert_non_null();\r
+ }\r
+\r
+ template<typename O, typename N>\r
+ void safe_iterator<O,N>::assert_owner(const O* owner) const throw(wrong_object)\r
+ {\r
+ m_body->assert_owner(owner);\r
+ }\r
+\r
+} // end namespace stlplus\r
-#ifndef STLPLUS_SIMPLE_PTR
-#define STLPLUS_SIMPLE_PTR
-////////////////////////////////////////////////////////////////////////////////
-
-// Author: Daniel Milton, Andy Rushton
-// Copyright: (c) Southampton University 1999-2004
-// (c) Daniel Milton, Andy Rushton 2004-2009
-// License: BSD License, see ../docs/license.html
-
-// A smart pointer is a memory-managing pointer to an object. If you like, it
-// is a zero-dimensional container.
-
-// Assignment of smart pointers result in multiple aliases of the same object.
-// The term alias is used to differentiate from conventional pointers because
-// the semantics are different.
-
-// Aliases can be turned into copies if the pointed-to class supports copying.
-
-// These simple_ptr classes from DJDM have slightly different semantics than
-// the smart_ptr classes of AJR. There are no cross-pointer side effects
-// that occur when the pointer is cleared. The clear() function is effectively
-// equivalent to the clear_unique() function of the smart_ptr. The only way
-// that a "referenced" object will be deleted is if all simple_ptr's that
-// reference the object are cleared (by deletion, manual clearing or reassignment).
-
-// Also, the simple pointer cannot contain a reference to a shared null pointer
-// (which occurs as a side-effect of clearing a multiply referenced object in
-// the smart_ptr classes). Which means that if you have a null simple_ptr, then
-// the assignment of any other null simple_ptr will NOT reassign the reference of
-// any other simple_ptr. Hence, the simple_ptr class acts a little more like a
-// normal pointer (with fewer side effects), with the added bonus of containment.
-
-// Due to the way that the simple_ptr contains the data, it also allows the
-// addition of various casting functions, while still keeping the managed data
-// containment functionality of the underlying object. This means that you can
-// have two simple_ptr's of different template types, both pointing to the same
-// data (if the differing template types are derivatives of each other).
-
-// The base class is simple_ptr_base which defines the common interface. Then
-// there are three subclasses which have the same interface but different copy
-// semantics:
-
-// - simple_ptr for simple types and classes which have copy constructors
-// - simple_ptr_clone for polymorphic class hierarchies which are copied using a clone method
-// - simple_ptr_nocopy for any class that cannot or should not be copied
-
-////////////////////////////////////////////////////////////////////////////////
-#include "containers_fixes.hpp"
-#include "exceptions.hpp"
-#include "copy_functors.hpp"
-#include <map>
-#include <string>
-
-namespace stlplus
-{
-
- ////////////////////////////////////////////////////////////////////////////////
- // Base class
- ////////////////////////////////////////////////////////////////////////////////
-
- template<typename T, typename C>
- class simple_ptr_base
- {
- public:
- //////////////////////////////////////////////////////////////////////////////
- // member type definitions
-
- typedef T value_type;
- typedef T& reference;
- typedef const T& const_reference;
- typedef C value_copy;
-
- //////////////////////////////////////////////////////////////////////////////
- // constructors and destructors
-
- // create a null pointer
- simple_ptr_base(void);
-
- // create a pointer containing a *copy* of the object using the template parameter C
- // this copy is taken because the pointer class maintains a dynamically allocated object
- // and the T& may not be (usually is not) dynamically allocated
- explicit simple_ptr_base(const T& data) throw(illegal_copy);
-
- // create a pointer containing a dynamically created object
- // Note: the object must be allocated *by the user* with new
- // constructor form - must be called in the form smart_ptr_base<type> x(new type(args))
- explicit simple_ptr_base(T* data);
-
- // copy constructor implements aliasing so no copy is made
- // note that the copy constructor should NOT be explicit, as this breaks
- // the returning of pointer objects from functions (at least within GCC 4.4)
- simple_ptr_base(const simple_ptr_base<T,C>& r);
-
- // assignment operator - required, else the output of GCC suffers segmentation faults
- simple_ptr_base<T,C>& operator=(const simple_ptr_base<T,C>& r);
-
- // destructor decrements the reference count and delete only when the last reference is destroyed
- ~simple_ptr_base(void);
-
- //////////////////////////////////////////////////////////////////////////////
- // logical tests to see if there is anything contained in the pointer since it can be null
-
- // there are two forms:explicit and implicit
- // implicit: if(!r) or if(r)
- // explicit: if(r.null()) or if(r.present())
- operator bool(void) const;
- bool operator!(void) const;
- bool present(void) const;
- bool null(void) const;
-
- //////////////////////////////////////////////////////////////////////////////
- // dereference operators and functions
-
- // dereference the smart pointer to get the object - use in the form *p1
- T& operator*(void) throw(null_dereference);
- const T& operator*(void) const throw(null_dereference);
-
- // used as a prefix to a member access to the contained object e.g. p1->print() calls T::print()
- T* operator->(void) throw(null_dereference);
- const T* operator->(void) const throw(null_dereference);
-
- //////////////////////////////////////////////////////////////////////////////
- // explicit function forms of the above assignment and dereference operators
-
- // set the value - note that this does a copy using the C template parameter
- void set_value(const T& data) throw(illegal_copy);
- // get the value
- T& value(void) throw(null_dereference);
- const T& value(void) const throw(null_dereference);
-
- // set the pointer
- // deletes the previous pointer and adopts the passed pointer instead
- // Note: the object must be allocated *by the user* with new
- // Warning: it is very easy to break the memory management with this operation
- void set(T* data = 0);
- // get the pointer
- T* pointer(void);
- const T* pointer(void) const;
-
- //////////////////////////////////////////////////////////////////////////////
- // functions to manage aliases
-
- // make this an alias of the passed object
- void alias(const simple_ptr_base<T,C>&);
-
- // test whether two pointers point to the same object(known as aliasing the object)
- // used in the form if(a.aliases(b))
- bool aliases(const simple_ptr_base<T,C>&) const;
-
- // find the number of aliases - used when you need to know whether an
- // object is still referred to from elsewhere (rare!)
- unsigned alias_count(void) const;
-
- // clear the reference to the object, but only delete the object if there are no
- // other references to that object. Hence, this does not affect other pointers
- // that are pointing to the same object.
- void clear(void);
-
- // This is just an alias of the clear() function, provided for completeness of
- // the interface when acting as a replacement for the smart_ptr classes
- void clear_unique(void);
-
- //////////////////////////////////////////////////////////////////////////////
- // functions that involve copying
-
- // these functions use the copy functor passed as the template parameter C
- // to copy the object with the right copy semantics. If the copy functor
- // is no_copy, an exception will be thrown.
-
- // make this pointer unique with respect to any other references to the same object
- // if this pointer is already unique, it does nothing - otherwise it copies the object
- void make_unique(void) throw(illegal_copy);
-
- // make this pointer a unique copy of the parameter
- // useful for expressions like p1.copy(p2) which makes p1 a pointer to a unique copy of the contents of p2
- void copy(const simple_ptr_base<T,C>&) throw(illegal_copy);
-
- //////////////////////////////////////////////////////////////////////////////
- // functions that involve casting
-
-#ifdef STLPLUS_MEMBER_TEMPLATES
-
- // dynamic cast of underlying pointer to a derived/parent
- template<typename T2> simple_ptr_base<T2,C> dyn_cast(void) const;
-
- // static cast of underlying pointer to a derived/parent
- template<typename T2> simple_ptr_base<T2,C> stat_cast(void) const;
-
- // cast of underlying pointer to a base - while keeping the same ref-counted object
- template<typename T2> simple_ptr_base<T2,C> cast(void) const;
-
-#endif
-
- //////////////////////////////////////////////////////////////////////////////
-
- protected:
- T* m_pointer;
- unsigned* m_count;
-
- public:
- // internal use only - had to make them public because they need to be
- // accessed by routines that could not be made friends
- // can't have a handle due to the way the simple pointer stores it's data
- // in separate counter and pointer objects
- unsigned* _count(void) const;
- T* _pointer(void) const;
- void _make_alias(T* pointer, unsigned* count);
-
- private:
- void increment(void);
- bool decrement(void);
- };
-
- ////////////////////////////////////////////////////////////////////////////////
- // simple_ptr for simple types and classes which have copy constructors
-
- template <typename T>
- class simple_ptr : public simple_ptr_base<T, constructor_copy<T> >
- {
- public:
- simple_ptr(void) {}
- explicit simple_ptr(const T& data) : simple_ptr_base<T, constructor_copy<T> >(data) {}
- explicit simple_ptr(T* data) : simple_ptr_base<T, constructor_copy<T> >(data) {}
- simple_ptr<T>& operator=(const T& data) {set_value(data); return *this;}
- simple_ptr<T>& operator=(T* data) {set(data); return *this;}
- ~simple_ptr(void) {}
- };
-
- ////////////////////////////////////////////////////////////////////////////////
- // smart_ptr_clone for polymorphic class hierarchies which have a clone method
-
- template <typename T>
- class simple_ptr_clone : public simple_ptr_base<T, clone_copy<T> >
- {
- public:
- simple_ptr_clone(void) {}
- explicit simple_ptr_clone(const T& data) : simple_ptr_base<T, clone_copy<T> >(data) {}
- explicit simple_ptr_clone(T* data) : simple_ptr_base<T, clone_copy<T> >(data) {}
- simple_ptr_clone<T>& operator=(const T& data) {set_value(data); return *this;}
- simple_ptr_clone<T>& operator=(T* data) {set(data); return *this;}
- ~simple_ptr_clone(void) {}
- };
-
- ////////////////////////////////////////////////////////////////////////////////
- // smart_ptr_nocopy for any class that cannot or should not be copied
-
- template <typename T>
- class simple_ptr_nocopy : public simple_ptr_base<T, no_copy<T> >
- {
- public:
- simple_ptr_nocopy(void) {}
- explicit simple_ptr_nocopy(const T& data) : simple_ptr_base<T, no_copy<T> >(data) {}
- explicit simple_ptr_nocopy(T* data) : simple_ptr_base<T, no_copy<T> >(data) {}
- simple_ptr_nocopy<T>& operator=(const T& data) {set_value(data); return *this;}
- simple_ptr_nocopy<T>& operator=(T* data) {set(data); return *this;}
- ~simple_ptr_nocopy(void) {}
- };
-
- ////////////////////////////////////////////////////////////////////////////////
-
-} // end namespace stlplus
-
-#include "simple_ptr.tpp"
-#endif
+#ifndef STLPLUS_SIMPLE_PTR\r
+#define STLPLUS_SIMPLE_PTR\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+// Author: Daniel Milton\r
+// Copyright: (c) Daniel Milton 2002 onwards\r
+// License: BSD License, see ../docs/license.html\r
+\r
+// A smart pointer is a memory-managing pointer to an object. If you like, it\r
+// is a zero-dimensional container.\r
+\r
+// Assignment of smart pointers result in multiple aliases of the same object.\r
+// The term alias is used to differentiate from conventional pointers because\r
+// the semantics are different.\r
+\r
+// Aliases can be turned into copies if the pointed-to class supports copying.\r
+\r
+// These simple_ptr classes from DJDM have slightly different semantics than\r
+// the smart_ptr classes of AJR. There are no cross-pointer side effects\r
+// that occur when the pointer is cleared. The clear() function is effectively\r
+// equivalent to the clear_unique() function of the smart_ptr. The only way\r
+// that a "referenced" object will be deleted is if all simple_ptr's that\r
+// reference the object are cleared (by deletion, manual clearing or reassignment).\r
+\r
+// Also, the simple pointer cannot contain a reference to a shared null pointer\r
+// (which occurs as a side-effect of clearing a multiply referenced object in\r
+// the smart_ptr classes). Which means that if you have a null simple_ptr, then\r
+// the assignment of any other null simple_ptr will NOT reassign the reference of\r
+// any other simple_ptr. Hence, the simple_ptr class acts a little more like a\r
+// normal pointer (with fewer side effects), with the added bonus of containment.\r
+\r
+// Due to the way that the simple_ptr contains the data, it also allows the\r
+// addition of various casting functions, while still keeping the managed data\r
+// containment functionality of the underlying object. This means that you can\r
+// have two simple_ptr's of different template types, both pointing to the same\r
+// data (if the differing template types are derivatives of each other).\r
+\r
+// The base class is simple_ptr_base which defines the common interface. Then\r
+// there are three subclasses which have the same interface but different copy\r
+// semantics:\r
+\r
+// - simple_ptr for simple types and classes which have copy constructors\r
+// - simple_ptr_clone for polymorphic class hierarchies which are copied using a clone method\r
+// - simple_ptr_nocopy for any class that cannot or should not be copied\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "containers_fixes.hpp"\r
+#include "exceptions.hpp"\r
+#include "copy_functors.hpp"\r
+#include <map>\r
+#include <string>\r
+\r
+namespace stlplus\r
+{\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // Base class\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+\r
+ template<typename T, typename C>\r
+ class simple_ptr_base\r
+ {\r
+ public:\r
+ //////////////////////////////////////////////////////////////////////////////\r
+ // member type definitions\r
+\r
+ typedef T value_type;\r
+ typedef T& reference;\r
+ typedef const T& const_reference;\r
+ typedef C value_copy;\r
+\r
+ //////////////////////////////////////////////////////////////////////////////\r
+ // constructors and destructors\r
+\r
+ // create a null pointer\r
+ simple_ptr_base(void);\r
+\r
+ // create a pointer containing a dynamically created object\r
+ // Note: the object must be allocated *by the user* with new\r
+ // constructor form - must be called in the form smart_ptr_base<type> x(new type(args))\r
+ explicit simple_ptr_base(T* data);\r
+\r
+ // copy constructor implements aliasing so no copy is made\r
+ // note that the copy constructor should NOT be explicit, as this breaks\r
+ // the returning of pointer objects from functions (at least within GCC 4.4)\r
+ simple_ptr_base(const simple_ptr_base<T,C>& r);\r
+\r
+ // assignment operator - required, else the output of GCC suffers segmentation faults\r
+ simple_ptr_base<T,C>& operator=(const simple_ptr_base<T,C>& r);\r
+\r
+ // destructor decrements the reference count and delete only when the last reference is destroyed\r
+ ~simple_ptr_base(void);\r
+\r
+ //////////////////////////////////////////////////////////////////////////////\r
+ // logical tests to see if there is anything contained in the pointer since it can be null\r
+\r
+ // there are two forms:explicit and implicit\r
+ // implicit: if(!r) or if(r)\r
+ // explicit: if(r.null()) or if(r.present())\r
+ operator bool(void) const;\r
+ bool operator!(void) const;\r
+ bool present(void) const;\r
+ bool null(void) const;\r
+\r
+ //////////////////////////////////////////////////////////////////////////////\r
+ // dereference operators and functions\r
+\r
+ // dereference the smart pointer to get the object - use in the form *p1\r
+ T& operator*(void) throw(null_dereference);\r
+ const T& operator*(void) const throw(null_dereference);\r
+\r
+ // used as a prefix to a member access to the contained object e.g. p1->print() calls T::print()\r
+ T* operator->(void) throw(null_dereference);\r
+ const T* operator->(void) const throw(null_dereference);\r
+\r
+ //////////////////////////////////////////////////////////////////////////////\r
+ // explicit function forms of the above assignment and dereference operators\r
+\r
+ // get the value\r
+ T& value(void) throw(null_dereference);\r
+ const T& value(void) const throw(null_dereference);\r
+\r
+ // set the pointer\r
+ // deletes the previous pointer and adopts the passed pointer instead\r
+ // Note: the object must be allocated *by the user* with new\r
+ // Warning: it is very easy to break the memory management with this operation\r
+ void set(T* data = 0);\r
+ // get the pointer\r
+ T* pointer(void);\r
+ const T* pointer(void) const;\r
+\r
+ //////////////////////////////////////////////////////////////////////////////\r
+ // functions to manage aliases\r
+\r
+ // make this an alias of the passed object\r
+ void alias(const simple_ptr_base<T,C>&);\r
+\r
+ // test whether two pointers point to the same object(known as aliasing the object)\r
+ // used in the form if(a.aliases(b))\r
+ bool aliases(const simple_ptr_base<T,C>&) const;\r
+\r
+ // find the number of aliases - used when you need to know whether an\r
+ // object is still referred to from elsewhere (rare!)\r
+ unsigned alias_count(void) const;\r
+\r
+ // clear the reference to the object, but only delete the object if there are no\r
+ // other references to that object. Hence, this does not affect other pointers\r
+ // that are pointing to the same object.\r
+ void clear(void);\r
+\r
+ // This is just an alias of the clear() function, provided for completeness of\r
+ // the interface when acting as a replacement for the smart_ptr classes\r
+ void clear_unique(void);\r
+\r
+ //////////////////////////////////////////////////////////////////////////////\r
+ // functions that involve copying\r
+\r
+ // these functions use the copy functor passed as the template parameter C\r
+ // to copy the object with the right copy semantics. If the copy functor\r
+ // is no_copy, an exception will be thrown.\r
+\r
+ // create a pointer containing a *copy* of the object using the template parameter C\r
+ // this copy is taken because the pointer class maintains a dynamically allocated object\r
+ // and the T& may not be (usually is not) dynamically allocated\r
+ explicit simple_ptr_base(const T& data) throw(illegal_copy);\r
+\r
+ // set the value - note that this does a copy using the C template parameter\r
+ void set_value(const T& data) throw(illegal_copy);\r
+\r
+ // make this pointer unique with respect to any other references to the same object\r
+ // if this pointer is already unique, it does nothing - otherwise it copies the object\r
+ void make_unique(void) throw(illegal_copy);\r
+\r
+ // make this pointer a unique copy of the parameter\r
+ // useful for expressions like p1.copy(p2) which makes p1 a pointer to a unique copy of the contents of p2\r
+ void copy(const simple_ptr_base<T,C>&) throw(illegal_copy);\r
+\r
+ //////////////////////////////////////////////////////////////////////////////\r
+\r
+ protected:\r
+ T* m_pointer;\r
+ unsigned* m_count;\r
+\r
+ public:\r
+ // internal use only - had to make them public because they need to be\r
+ // accessed by routines that could not be made friends\r
+ // can't have a handle due to the way the simple pointer stores it's data\r
+ // in separate counter and pointer objects\r
+ unsigned* _count(void) const;\r
+ T* _pointer(void) const;\r
+ void _make_alias(T* pointer, unsigned* count);\r
+\r
+ private:\r
+ void increment(void);\r
+ bool decrement(void);\r
+ };\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // simple_ptr for simple types and classes which have copy constructors\r
+\r
+ template <typename T>\r
+ class simple_ptr : public simple_ptr_base<T, constructor_copy<T> >\r
+ {\r
+ public:\r
+ simple_ptr(void) {}\r
+ explicit simple_ptr(const T& data) : simple_ptr_base<T, constructor_copy<T> >(data) {}\r
+ explicit simple_ptr(T* data) : simple_ptr_base<T, constructor_copy<T> >(data) {}\r
+ simple_ptr<T>& operator=(const T& data) {set_value(data); return *this;}\r
+ simple_ptr<T>& operator=(T* data) {set(data); return *this;}\r
+ ~simple_ptr(void) {}\r
+\r
+#ifdef STLPLUS_MEMBER_TEMPLATES\r
+ // functions that involve casting\r
+ // moved from base class for two main reasons, though the second is a feature of the first:\r
+\r
+ // 1. GCC cannot cast the previous base result of simple_ptr_base<T2, constructor_copy<T> >\r
+ // as a simple_ptr<T2> even though it used to look like a duck and quack like a duck.\r
+ // I think it was really complaining that the copy class was not guaranteed to be the same.\r
+\r
+ // 2. Within the cast routines, one pointer type tried accessing private data of the other\r
+ // pointer type and even though they are really the same type, was not allowed. Because\r
+ // of this, the "private" function _make_alias is utilised to get the same result.\r
+\r
+ // By having the cast functions in each derived class, you are guaranteed to use the same\r
+ // copy class - no question. GCC is ok with this.\r
+\r
+ template<typename T2> simple_ptr<T2> dyn_cast(void) const;\r
+ template<typename T2> simple_ptr<T2> stat_cast(void) const;\r
+ template<typename T2> simple_ptr<T2> cast(void) const;\r
+#endif\r
+ };\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // simple_ptr_clone for polymorphic class hierarchies which have a clone method\r
+\r
+ template <typename T>\r
+ class simple_ptr_clone : public simple_ptr_base<T, clone_copy<T> >\r
+ {\r
+ public:\r
+ simple_ptr_clone(void) {}\r
+ explicit simple_ptr_clone(const T& data) : simple_ptr_base<T, clone_copy<T> >(data) {}\r
+ explicit simple_ptr_clone(T* data) : simple_ptr_base<T, clone_copy<T> >(data) {}\r
+ simple_ptr_clone<T>& operator=(const T& data) {set_value(data); return *this;}\r
+ simple_ptr_clone<T>& operator=(T* data) {set(data); return *this;}\r
+ ~simple_ptr_clone(void) {}\r
+\r
+#ifdef STLPLUS_MEMBER_TEMPLATES\r
+ // functions that involve casting\r
+ // moved from base class - see simple_ptr above\r
+ template<typename T2> simple_ptr_clone<T2> dyn_cast(void) const;\r
+ template<typename T2> simple_ptr_clone<T2> stat_cast(void) const;\r
+ template<typename T2> simple_ptr_clone<T2> cast(void) const;\r
+#endif\r
+};\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // simple_ptr_nocopy for any class that cannot or should not be copied\r
+\r
+ template <typename T>\r
+ class simple_ptr_nocopy : public simple_ptr_base<T, no_copy<T> >\r
+ {\r
+ public:\r
+ simple_ptr_nocopy(void) {}\r
+ explicit simple_ptr_nocopy(T* data) : simple_ptr_base<T, no_copy<T> >(data) {}\r
+ simple_ptr_nocopy<T>& operator=(T* data) {set(data); return *this;}\r
+ ~simple_ptr_nocopy(void) {}\r
+\r
+#ifdef STLPLUS_MEMBER_TEMPLATES\r
+ // functions that involve casting\r
+ // moved from base class - see simple_ptr above\r
+ template<typename T2> simple_ptr_nocopy<T2> dyn_cast(void) const;\r
+ template<typename T2> simple_ptr_nocopy<T2> stat_cast(void) const;\r
+ template<typename T2> simple_ptr_nocopy<T2> cast(void) const;\r
+#endif\r
+ };\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
+\r
+#include "simple_ptr.tpp"\r
+#endif\r
-////////////////////////////////////////////////////////////////////////////////
-
-// Author: Daniel Milton
-// Copyright: (c) Daniel Milton 2002-2009
-
-////////////////////////////////////////////////////////////////////////////////
-
-namespace stlplus
-{
-
- ////////////////////////////////////////////////////////////////////////////////
- // simple_ptr_base class
- ////////////////////////////////////////////////////////////////////////////////
-
- ////////////////////////////////////////////////////////////////////////////////
- // constructors, assignments and destructors
-
- // create a null pointer
- template <typename T, typename C>
- simple_ptr_base<T,C>::simple_ptr_base(void) :
- m_pointer(0),
- m_count(new unsigned(1))
- {
- }
-
- // create a pointer containing a *copy* of the object pointer
- template <typename T, typename C>
- simple_ptr_base<T,C>::simple_ptr_base(const T& data) throw(illegal_copy) :
- m_pointer(C()(data)),
- m_count(new unsigned(1))
- {
- }
-
- // create a pointer containing a dynamically created object
- // Note: the object must be allocated *by the user* with new
- // constructor form - must be called in the form simple_ptr<type> x(new type(args))
- template <typename T, typename C>
- simple_ptr_base<T,C>::simple_ptr_base(T* data) :
- m_pointer(data),
- m_count(new unsigned(1))
- {
- }
-
- // copy constructor implements counted referencing - no copy is made
- template <typename T, typename C>
- simple_ptr_base<T,C>::simple_ptr_base(const simple_ptr_base<T,C>& r) :
- m_pointer(r.m_pointer),
- m_count(r.m_count)
- {
- increment();
- }
-
- // assignment operator - required, else the output of GCC suffers segmentation faults
- template <typename T, typename C>
- simple_ptr_base<T,C>& simple_ptr_base<T,C>::operator=(const simple_ptr_base<T,C>& r)
- {
- alias(r);
- return *this;
- }
-
- // destructor decrements the reference count and delete only when the last reference is destroyed
- template <typename T, typename C>
- simple_ptr_base<T,C>::~simple_ptr_base(void)
- {
- if(decrement())
- {
- delete m_pointer;
- delete m_count;
- }
- }
-
- //////////////////////////////////////////////////////////////////////////////
- // logical tests to see if there is anything contained in the pointer since it can be null
-
- template <typename T, typename C>
- bool simple_ptr_base<T,C>::null(void) const
- {
- return m_pointer==0;
- }
-
- template <typename T, typename C>
- bool simple_ptr_base<T,C>::present(void) const
- {
- return m_pointer!=0;
- }
-
- template <typename T, typename C>
- bool simple_ptr_base<T,C>::operator!(void) const
- {
- return m_pointer==0;
- }
-
- template <typename T, typename C>
- simple_ptr_base<T,C>::operator bool(void) const
- {
- return m_pointer!=0;
- }
-
- //////////////////////////////////////////////////////////////////////////////
- // dereference operators and functions
-
- template <typename T, typename C>
- T& simple_ptr_base<T,C>::operator*(void) throw(null_dereference)
- {
- if (!m_pointer) throw null_dereference("null pointer dereferenced in simple_ptr::operator*");
- return *m_pointer;
- }
-
- template <typename T, typename C>
- const T& simple_ptr_base<T,C>::operator*(void) const throw(null_dereference)
- {
- if (!m_pointer) throw null_dereference("null pointer dereferenced in simple_ptr::operator*");
- return *m_pointer;
- }
-
- template <typename T, typename C>
- T* simple_ptr_base<T,C>::operator->(void) throw(null_dereference)
- {
- if (!m_pointer) throw null_dereference("null pointer dereferenced in simple_ptr::operator->");
- return m_pointer;
- }
-
- template <typename T, typename C>
- const T* simple_ptr_base<T,C>::operator->(void) const throw(null_dereference)
- {
- if (!m_pointer) throw null_dereference("null pointer dereferenced in simple_ptr::operator->");
- return m_pointer;
- }
-
- //////////////////////////////////////////////////////////////////////////////
- // explicit function forms of the above assignment dereference operators
-
- template <typename T, typename C>
- void simple_ptr_base<T,C>::set_value(const T& data) throw(illegal_copy)
- {
- set(C()(data));
- }
-
- template <typename T, typename C>
- T& simple_ptr_base<T,C>::value(void) throw(null_dereference)
- {
- if (!m_pointer) throw null_dereference("null pointer dereferenced in simple_ptr::value");
- return *m_pointer;
- }
-
- template <typename T, typename C>
- const T& simple_ptr_base<T,C>::value(void) const throw(null_dereference)
- {
- if (!m_pointer) throw null_dereference("null pointer dereferenced in simple_ptr::value");
- return *m_pointer;
- }
-
- template <typename T, typename C>
- void simple_ptr_base<T,C>::set(T* data)
- {
- unsigned& count = *m_count;
- if (count<=1)
- delete m_pointer;
- else
- {
- --count;
- m_count = new unsigned(1);
- }
- m_pointer = data;
- }
-
- template <typename T, typename C>
- T* simple_ptr_base<T,C>::pointer(void)
- {
- return m_pointer;
- }
-
- template <typename T, typename C>
- const T* simple_ptr_base<T,C>::pointer(void) const
- {
- return m_pointer;
- }
-
- ////////////////////////////////////////////////////////////////////////////////
- // functions to manage counted referencing
-
- template <typename T, typename C>
- void simple_ptr_base<T,C>::increment(void)
- {
- ++(*m_count);
- }
-
- template <typename T, typename C>
- bool simple_ptr_base<T,C>::decrement(void)
- {
- unsigned& count = *m_count;
- --count;
- return count == 0;
- }
-
- // make this an alias of the passed object
- template <typename T, typename C>
- void simple_ptr_base<T,C>::alias(const simple_ptr_base<T,C>& r)
- {
- // make it alias-copy safe - this means that I don't try to do the
- // assignment if r is either the same object or an alias of it
- if (m_pointer==r.m_pointer) return;
- if(decrement()) {
- delete m_pointer;
- delete m_count;
- }
- m_pointer = r.m_pointer;
- m_count = r.m_count;
- increment();
- }
-
- template <typename T, typename C>
- bool simple_ptr_base<T,C>::aliases(const simple_ptr_base<T,C>& r) const
- {
- return m_count == r.m_count;
- }
-
- template <typename T, typename C>
- unsigned simple_ptr_base<T,C>::alias_count(void) const
- {
- return *m_count;
- }
-
- template <typename T, typename C>
- void simple_ptr_base<T,C>::clear(void)
- {
- set(0);
- }
-
- template <typename T, typename C>
- void simple_ptr_base<T,C>::clear_unique(void)
- {
- set(0); // no difference between clear and clear_unique with the simple_ptr
- }
-
- template <typename T, typename C>
- void simple_ptr_base<T,C>::make_unique(void) throw(illegal_copy)
- {
- unsigned& count = *m_count;
- if (count <= 1) return;
- --count;
- if (m_pointer) m_pointer = C()(*m_pointer);
- m_count = new unsigned(1);
- }
-
- template <typename T, typename C>
- void simple_ptr_base<T,C>::copy(const simple_ptr_base<T,C>& data) throw(illegal_copy)
- {
- alias(data);
- make_unique();
- }
-
-#ifdef STLPLUS_MEMBER_TEMPLATES
-
- // dynamic cast of underlying pointer to a derived/parent
- template <typename T, typename C>
- template <typename T2>
- simple_ptr_base<T2,C> simple_ptr_base<T,C>::dyn_cast(void) const
- {
- simple_ptr_base<T2,C> rtn;
- rtn.m_pointer = dynamic_cast<T2*>(m_pointer);
- if (rtn.m_pointer) {
- delete rtn.m_count;
- rtn.m_count = m_count;
- rtn.increment();
- }
- return rtn;
- }
-
- // static cast of underlying pointer to a derived/parent
- template <typename T, typename C>
- template <typename T2>
- simple_ptr_base<T2,C> simple_ptr_base<T,C>::stat_cast(void) const
- {
- simple_ptr_base<T2,C> rtn;
- rtn.m_pointer = static_cast<T2*>(m_pointer);
- if (rtn.m_pointer) {
- delete rtn.m_count;
- rtn.m_count = m_count;
- rtn.increment();
- }
- return rtn;
- }
-
- // cast of underlying pointer to a base - while keeping the same ref-counted object
- template <typename T, typename C>
- template <typename T2>
- simple_ptr_base<T2,C> simple_ptr_base<T,C>::cast(void) const
- {
- simple_ptr_base<T2,C> rtn;
- rtn.m_pointer = (T2*)m_pointer;
- if (rtn.m_pointer) {
- delete rtn.m_count;
- rtn.m_count = m_count;
- rtn.increment();
- }
- return rtn;
- }
-
-#endif
-
- // internal function for distinguishing unique simple_ptr objects
- // used for example in persistence routines
-
- template <typename T, typename C>
- unsigned* simple_ptr_base<T,C>::_count(void) const
- {
- return m_count;
- }
-
- template <typename T, typename C>
- T* simple_ptr_base<T,C>::_pointer(void) const
- {
- return m_pointer;
- }
-
- template <typename T, typename C>
- void simple_ptr_base<T,C>::_make_alias(T* pointer, unsigned* count)
- {
- // make it alias-copy safe - this means that I don't try to do the
- // assignment if r is either the same object or an alias of it
- if (m_count != count)
- {
- if(decrement())
- {
- delete m_pointer;
- delete m_count;
- }
- m_pointer = pointer;
- m_count = count;
- increment();
- }
- }
-
- ////////////////////////////////////////////////////////////////////////////////
-
-} // end namespace stlplus
-
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+// Author: Daniel Milton\r
+// Copyright: (c) Daniel Milton 2002 onwards\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // simple_ptr_base class\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // constructors, assignments and destructors\r
+\r
+ // create a null pointer\r
+ template <typename T, typename C>\r
+ simple_ptr_base<T,C>::simple_ptr_base(void) :\r
+ m_pointer(0),\r
+ m_count(new unsigned(1))\r
+ {\r
+ }\r
+\r
+ // create a pointer containing a *copy* of the object pointer\r
+ template <typename T, typename C>\r
+ simple_ptr_base<T,C>::simple_ptr_base(const T& data) throw(illegal_copy) :\r
+ m_pointer(C()(data)),\r
+ m_count(new unsigned(1))\r
+ {\r
+ }\r
+\r
+ // create a pointer containing a dynamically created object\r
+ // Note: the object must be allocated *by the user* with new\r
+ // constructor form - must be called in the form simple_ptr<type> x(new type(args))\r
+ template <typename T, typename C>\r
+ simple_ptr_base<T,C>::simple_ptr_base(T* data) :\r
+ m_pointer(data),\r
+ m_count(new unsigned(1))\r
+ {\r
+ }\r
+\r
+ // copy constructor implements counted referencing - no copy is made\r
+ template <typename T, typename C>\r
+ simple_ptr_base<T,C>::simple_ptr_base(const simple_ptr_base<T,C>& r) :\r
+ m_pointer(r.m_pointer),\r
+ m_count(r.m_count)\r
+ {\r
+ increment();\r
+ }\r
+\r
+ // assignment operator - required, else the output of GCC suffers segmentation faults\r
+ template <typename T, typename C>\r
+ simple_ptr_base<T,C>& simple_ptr_base<T,C>::operator=(const simple_ptr_base<T,C>& r)\r
+ {\r
+ alias(r);\r
+ return *this;\r
+ }\r
+\r
+ // destructor decrements the reference count and delete only when the last reference is destroyed\r
+ template <typename T, typename C>\r
+ simple_ptr_base<T,C>::~simple_ptr_base(void)\r
+ {\r
+ if(decrement()) \r
+ {\r
+ delete m_pointer;\r
+ delete m_count;\r
+ }\r
+ }\r
+\r
+ //////////////////////////////////////////////////////////////////////////////\r
+ // logical tests to see if there is anything contained in the pointer since it can be null\r
+\r
+ template <typename T, typename C>\r
+ bool simple_ptr_base<T,C>::null(void) const\r
+ {\r
+ return m_pointer==0;\r
+ }\r
+\r
+ template <typename T, typename C>\r
+ bool simple_ptr_base<T,C>::present(void) const\r
+ {\r
+ return m_pointer!=0;\r
+ }\r
+\r
+ template <typename T, typename C>\r
+ bool simple_ptr_base<T,C>::operator!(void) const\r
+ {\r
+ return m_pointer==0;\r
+ }\r
+\r
+ template <typename T, typename C>\r
+ simple_ptr_base<T,C>::operator bool(void) const\r
+ {\r
+ return m_pointer!=0;\r
+ }\r
+\r
+ //////////////////////////////////////////////////////////////////////////////\r
+ // dereference operators and functions\r
+\r
+ template <typename T, typename C>\r
+ T& simple_ptr_base<T,C>::operator*(void) throw(null_dereference)\r
+ {\r
+ if (!m_pointer) throw null_dereference("null pointer dereferenced in simple_ptr::operator*");\r
+ return *m_pointer;\r
+ }\r
+\r
+ template <typename T, typename C>\r
+ const T& simple_ptr_base<T,C>::operator*(void) const throw(null_dereference)\r
+ {\r
+ if (!m_pointer) throw null_dereference("null pointer dereferenced in simple_ptr::operator*");\r
+ return *m_pointer;\r
+ }\r
+\r
+ template <typename T, typename C>\r
+ T* simple_ptr_base<T,C>::operator->(void) throw(null_dereference)\r
+ {\r
+ if (!m_pointer) throw null_dereference("null pointer dereferenced in simple_ptr::operator->");\r
+ return m_pointer;\r
+ }\r
+\r
+ template <typename T, typename C>\r
+ const T* simple_ptr_base<T,C>::operator->(void) const throw(null_dereference)\r
+ {\r
+ if (!m_pointer) throw null_dereference("null pointer dereferenced in simple_ptr::operator->");\r
+ return m_pointer;\r
+ }\r
+\r
+ //////////////////////////////////////////////////////////////////////////////\r
+ // explicit function forms of the above assignment dereference operators\r
+\r
+ template <typename T, typename C>\r
+ void simple_ptr_base<T,C>::set_value(const T& data) throw(illegal_copy)\r
+ {\r
+ set(C()(data));\r
+ }\r
+\r
+ template <typename T, typename C>\r
+ T& simple_ptr_base<T,C>::value(void) throw(null_dereference)\r
+ {\r
+ if (!m_pointer) throw null_dereference("null pointer dereferenced in simple_ptr::value");\r
+ return *m_pointer;\r
+ }\r
+\r
+ template <typename T, typename C>\r
+ const T& simple_ptr_base<T,C>::value(void) const throw(null_dereference)\r
+ {\r
+ if (!m_pointer) throw null_dereference("null pointer dereferenced in simple_ptr::value");\r
+ return *m_pointer;\r
+ }\r
+\r
+ template <typename T, typename C>\r
+ void simple_ptr_base<T,C>::set(T* data)\r
+ {\r
+ unsigned& count = *m_count;\r
+ if (count<=1)\r
+ delete m_pointer;\r
+ else\r
+ {\r
+ --count;\r
+ m_count = new unsigned(1);\r
+ }\r
+ m_pointer = data;\r
+ }\r
+\r
+ template <typename T, typename C>\r
+ T* simple_ptr_base<T,C>::pointer(void)\r
+ {\r
+ return m_pointer;\r
+ }\r
+\r
+ template <typename T, typename C>\r
+ const T* simple_ptr_base<T,C>::pointer(void) const\r
+ {\r
+ return m_pointer;\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // functions to manage counted referencing\r
+\r
+ template <typename T, typename C>\r
+ void simple_ptr_base<T,C>::increment(void)\r
+ {\r
+ ++(*m_count);\r
+ }\r
+\r
+ template <typename T, typename C>\r
+ bool simple_ptr_base<T,C>::decrement(void)\r
+ {\r
+ unsigned& count = *m_count;\r
+ --count;\r
+ return count == 0;\r
+ }\r
+\r
+ // make this an alias of the passed object\r
+ template <typename T, typename C>\r
+ void simple_ptr_base<T,C>::alias(const simple_ptr_base<T,C>& r)\r
+ {\r
+ // make it alias-copy safe - this means that I don't try to do the\r
+ // assignment if r is either the same object or an alias of it\r
+ if (m_pointer==r.m_pointer) return;\r
+ if(decrement()) {\r
+ delete m_pointer;\r
+ delete m_count;\r
+ }\r
+ m_pointer = r.m_pointer;\r
+ m_count = r.m_count;\r
+ increment();\r
+ }\r
+\r
+ template <typename T, typename C>\r
+ bool simple_ptr_base<T,C>::aliases(const simple_ptr_base<T,C>& r) const\r
+ {\r
+ return m_count == r.m_count;\r
+ }\r
+\r
+ template <typename T, typename C>\r
+ unsigned simple_ptr_base<T,C>::alias_count(void) const\r
+ {\r
+ return *m_count;\r
+ }\r
+\r
+ template <typename T, typename C>\r
+ void simple_ptr_base<T,C>::clear(void)\r
+ {\r
+ set(0);\r
+ }\r
+\r
+ template <typename T, typename C>\r
+ void simple_ptr_base<T,C>::clear_unique(void)\r
+ {\r
+ set(0); // no difference between clear and clear_unique with the simple_ptr\r
+ }\r
+\r
+ template <typename T, typename C>\r
+ void simple_ptr_base<T,C>::make_unique(void) throw(illegal_copy)\r
+ {\r
+ unsigned& count = *m_count;\r
+ if (count <= 1) return;\r
+ --count;\r
+ if (m_pointer) m_pointer = C()(*m_pointer);\r
+ m_count = new unsigned(1);\r
+ }\r
+\r
+ template <typename T, typename C>\r
+ void simple_ptr_base<T,C>::copy(const simple_ptr_base<T,C>& data) throw(illegal_copy)\r
+ {\r
+ alias(data);\r
+ make_unique();\r
+ }\r
+\r
+ // internal function for distinguishing unique simple_ptr objects\r
+ // used for example in persistence routines\r
+\r
+ template <typename T, typename C>\r
+ unsigned* simple_ptr_base<T,C>::_count(void) const\r
+ {\r
+ return m_count;\r
+ }\r
+\r
+ template <typename T, typename C>\r
+ T* simple_ptr_base<T,C>::_pointer(void) const\r
+ {\r
+ return m_pointer;\r
+ }\r
+\r
+ template <typename T, typename C>\r
+ void simple_ptr_base<T,C>::_make_alias(T* pointer, unsigned* count)\r
+ {\r
+ // make it alias-copy safe - this means that I don't try to do the\r
+ // assignment if r is either the same object or an alias of it\r
+ if (m_count != count)\r
+ {\r
+ if(decrement())\r
+ {\r
+ delete m_pointer;\r
+ delete m_count;\r
+ }\r
+ m_pointer = pointer;\r
+ m_count = count;\r
+ increment();\r
+ }\r
+ }\r
+\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // simple_ptr class\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+\r
+#ifdef STLPLUS_MEMBER_TEMPLATES\r
+\r
+ template <typename T>\r
+ template <typename T2>\r
+ simple_ptr<T2> simple_ptr<T>::dyn_cast(void) const {\r
+ simple_ptr<T2> rtn;\r
+ T2* p = dynamic_cast<T2*>(this->m_pointer);\r
+ if (p) rtn._make_alias(p, this->m_count);\r
+ return rtn;\r
+ }\r
+\r
+ template <typename T>\r
+ template <typename T2>\r
+ simple_ptr<T2> simple_ptr<T>::stat_cast(void) const {\r
+ simple_ptr<T2> rtn;\r
+ T2* p = static_cast<T2*>(this->m_pointer);\r
+ if (p) rtn._make_alias(p, this->m_count);\r
+ return rtn;\r
+ }\r
+\r
+ template <typename T>\r
+ template <typename T2>\r
+ simple_ptr<T2> simple_ptr<T>::cast(void) const {\r
+ simple_ptr<T2> rtn;\r
+ T2* p = (T2*)this->m_pointer;\r
+ if (p) rtn._make_alias(p, this->m_count);\r
+ return rtn;\r
+ }\r
+\r
+#endif\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // simple_ptr_clone class\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+\r
+#ifdef STLPLUS_MEMBER_TEMPLATES\r
+\r
+ template <typename T>\r
+ template <typename T2>\r
+ simple_ptr_clone<T2> simple_ptr_clone<T>::dyn_cast(void) const {\r
+ simple_ptr_clone<T2> rtn;\r
+ T2* p = dynamic_cast<T2*>(this->m_pointer);\r
+ if (p) rtn._make_alias(p, this->m_count);\r
+ return rtn;\r
+ }\r
+\r
+ template <typename T>\r
+ template <typename T2>\r
+ simple_ptr_clone<T2> simple_ptr_clone<T>::stat_cast(void) const {\r
+ simple_ptr_clone<T2> rtn;\r
+ T2* p = static_cast<T2*>(this->m_pointer);\r
+ if (p) rtn._make_alias(p, this->m_count);\r
+ return rtn;\r
+ }\r
+\r
+ template <typename T>\r
+ template <typename T2>\r
+ simple_ptr_clone<T2> simple_ptr_clone<T>::cast(void) const {\r
+ simple_ptr_clone<T2> rtn;\r
+ T2* p = (T2*)this->m_pointer;\r
+ if (p) rtn._make_alias(p, this->m_count);\r
+ return rtn;\r
+ }\r
+\r
+#endif\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // simple_ptr_nocopy class\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+\r
+#ifdef STLPLUS_MEMBER_TEMPLATES\r
+\r
+ template <typename T>\r
+ template <typename T2>\r
+ simple_ptr_nocopy<T2> simple_ptr_nocopy<T>::dyn_cast(void) const {\r
+ simple_ptr_nocopy<T2> rtn;\r
+ T2* p = dynamic_cast<T2*>(this->m_pointer);\r
+ if (p) rtn._make_alias(p, this->m_count);\r
+ return rtn;\r
+ }\r
+\r
+ template <typename T>\r
+ template <typename T2>\r
+ simple_ptr_nocopy<T2> simple_ptr_nocopy<T>::stat_cast(void) const {\r
+ simple_ptr_nocopy<T2> rtn;\r
+ T2* p = static_cast<T2*>(this->m_pointer);\r
+ if (p) rtn._make_alias(p, this->m_count);\r
+ return rtn;\r
+ }\r
+\r
+ template <typename T>\r
+ template <typename T2>\r
+ simple_ptr_nocopy<T2> simple_ptr_nocopy<T>::cast(void) const {\r
+ simple_ptr_nocopy<T2> rtn;\r
+ T2* p = (T2*)this->m_pointer;\r
+ if (p) rtn._make_alias(p, this->m_count);\r
+ return rtn;\r
+ }\r
+\r
+#endif\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
+\r
-#ifndef STLPLUS_SMART_PTR
-#define STLPLUS_SMART_PTR
-////////////////////////////////////////////////////////////////////////////////
-
-// Author: Andy Rushton
-// Copyright: (c) Southampton University 1999-2004
-// (c) Andy Rushton 2004-2009
-// License: BSD License, see ../docs/license.html
-
-// A smart pointer is a memory-managing pointer to an object. If you like, it
-// is a zero-dimensional container.
-
-// Assignment of smart pointers result in multiple aliases of the same object.
-// The term alias is used to differentiate from conventional pointers because
-// the semantics are different.
-
-// Aliases can be turned into copies if the pointed-to class supports copying.
-
-// The base class is smart_ptr_base which defines the common interface. Then
-// there are three subclasses which have the same interface but different copy
-// semantics:
-
-// - smart_ptr for simple types and classes which have copy constructors
-// - smart_ptr_clone for polymorphic class hierarchies which are copied using a clone method
-// - smart_ptr_nocopy for any class that cannot or should not be copied
-
-////////////////////////////////////////////////////////////////////////////////
-#include "containers_fixes.hpp"
-#include "exceptions.hpp"
-#include "copy_functors.hpp"
-#include <map>
-#include <string>
-
-namespace stlplus
-{
-
- ////////////////////////////////////////////////////////////////////////////////
- // internals
-
- template<typename T> class smart_ptr_holder;
-
- ////////////////////////////////////////////////////////////////////////////////
- // Base class
- ////////////////////////////////////////////////////////////////////////////////
-
- template<typename T, typename C>
- class smart_ptr_base
- {
- public:
- //////////////////////////////////////////////////////////////////////////////
- // member type definitions
-
- typedef T value_type;
- typedef T& reference;
- typedef const T& const_reference;
- typedef C value_copy;
-
- //////////////////////////////////////////////////////////////////////////////
- // constructors and destructors
-
- // create a null pointer
- smart_ptr_base(void);
-
- // create a pointer containing a *copy* of the object using the template parameter C
- // this copy is taken because the pointer class maintains a dynamically allocated object
- // and the T& may not be (usually is not) dynamically allocated
- explicit smart_ptr_base(const T& data) throw(illegal_copy);
-
- // create a pointer containing a dynamically created object
- // Note: the object must be allocated *by the user* with new
- // constructor form - must be called in the form smart_ptr_base<type> x(new type(args))
- explicit smart_ptr_base(T* data);
-
- // copy constructor implements aliasing so no copy is made
- // note that the copy constructor should NOT be explicit, as this breaks
- // the returning of pointer objects from functions (at least within GCC 4.4)
- smart_ptr_base(const smart_ptr_base<T,C>& r);
-
- // assignment operator - required, else the output of GCC suffers segmentation faults
- smart_ptr_base<T,C>& operator=(const smart_ptr_base<T,C>& r);
-
- // destructor decrements the reference count and delete only when the last reference is destroyed
- ~smart_ptr_base(void);
-
- //////////////////////////////////////////////////////////////////////////////
- // logical tests to see if there is anything contained in the pointer since it can be null
-
- // there are two forms:explicit and implicit
- // implicit: if(!r) or if(r)
- // explicit: if(r.null()) or if(r.present())
- operator bool(void) const;
- bool operator!(void) const;
- bool present(void) const;
- bool null(void) const;
-
- //////////////////////////////////////////////////////////////////////////////
- // dereference operators and functions
-
- // dereference the smart pointer to get the object - use in the form *p1
- T& operator*(void) throw(null_dereference);
- const T& operator*(void) const throw(null_dereference);
-
- // used as a prefix to a member access to the contained object e.g. p1->print() calls T::print()
- T* operator->(void) throw(null_dereference);
- const T* operator->(void) const throw(null_dereference);
-
- //////////////////////////////////////////////////////////////////////////////
- // explicit function forms of the above assignment and dereference operators
-
- // set the value - note that this does a copy using the C template parameter
- void set_value(const T& data) throw(illegal_copy);
- // get the value
- T& value(void) throw(null_dereference);
- const T& value(void) const throw(null_dereference);
-
- // set the pointer
- // deletes the previous pointer and adopts the passed pointer instead
- // Note: the object must be allocated *by the user* with new
- // Warning: it is very easy to break the memory management with this operation
- void set(T* data = 0);
- // get the pointer
- T* pointer(void);
- const T* pointer(void) const;
-
- //////////////////////////////////////////////////////////////////////////////
- // functions to manage aliases
-
- // make this an alias of the passed object
- void alias(const smart_ptr_base<T,C>&);
-
- // test whether two pointers point to the same object(known as aliasing the object)
- // used in the form if(a.aliases(b))
- bool aliases(const smart_ptr_base<T,C>&) const;
-
- // find the number of aliases - used when you need to know whether an
- // object is still referred to from elsewhere (rare!)
- unsigned alias_count(void) const;
-
- // delete the object and make the pointer null - does not make it unique
- // first, so all other pointers to this will be null too
- void clear(void);
-
- // make the pointer unique and null in one step - does not affect other
- // pointers that were pointing to the same object
- void clear_unique(void);
-
- //////////////////////////////////////////////////////////////////////////////
- // functions that involve copying
-
- // these functions use the copy functor passed as the template parameter C
- // to copy the object with the right copy semantics. If the copy functor
- // is no_copy, an exception will be thrown.
-
- // make this pointer unique with respect to any other references to the same object
- // if this pointer is already unique, it does nothing - otherwise it copies the object
- void make_unique(void) throw(illegal_copy);
-
- // make this pointer a unique copy of the parameter
- // useful for expressions like p1.copy(p2) which makes p1 a pointer to a unique copy of the contents of p2
- void copy(const smart_ptr_base<T,C>&) throw(illegal_copy);
-
- protected:
- smart_ptr_holder<T>* m_holder;
-
- public:
- // internal use only - had to make them public because they need to be
- // accessed by routines that could not be made friends
- smart_ptr_holder<T>* _handle(void) const;
- void _make_alias(smart_ptr_holder<T>* handle);
- };
-
- ////////////////////////////////////////////////////////////////////////////////
- // smart_ptr for simple types and classes which have copy constructors
-
- template <typename T>
- class smart_ptr : public smart_ptr_base<T, constructor_copy<T> >
- {
- public:
- smart_ptr(void) {}
- explicit smart_ptr(const T& data) : smart_ptr_base<T, constructor_copy<T> >(data) {}
- explicit smart_ptr(T* data) : smart_ptr_base<T, constructor_copy<T> >(data) {}
- smart_ptr<T>& operator=(const T& data) {set_value(data); return *this;}
- smart_ptr<T>& operator=(T* data) {set(data); return *this;}
- ~smart_ptr(void) {}
- };
-
- ////////////////////////////////////////////////////////////////////////////////
- // smart_ptr_clone for polymorphic class hierarchies which have a clone method
-
- template <typename T>
- class smart_ptr_clone : public smart_ptr_base<T, clone_copy<T> >
- {
- public:
- smart_ptr_clone(void) {}
- explicit smart_ptr_clone(const T& data) : smart_ptr_base<T, clone_copy<T> >(data) {}
- explicit smart_ptr_clone(T* data) : smart_ptr_base<T, clone_copy<T> >(data) {}
- smart_ptr_clone<T>& operator=(const T& data) {set_value(data); return *this;}
- smart_ptr_clone<T>& operator=(T* data) {set(data); return *this;}
- ~smart_ptr_clone(void) {}
- };
-
- ////////////////////////////////////////////////////////////////////////////////
- // smart_ptr_nocopy for any class that cannot or should not be copied
-
- template <typename T>
- class smart_ptr_nocopy : public smart_ptr_base<T, no_copy<T> >
- {
- public:
- smart_ptr_nocopy(void) {}
- explicit smart_ptr_nocopy(const T& data) : smart_ptr_base<T, no_copy<T> >(data) {}
- explicit smart_ptr_nocopy(T* data) : smart_ptr_base<T, no_copy<T> >(data) {}
- smart_ptr_nocopy<T>& operator=(const T& data) {set_value(data); return *this;}
- smart_ptr_nocopy<T>& operator=(T* data) {set(data); return *this;}
- ~smart_ptr_nocopy(void) {}
- };
-
- ////////////////////////////////////////////////////////////////////////////////
-
-} // end namespace stlplus
-
-#include "smart_ptr.tpp"
-#endif
+#ifndef STLPLUS_SMART_PTR\r
+#define STLPLUS_SMART_PTR\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+// Author: Andy Rushton\r
+// Copyright: (c) Southampton University 1999-2004\r
+// (c) Andy Rushton 2004 onwards\r
+// License: BSD License, see ../docs/license.html\r
+\r
+// A smart pointer is a memory-managing pointer to an object. If you like, it\r
+// is a zero-dimensional container.\r
+\r
+// Assignment of smart pointers result in multiple aliases of the same object.\r
+// The term alias is used to differentiate from conventional pointers because\r
+// the semantics are different.\r
+\r
+// Aliases can be turned into copies if the pointed-to class supports copying.\r
+\r
+// The base class is smart_ptr_base which defines the common interface. Then\r
+// there are three subclasses which have the same interface but different copy\r
+// semantics:\r
+\r
+// - smart_ptr for simple types and classes which have copy constructors\r
+// - smart_ptr_clone for polymorphic class hierarchies which are copied using a clone method\r
+// - smart_ptr_nocopy for any class that cannot or should not be copied\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "containers_fixes.hpp"\r
+#include "exceptions.hpp"\r
+#include "copy_functors.hpp"\r
+#include <map>\r
+#include <string>\r
+\r
+namespace stlplus\r
+{\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // internals\r
+\r
+ template<typename T> class smart_ptr_holder;\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // Base class\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+\r
+ template<typename T, typename C>\r
+ class smart_ptr_base\r
+ {\r
+ public:\r
+ //////////////////////////////////////////////////////////////////////////////\r
+ // member type definitions\r
+\r
+ typedef T value_type;\r
+ typedef T& reference;\r
+ typedef const T& const_reference;\r
+ typedef C value_copy;\r
+\r
+ //////////////////////////////////////////////////////////////////////////////\r
+ // constructors and destructors\r
+\r
+ // create a null pointer\r
+ smart_ptr_base(void);\r
+\r
+ // create a pointer containing a dynamically created object\r
+ // Note: the object must be allocated *by the user* with new\r
+ // constructor form - must be called in the form smart_ptr_base<type> x(new type(args))\r
+ explicit smart_ptr_base(T* data);\r
+\r
+ // copy constructor implements aliasing so no copy is made\r
+ // note that the copy constructor should NOT be explicit, as this breaks\r
+ // the returning of pointer objects from functions (at least within GCC 4.4)\r
+ smart_ptr_base(const smart_ptr_base<T,C>& r);\r
+\r
+ // assignment operator - required, else the output of GCC suffers segmentation faults\r
+ smart_ptr_base<T,C>& operator=(const smart_ptr_base<T,C>& r);\r
+\r
+ // destructor decrements the reference count and delete only when the last reference is destroyed\r
+ ~smart_ptr_base(void);\r
+\r
+ //////////////////////////////////////////////////////////////////////////////\r
+ // logical tests to see if there is anything contained in the pointer since it can be null\r
+\r
+ // there are two forms:explicit and implicit\r
+ // implicit: if(!r) or if(r)\r
+ // explicit: if(r.null()) or if(r.present())\r
+ operator bool(void) const;\r
+ bool operator!(void) const;\r
+ bool present(void) const;\r
+ bool null(void) const;\r
+\r
+ //////////////////////////////////////////////////////////////////////////////\r
+ // dereference operators and functions\r
+\r
+ // dereference the smart pointer to get the object - use in the form *p1\r
+ T& operator*(void) throw(null_dereference);\r
+ const T& operator*(void) const throw(null_dereference);\r
+\r
+ // used as a prefix to a member access to the contained object e.g. p1->print() calls T::print()\r
+ T* operator->(void) throw(null_dereference);\r
+ const T* operator->(void) const throw(null_dereference);\r
+\r
+ //////////////////////////////////////////////////////////////////////////////\r
+ // explicit function forms of the above assignment and dereference operators\r
+\r
+ // get the value\r
+ T& value(void) throw(null_dereference);\r
+ const T& value(void) const throw(null_dereference);\r
+\r
+ // set the pointer\r
+ // deletes the previous pointer and adopts the passed pointer instead\r
+ // Note: the object must be allocated *by the user* with new\r
+ // Warning: it is very easy to break the memory management with this operation\r
+ void set(T* data = 0);\r
+ // get the pointer\r
+ T* pointer(void);\r
+ const T* pointer(void) const;\r
+\r
+ //////////////////////////////////////////////////////////////////////////////\r
+ // functions to manage aliases\r
+\r
+ // make this an alias of the passed object\r
+ void alias(const smart_ptr_base<T,C>&);\r
+\r
+ // test whether two pointers point to the same object(known as aliasing the object)\r
+ // used in the form if(a.aliases(b))\r
+ bool aliases(const smart_ptr_base<T,C>&) const;\r
+\r
+ // find the number of aliases - used when you need to know whether an\r
+ // object is still referred to from elsewhere (rare!)\r
+ unsigned alias_count(void) const;\r
+\r
+ // delete the object and make the pointer null - does not make it unique\r
+ // first, so all other pointers to this will be null too\r
+ void clear(void);\r
+\r
+ // make the pointer unique and null in one step - does not affect other\r
+ // pointers that were pointing to the same object\r
+ void clear_unique(void);\r
+\r
+ //////////////////////////////////////////////////////////////////////////////\r
+ // functions that involve copying\r
+\r
+ // these functions use the copy functor passed as the template parameter C\r
+ // to copy the object with the right copy semantics. If the copy functor\r
+ // is no_copy, an exception will be thrown.\r
+\r
+ // create a pointer containing a *copy* of the object using the template parameter C\r
+ // this copy is taken because the pointer class maintains a dynamically allocated object\r
+ // and the T& may not be (usually is not) dynamically allocated\r
+ explicit smart_ptr_base(const T& data) throw(illegal_copy);\r
+\r
+ // set the value - note that this does a copy using the C template parameter\r
+ void set_value(const T& data) throw(illegal_copy);\r
+\r
+ // make this pointer unique with respect to any other references to the same object\r
+ // if this pointer is already unique, it does nothing - otherwise it copies the object\r
+ void make_unique(void) throw(illegal_copy);\r
+\r
+ // make this pointer a unique copy of the parameter\r
+ // useful for expressions like p1.copy(p2) which makes p1 a pointer to a unique copy of the contents of p2\r
+ void copy(const smart_ptr_base<T,C>&) throw(illegal_copy);\r
+\r
+ protected:\r
+ smart_ptr_holder<T>* m_holder;\r
+\r
+ public:\r
+ // internal use only - had to make them public because they need to be\r
+ // accessed by routines that could not be made friends\r
+ smart_ptr_holder<T>* _handle(void) const;\r
+ void _make_alias(smart_ptr_holder<T>* handle);\r
+ };\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // smart_ptr for simple types and classes which have copy constructors\r
+\r
+ template <typename T>\r
+ class smart_ptr : public smart_ptr_base<T, constructor_copy<T> >\r
+ {\r
+ public:\r
+ smart_ptr(void) {}\r
+ explicit smart_ptr(const T& data) : smart_ptr_base<T, constructor_copy<T> >(data) {}\r
+ explicit smart_ptr(T* data) : smart_ptr_base<T, constructor_copy<T> >(data) {}\r
+ smart_ptr<T>& operator=(const T& data) {set_value(data); return *this;}\r
+ smart_ptr<T>& operator=(T* data) {set(data); return *this;}\r
+ ~smart_ptr(void) {}\r
+ };\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // smart_ptr_clone for polymorphic class hierarchies which have a clone method\r
+\r
+ template <typename T>\r
+ class smart_ptr_clone : public smart_ptr_base<T, clone_copy<T> >\r
+ {\r
+ public:\r
+ smart_ptr_clone(void) {}\r
+ explicit smart_ptr_clone(const T& data) : smart_ptr_base<T, clone_copy<T> >(data) {}\r
+ explicit smart_ptr_clone(T* data) : smart_ptr_base<T, clone_copy<T> >(data) {}\r
+ smart_ptr_clone<T>& operator=(const T& data) {set_value(data); return *this;}\r
+ smart_ptr_clone<T>& operator=(T* data) {set(data); return *this;}\r
+ ~smart_ptr_clone(void) {}\r
+ };\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // smart_ptr_nocopy for any class that cannot or should not be copied\r
+\r
+ template <typename T>\r
+ class smart_ptr_nocopy : public smart_ptr_base<T, no_copy<T> >\r
+ {\r
+ public:\r
+ smart_ptr_nocopy(void) {}\r
+ explicit smart_ptr_nocopy(T* data) : smart_ptr_base<T, no_copy<T> >(data) {}\r
+ smart_ptr_nocopy<T>& operator=(T* data) {set(data); return *this;}\r
+ ~smart_ptr_nocopy(void) {}\r
+ };\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
+\r
+#include "smart_ptr.tpp"\r
+#endif\r
-////////////////////////////////////////////////////////////////////////////////
-
-// Author: Andy Rushton
-// Copyright: (c) Southampton University 1999-2004
-// (c) Andy Rushton 2004-2009
-// License: BSD License, see ../docs/license.html
-
-////////////////////////////////////////////////////////////////////////////////
-
-namespace stlplus
-{
-
- ////////////////////////////////////////////////////////////////////////////////
- // internal holder data structure
- ////////////////////////////////////////////////////////////////////////////////
-
- template<typename T>
- class smart_ptr_holder
- {
- private:
- unsigned m_count;
- T* m_data;
-
- // make these private to disallow copying because the holder doesn't know how to copy
- smart_ptr_holder(const smart_ptr_holder& s) :
- m_count(0), m_data(0)
- {
- }
-
- smart_ptr_holder& operator=(const smart_ptr_holder& s)
- {
- return *this;
- }
-
- public:
- smart_ptr_holder(T* p = 0) :
- m_count(1), m_data(p)
- {
- }
-
- ~smart_ptr_holder(void)
- {
- clear();
- }
-
- unsigned count(void) const
- {
- return m_count;
- }
-
- void increment(void)
- {
- ++m_count;
- }
-
- bool decrement(void)
- {
- --m_count;
- return m_count == 0;
- }
-
- bool null(void)
- {
- return m_data == 0;
- }
-
- void clear(void)
- {
- if(m_data)
- delete m_data;
- m_data = 0;
- }
-
- void set(T* p = 0)
- {
- clear();
- m_data = p;
- }
-
- T*& pointer(void)
- {
- return m_data;
- }
-
- const T* pointer(void) const
- {
- return m_data;
- }
-
- T& value(void)
- {
- return *m_data;
- }
-
- const T& value(void) const
- {
- return *m_data;
- }
- };
-
- ////////////////////////////////////////////////////////////////////////////////
- // smart_ptr_base class
- ////////////////////////////////////////////////////////////////////////////////
-
- ////////////////////////////////////////////////////////////////////////////////
- // constructors, assignments and destructors
-
- // create a null pointer
- template <typename T, typename C>
- smart_ptr_base<T,C>::smart_ptr_base(void) :
- m_holder(new smart_ptr_holder<T>)
- {
- }
-
- // create a pointer containing a *copy* of the object pointer
- template <typename T, typename C>
- smart_ptr_base<T,C>::smart_ptr_base(const T& data) throw(illegal_copy) :
- m_holder(new smart_ptr_holder<T>)
- {
- m_holder->set(C()(data));
- }
-
- // create a pointer containing a dynamically created object
- // Note: the object must be allocated *by the user* with new
- // constructor form - must be called in the form smart_ptr<type> x(new type(args))
- template <typename T, typename C>
- smart_ptr_base<T,C>::smart_ptr_base(T* data) :
- m_holder(new smart_ptr_holder<T>)
- {
- m_holder->set(data);
- }
-
- // copy constructor implements counted referencing - no copy is made
- template <typename T, typename C>
- smart_ptr_base<T,C>::smart_ptr_base(const smart_ptr_base<T,C>& r) :
- m_holder(0)
- {
- m_holder = r.m_holder;
- m_holder->increment();
- }
-
- // assignment operator - required, else the output of GCC suffers segmentation faults
- template <typename T, typename C>
- smart_ptr_base<T,C>& smart_ptr_base<T,C>::operator=(const smart_ptr_base<T,C>& r)
- {
- alias(r);
- return *this;
- }
-
- // destructor decrements the reference count and delete only when the last reference is destroyed
- template <typename T, typename C>
- smart_ptr_base<T,C>::~smart_ptr_base(void)
- {
- if(m_holder->decrement())
- delete m_holder;
- }
-
- //////////////////////////////////////////////////////////////////////////////
- // logical tests to see if there is anything contained in the pointer since it can be null
-
- template <typename T, typename C>
- bool smart_ptr_base<T,C>::null(void) const
- {
- return m_holder->null();
- }
-
- template <typename T, typename C>
- bool smart_ptr_base<T,C>::present(void) const
- {
- return !m_holder->null();
- }
-
- template <typename T, typename C>
- bool smart_ptr_base<T,C>::operator!(void) const
- {
- return m_holder->null();
- }
-
- template <typename T, typename C>
- smart_ptr_base<T,C>::operator bool(void) const
- {
- return !m_holder->null();
- }
-
- //////////////////////////////////////////////////////////////////////////////
- // dereference operators and functions
-
- template <typename T, typename C>
- T& smart_ptr_base<T,C>::operator*(void) throw(null_dereference)
- {
- if (m_holder->null()) throw null_dereference("null pointer dereferenced in smart_ptr::operator*");
- return m_holder->value();
- }
-
- template <typename T, typename C>
- const T& smart_ptr_base<T,C>::operator*(void) const throw(null_dereference)
- {
- if (m_holder->null()) throw null_dereference("null pointer dereferenced in smart_ptr::operator*");
- return m_holder->value();
- }
-
- template <typename T, typename C>
- T* smart_ptr_base<T,C>::operator->(void) throw(null_dereference)
- {
- if (m_holder->null()) throw null_dereference("null pointer dereferenced in smart_ptr::operator->");
- return m_holder->pointer();
- }
-
- template <typename T, typename C>
- const T* smart_ptr_base<T,C>::operator->(void) const throw(null_dereference)
- {
- if (m_holder->null()) throw null_dereference("null pointer dereferenced in smart_ptr::operator->");
- return m_holder->pointer();
- }
-
- //////////////////////////////////////////////////////////////////////////////
- // explicit function forms of the above assignment dereference operators
-
- template <typename T, typename C>
- void smart_ptr_base<T,C>::set_value(const T& data) throw(illegal_copy)
- {
- m_holder->set(C()(data));
- }
-
- template <typename T, typename C>
- T& smart_ptr_base<T,C>::value(void) throw(null_dereference)
- {
- if (m_holder->null()) throw null_dereference("null pointer dereferenced in smart_ptr::value");
- return m_holder->value();
- }
-
- template <typename T, typename C>
- const T& smart_ptr_base<T,C>::value(void) const throw(null_dereference)
- {
- if (m_holder->null()) throw null_dereference("null pointer dereferenced in smart_ptr::value");
- return m_holder->value();
- }
-
- template <typename T, typename C>
- void smart_ptr_base<T,C>::set(T* data)
- {
- m_holder->set(data);
- }
-
- template <typename T, typename C>
- T* smart_ptr_base<T,C>::pointer(void)
- {
- return m_holder->pointer();
- }
-
- template <typename T, typename C>
- const T* smart_ptr_base<T,C>::pointer(void) const
- {
- return m_holder->pointer();
- }
-
- ////////////////////////////////////////////////////////////////////////////////
- // functions to manage counted referencing
-
- // make this an alias of the passed object
- template <typename T, typename C>
- void smart_ptr_base<T,C>::alias(const smart_ptr_base<T,C>& r)
- {
- _make_alias(r.m_holder);
- }
-
- template <typename T, typename C>
- bool smart_ptr_base<T,C>::aliases(const smart_ptr_base<T,C>& r) const
- {
- return m_holder == r.m_holder;
- }
-
- template <typename T, typename C>
- unsigned smart_ptr_base<T,C>::alias_count(void) const
- {
- return m_holder->count();
- }
-
- template <typename T, typename C>
- void smart_ptr_base<T,C>::clear(void)
- {
- m_holder->clear();
- }
-
- template <typename T, typename C>
- void smart_ptr_base<T,C>::clear_unique(void)
- {
- if (m_holder->count() == 1)
- m_holder->clear();
- else
- {
- m_holder->decrement();
- m_holder = 0;
- m_holder = new smart_ptr_holder<T>;
- }
- }
-
- template <typename T, typename C>
- void smart_ptr_base<T,C>::make_unique(void) throw(illegal_copy)
- {
- if (m_holder->count() > 1)
- {
- smart_ptr_holder<T>* old_holder = m_holder;
- m_holder->decrement();
- m_holder = 0;
- m_holder = new smart_ptr_holder<T>;
- if (old_holder->pointer())
- m_holder->set(C()(old_holder->value()));
- }
- }
-
- template <typename T, typename C>
- void smart_ptr_base<T,C>::copy(const smart_ptr_base<T,C>& data) throw(illegal_copy)
- {
- alias(data);
- make_unique();
- }
-
- // internal function for distinguishing unique smart_ptr objects
- // used for example in persistence routines
-
- template <typename T, typename C>
- smart_ptr_holder<T>* smart_ptr_base<T,C>::_handle(void) const
- {
- return m_holder;
- }
-
- template <typename T, typename C>
- void smart_ptr_base<T,C>::_make_alias(smart_ptr_holder<T>* r_holder)
- {
- // make it alias-copy safe - this means that I don't try to do the
- // assignment if r is either the same object or an alias of it
- if (m_holder != r_holder)
- {
- if (m_holder->decrement())
- delete m_holder;
- m_holder = r_holder;
- m_holder->increment();
- }
- }
-
- ////////////////////////////////////////////////////////////////////////////////
-
-} // end namespace stlplus
-
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+// Author: Andy Rushton\r
+// Copyright: (c) Southampton University 1999-2004\r
+// (c) Andy Rushton 2004 onwards\r
+// License: BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // internal holder data structure\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+\r
+ template<typename T>\r
+ class smart_ptr_holder\r
+ {\r
+ private:\r
+ unsigned m_count;\r
+ T* m_data;\r
+\r
+ // make these private to disallow copying because the holder doesn't know how to copy\r
+ smart_ptr_holder(const smart_ptr_holder& s) :\r
+ m_count(0), m_data(0)\r
+ {\r
+ }\r
+\r
+ smart_ptr_holder& operator=(const smart_ptr_holder& s)\r
+ {\r
+ return *this;\r
+ }\r
+\r
+ public:\r
+ smart_ptr_holder(T* p = 0) :\r
+ m_count(1), m_data(p)\r
+ {\r
+ }\r
+\r
+ ~smart_ptr_holder(void)\r
+ {\r
+ clear();\r
+ }\r
+\r
+ unsigned count(void) const\r
+ {\r
+ return m_count;\r
+ }\r
+\r
+ void increment(void)\r
+ {\r
+ ++m_count;\r
+ }\r
+\r
+ bool decrement(void)\r
+ {\r
+ --m_count;\r
+ return m_count == 0;\r
+ }\r
+\r
+ bool null(void)\r
+ {\r
+ return m_data == 0;\r
+ }\r
+\r
+ void clear(void)\r
+ {\r
+ if(m_data)\r
+ delete m_data;\r
+ m_data = 0;\r
+ }\r
+\r
+ void set(T* p = 0)\r
+ {\r
+ clear();\r
+ m_data = p;\r
+ }\r
+\r
+ T*& pointer(void)\r
+ {\r
+ return m_data;\r
+ }\r
+\r
+ const T* pointer(void) const\r
+ {\r
+ return m_data;\r
+ }\r
+\r
+ T& value(void)\r
+ {\r
+ return *m_data;\r
+ }\r
+\r
+ const T& value(void) const\r
+ {\r
+ return *m_data;\r
+ }\r
+ };\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // smart_ptr_base class\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // constructors, assignments and destructors\r
+\r
+ // create a null pointer\r
+ template <typename T, typename C>\r
+ smart_ptr_base<T,C>::smart_ptr_base(void) :\r
+ m_holder(new smart_ptr_holder<T>)\r
+ {\r
+ }\r
+\r
+ // create a pointer containing a *copy* of the object pointer\r
+ template <typename T, typename C>\r
+ smart_ptr_base<T,C>::smart_ptr_base(const T& data) throw(illegal_copy) :\r
+ m_holder(new smart_ptr_holder<T>)\r
+ {\r
+ m_holder->set(C()(data));\r
+ }\r
+\r
+ // create a pointer containing a dynamically created object\r
+ // Note: the object must be allocated *by the user* with new\r
+ // constructor form - must be called in the form smart_ptr<type> x(new type(args))\r
+ template <typename T, typename C>\r
+ smart_ptr_base<T,C>::smart_ptr_base(T* data) :\r
+ m_holder(new smart_ptr_holder<T>)\r
+ {\r
+ m_holder->set(data);\r
+ }\r
+\r
+ // copy constructor implements counted referencing - no copy is made\r
+ template <typename T, typename C>\r
+ smart_ptr_base<T,C>::smart_ptr_base(const smart_ptr_base<T,C>& r) :\r
+ m_holder(0)\r
+ {\r
+ m_holder = r.m_holder;\r
+ m_holder->increment();\r
+ }\r
+\r
+ // assignment operator - required, else the output of GCC suffers segmentation faults\r
+ template <typename T, typename C>\r
+ smart_ptr_base<T,C>& smart_ptr_base<T,C>::operator=(const smart_ptr_base<T,C>& r) \r
+ {\r
+ alias(r);\r
+ return *this;\r
+ }\r
+\r
+ // destructor decrements the reference count and delete only when the last reference is destroyed\r
+ template <typename T, typename C>\r
+ smart_ptr_base<T,C>::~smart_ptr_base(void)\r
+ {\r
+ if(m_holder->decrement())\r
+ delete m_holder;\r
+ }\r
+\r
+ //////////////////////////////////////////////////////////////////////////////\r
+ // logical tests to see if there is anything contained in the pointer since it can be null\r
+\r
+ template <typename T, typename C>\r
+ bool smart_ptr_base<T,C>::null(void) const\r
+ {\r
+ return m_holder->null();\r
+ }\r
+\r
+ template <typename T, typename C>\r
+ bool smart_ptr_base<T,C>::present(void) const\r
+ {\r
+ return !m_holder->null();\r
+ }\r
+\r
+ template <typename T, typename C>\r
+ bool smart_ptr_base<T,C>::operator!(void) const\r
+ {\r
+ return m_holder->null();\r
+ }\r
+\r
+ template <typename T, typename C>\r
+ smart_ptr_base<T,C>::operator bool(void) const\r
+ {\r
+ return !m_holder->null();\r
+ }\r
+\r
+ //////////////////////////////////////////////////////////////////////////////\r
+ // dereference operators and functions\r
+\r
+ template <typename T, typename C>\r
+ T& smart_ptr_base<T,C>::operator*(void) throw(null_dereference)\r
+ {\r
+ if (m_holder->null()) throw null_dereference("null pointer dereferenced in smart_ptr::operator*");\r
+ return m_holder->value();\r
+ }\r
+\r
+ template <typename T, typename C>\r
+ const T& smart_ptr_base<T,C>::operator*(void) const throw(null_dereference)\r
+ {\r
+ if (m_holder->null()) throw null_dereference("null pointer dereferenced in smart_ptr::operator*");\r
+ return m_holder->value();\r
+ }\r
+\r
+ template <typename T, typename C>\r
+ T* smart_ptr_base<T,C>::operator->(void) throw(null_dereference)\r
+ {\r
+ if (m_holder->null()) throw null_dereference("null pointer dereferenced in smart_ptr::operator->");\r
+ return m_holder->pointer();\r
+ }\r
+\r
+ template <typename T, typename C>\r
+ const T* smart_ptr_base<T,C>::operator->(void) const throw(null_dereference)\r
+ {\r
+ if (m_holder->null()) throw null_dereference("null pointer dereferenced in smart_ptr::operator->");\r
+ return m_holder->pointer();\r
+ }\r
+\r
+ //////////////////////////////////////////////////////////////////////////////\r
+ // explicit function forms of the above assignment dereference operators\r
+\r
+ template <typename T, typename C>\r
+ void smart_ptr_base<T,C>::set_value(const T& data) throw(illegal_copy)\r
+ {\r
+ m_holder->set(C()(data));\r
+ }\r
+\r
+ template <typename T, typename C>\r
+ T& smart_ptr_base<T,C>::value(void) throw(null_dereference)\r
+ {\r
+ if (m_holder->null()) throw null_dereference("null pointer dereferenced in smart_ptr::value");\r
+ return m_holder->value();\r
+ }\r
+\r
+ template <typename T, typename C>\r
+ const T& smart_ptr_base<T,C>::value(void) const throw(null_dereference)\r
+ {\r
+ if (m_holder->null()) throw null_dereference("null pointer dereferenced in smart_ptr::value");\r
+ return m_holder->value();\r
+ }\r
+\r
+ template <typename T, typename C>\r
+ void smart_ptr_base<T,C>::set(T* data)\r
+ {\r
+ m_holder->set(data);\r
+ }\r
+\r
+ template <typename T, typename C>\r
+ T* smart_ptr_base<T,C>::pointer(void)\r
+ {\r
+ return m_holder->pointer();\r
+ }\r
+\r
+ template <typename T, typename C>\r
+ const T* smart_ptr_base<T,C>::pointer(void) const\r
+ {\r
+ return m_holder->pointer();\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // functions to manage counted referencing\r
+\r
+ // make this an alias of the passed object\r
+ template <typename T, typename C>\r
+ void smart_ptr_base<T,C>::alias(const smart_ptr_base<T,C>& r)\r
+ {\r
+ _make_alias(r.m_holder);\r
+ }\r
+\r
+ template <typename T, typename C>\r
+ bool smart_ptr_base<T,C>::aliases(const smart_ptr_base<T,C>& r) const\r
+ {\r
+ return m_holder == r.m_holder;\r
+ }\r
+\r
+ template <typename T, typename C>\r
+ unsigned smart_ptr_base<T,C>::alias_count(void) const\r
+ {\r
+ return m_holder->count();\r
+ }\r
+\r
+ template <typename T, typename C>\r
+ void smart_ptr_base<T,C>::clear(void)\r
+ {\r
+ m_holder->clear();\r
+ }\r
+\r
+ template <typename T, typename C>\r
+ void smart_ptr_base<T,C>::clear_unique(void)\r
+ {\r
+ if (m_holder->count() == 1)\r
+ m_holder->clear();\r
+ else\r
+ {\r
+ m_holder->decrement();\r
+ m_holder = 0;\r
+ m_holder = new smart_ptr_holder<T>;\r
+ }\r
+ }\r
+\r
+ template <typename T, typename C>\r
+ void smart_ptr_base<T,C>::make_unique(void) throw(illegal_copy)\r
+ {\r
+ if (m_holder->count() > 1)\r
+ {\r
+ smart_ptr_holder<T>* old_holder = m_holder;\r
+ m_holder->decrement();\r
+ m_holder = 0;\r
+ m_holder = new smart_ptr_holder<T>;\r
+ if (old_holder->pointer())\r
+ m_holder->set(C()(old_holder->value()));\r
+ }\r
+ }\r
+\r
+ template <typename T, typename C>\r
+ void smart_ptr_base<T,C>::copy(const smart_ptr_base<T,C>& data) throw(illegal_copy)\r
+ {\r
+ alias(data);\r
+ make_unique();\r
+ }\r
+\r
+ // internal function for distinguishing unique smart_ptr objects\r
+ // used for example in persistence routines\r
+\r
+ template <typename T, typename C>\r
+ smart_ptr_holder<T>* smart_ptr_base<T,C>::_handle(void) const\r
+ {\r
+ return m_holder;\r
+ }\r
+\r
+ template <typename T, typename C>\r
+ void smart_ptr_base<T,C>::_make_alias(smart_ptr_holder<T>* r_holder)\r
+ {\r
+ // make it alias-copy safe - this means that I don't try to do the\r
+ // assignment if r is either the same object or an alias of it\r
+ if (m_holder != r_holder)\r
+ {\r
+ if (m_holder->decrement())\r
+ delete m_holder;\r
+ m_holder = r_holder;\r
+ m_holder->increment();\r
+ }\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
+\r
-#ifndef STLPLUS_TRIPLE
-#define STLPLUS_TRIPLE
-////////////////////////////////////////////////////////////////////////////////
-
-// Author: Andy Rushton, from an original by Dan Milton
-// Copyright: (c) Southampton University 1999-2004
-// (c) Andy Rushton 2004-2009
-// License: BSD License, see ../docs/license.html
-
-// Similar to the STL pair but with three elements
-
-////////////////////////////////////////////////////////////////////////////////
-#include "containers_fixes.hpp"
-
-namespace stlplus
-{
-
- ////////////////////////////////////////////////////////////////////////////////
- // the triple class
-
- template<typename T1, typename T2, typename T3>
- struct triple
- {
- typedef T1 first_type;
- typedef T2 second_type;
- typedef T3 third_type;
-
- T1 first;
- T2 second;
- T3 third;
-
- triple(void);
- triple(const T1& p1, const T2& p2, const T3& p3);
- triple(const triple<T1,T2,T3>& t2);
- };
-
- ////////////////////////////////////////////////////////////////////////////////
- // creation
-
- template<typename T1, typename T2, typename T3>
- triple<T1,T2,T3> make_triple(const T1& first, const T2& second, const T3& third);
-
- ////////////////////////////////////////////////////////////////////////////////
- // comparison
-
- template<typename T1, typename T2, typename T3>
- bool operator == (const triple<T1,T2,T3>& left, const triple<T1,T2,T3>& right);
-
- ////////////////////////////////////////////////////////////////////////////////
-
-} // end namespace stlplus
-
-#include "triple.tpp"
-#endif
+#ifndef STLPLUS_TRIPLE\r
+#define STLPLUS_TRIPLE\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+// Author: Andy Rushton, from an original by Dan Milton\r
+// Copyright: (c) Southampton University 1999-2004\r
+// (c) Andy Rushton 2004 onwards\r
+// License: BSD License, see ../docs/license.html\r
+\r
+// Similar to the STL pair but with three elements\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "containers_fixes.hpp"\r
+\r
+namespace stlplus\r
+{\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // the triple class\r
+\r
+ template<typename T1, typename T2, typename T3>\r
+ struct triple\r
+ {\r
+ typedef T1 first_type;\r
+ typedef T2 second_type;\r
+ typedef T3 third_type;\r
+\r
+ T1 first;\r
+ T2 second;\r
+ T3 third;\r
+\r
+ triple(void);\r
+ triple(const T1& p1, const T2& p2, const T3& p3);\r
+ triple(const triple<T1,T2,T3>& t2);\r
+ };\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // creation\r
+\r
+ template<typename T1, typename T2, typename T3>\r
+ triple<T1,T2,T3> make_triple(const T1& first, const T2& second, const T3& third);\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // comparison\r
+\r
+ template<typename T1, typename T2, typename T3>\r
+ bool operator == (const triple<T1,T2,T3>& left, const triple<T1,T2,T3>& right);\r
+ template<typename T1, typename T2, typename T3>\r
+ bool operator < (const triple<T1,T2,T3>& left, const triple<T1,T2,T3>& right);\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
+\r
+#include "triple.tpp"\r
+#endif\r
-////////////////////////////////////////////////////////////////////////////////
-
-// Author: Andy Rushton, from an original by Dan Milton
-// Copyright: (c) Southampton University 1999-2004
-// (c) Andy Rushton 2004-2009
-// License: BSD License, see ../docs/license.html
-
-////////////////////////////////////////////////////////////////////////////////
-
-namespace stlplus
-{
-
- ////////////////////////////////////////////////////////////////////////////////
- // the triple class
-
- template<typename T1, typename T2, typename T3>
- triple<T1,T2,T3>::triple(void) :
- first(), second(), third()
- {
- }
-
- template<typename T1, typename T2, typename T3>
- triple<T1,T2,T3>::triple(const T1& p1, const T2& p2, const T3& p3) :
- first(p1), second(p2), third(p3)
- {
- }
-
- template<typename T1, typename T2, typename T3>
- triple<T1,T2,T3>::triple(const triple<T1,T2,T3>& t2) :
- first(t2.first), second(t2.second), third(t2.third)
- {
- }
-
- ////////////////////////////////////////////////////////////////////////////////
- // creation
-
- template<typename T1, typename T2, typename T3>
- triple<T1,T2,T3> make_triple(const T1& first, const T2& second, const T3& third)
- {
- return triple<T1,T2,T3>(first,second,third);
- }
-
- ////////////////////////////////////////////////////////////////////////////////
- // comparison
-
- template<typename T1, typename T2, typename T3>
- bool operator == (const triple<T1,T2,T3>& left, const triple<T1,T2,T3>& right)
- {
- // triples are equal if all elements are equal
- return left.first == right.first && left.second == right.second && left.third == right.third;
- }
-
- ////////////////////////////////////////////////////////////////////////////////
-
-} // end namespace stlplus
-
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+// Author: Andy Rushton, from an original by Dan Milton\r
+// Copyright: (c) Southampton University 1999-2004\r
+// (c) Andy Rushton 2004 onwards\r
+// License: BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // the triple class\r
+\r
+ template<typename T1, typename T2, typename T3>\r
+ triple<T1,T2,T3>::triple(void) :\r
+ first(), second(), third()\r
+ {\r
+ }\r
+\r
+ template<typename T1, typename T2, typename T3>\r
+ triple<T1,T2,T3>::triple(const T1& p1, const T2& p2, const T3& p3) :\r
+ first(p1), second(p2), third(p3)\r
+ {\r
+ }\r
+\r
+ template<typename T1, typename T2, typename T3>\r
+ triple<T1,T2,T3>::triple(const triple<T1,T2,T3>& t2) :\r
+ first(t2.first), second(t2.second), third(t2.third)\r
+ {\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // creation\r
+\r
+ template<typename T1, typename T2, typename T3>\r
+ triple<T1,T2,T3> make_triple(const T1& first, const T2& second, const T3& third)\r
+ {\r
+ return triple<T1,T2,T3>(first,second,third);\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // comparison\r
+\r
+ template<typename T1, typename T2, typename T3>\r
+ bool operator == (const triple<T1,T2,T3>& left, const triple<T1,T2,T3>& right)\r
+ {\r
+ // triples are equal if all elements are equal\r
+ return left.first == right.first && left.second == right.second && left.third == right.third;\r
+ }\r
+\r
+ template<typename T1, typename T2, typename T3>\r
+ bool operator < (const triple<T1,T2,T3>& left, const triple<T1,T2,T3>& right)\r
+ {\r
+ // use the < operator on each element\r
+ return left.first < right.first ? true :\r
+ right.first < left.first ? false :\r
+ left.second < right.second ? true :\r
+ right.second < left.second ? false :\r
+ left.third < right.third;\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
+\r
-////////////////////////////////////////////////////////////////////////////////
-
-// Author: Andy Rushton
-// Copyright: (c) Southampton University 1999-2004
-// (c) Andy Rushton 2004-2009
-// License: BSD License, see ../docs/license.html
-
-////////////////////////////////////////////////////////////////////////////////
-#include "build.hpp"
-#include "version.hpp"
-#include "dprintf.hpp"
-////////////////////////////////////////////////////////////////////////////////
-
-namespace stlplus
-{
-
- ////////////////////////////////////////////////////////////////////////////////
- // report the platform-specific details of this build
-
- std::string build(void)
- {
- ////////////////////////////////////////////////////////////////////////////////
- // work out the platform
-
-#ifdef _WIN32
- std::string platform("Windows");
-#else
- // at present there are no variations between different Unix platforms so
- // they all map onto the generic Unix platform
- std::string platform("Generic Unix");
-#endif
-
- ////////////////////////////////////////////////////////////////////////////////
- // work out the compiler
-
-#if defined __GNUC__
- std::string compiler(dformat("gcc v%s",__VERSION__));
-#elif defined _MSC_VER
- std::string compiler(dformat("MSVC v%0.2f",((float)_MSC_VER)/100.0));
-#elif defined __BORLANDC__
- std::string compiler(dformat("Borland v%d.%d",__BORLANDC__/256,__BORLANDC__/16%16));
-#else
- std::string compiler("unknown compiler");
-#endif
-
- ////////////////////////////////////////////////////////////////////////////////
- // work out the kind of build
- // there are two variants - debug and release
-
-#ifndef NDEBUG
- std::string variant("debug");
-#else
- std::string variant("release");
-#endif
-
- return std::string("STLplus v") + version() + ", " + platform + ", " + compiler + ", " + variant;
- }
-
-////////////////////////////////////////////////////////////////////////////////
-} // end namespace stlplus
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+// Author: Andy Rushton\r
+// Copyright: (c) Southampton University 1999-2004\r
+// (c) Andy Rushton 2004 onwards\r
+// License: BSD License, see ../docs/license.html\r
+\r
+// report the platform-specific details of this build\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "build.hpp"\r
+#include "version.hpp"\r
+#include "dprintf.hpp"\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+ // STLplus version in the form "STLplus v3.0" - see version.hpp for a way of getting just the version number\r
+ std::string stlplus_version(void)\r
+ {\r
+ return std::string("STLplus v") + version();\r
+ }\r
+\r
+ // platform is the target operating system in the form "Windows" or "Generic Unix"\r
+ std::string platform(void)\r
+ {\r
+#if defined _WIN32\r
+ return std::string("Windows");\r
+#else\r
+ // at present there are no variations between different Unix platforms so\r
+ // they all map onto the generic Unix platform\r
+ return std::string("Generic Unix");\r
+#endif\r
+ }\r
+\r
+ // compiler_name is the short name of the compiler, e.g. "gcc" or "MSVC"\r
+ std::string compiler_name(void)\r
+ {\r
+#if defined __GNUC__\r
+ return std::string("gcc");\r
+#elif defined _MSC_VER\r
+ return std::string("MSVC");\r
+#elif defined __BORLANDC__\r
+ return std::string("Borland");\r
+#else\r
+ return std::string("unknown compiler");\r
+#endif\r
+ }\r
+\r
+ // compiler_version is the version string of the compiler e.g. "3.4" for gcc or "15.00" for MSVC\r
+ std::string compiler_version(void)\r
+ {\r
+#if defined __GNUC__\r
+ return dformat("%d.%d.%d",__GNUC__,__GNUC_MINOR__,__GNUC_PATCHLEVEL__);\r
+#elif defined _MSC_VER\r
+ return dformat("%0.2f",((float)_MSC_VER)/100.0);\r
+#elif defined __BORLANDC__\r
+ return dformat("%d.%d%d",__BORLANDC__/256,__BORLANDC__/16%16,__BORLANDC__%16);\r
+#else\r
+ return std::string();\r
+#endif\r
+ }\r
+\r
+ // compiler is the compilation system and version above combined into a human- readable form e.g. "gcc v3.4"\r
+ std::string compiler(void)\r
+ {\r
+ return compiler_name() + std::string(" v") + compiler_version();\r
+ }\r
+\r
+ // variant is the kind of build - "debug" or "release"\r
+ std::string variant(void)\r
+ {\r
+#ifndef NDEBUG\r
+ return std::string("debug");\r
+#else\r
+ return std::string("release");\r
+#endif\r
+\r
+ }\r
+\r
+ std::string build(void)\r
+ {\r
+ return stlplus_version() + ", " + platform() + ", " + compiler() + ", " + variant();\r
+ }\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+} // end namespace stlplus\r
-#ifndef STLPLUS_BUILD
-#define STLPLUS_BUILD
-////////////////////////////////////////////////////////////////////////////////
-
-// Author: Andy Rushton
-// Copyright: (c) Southampton University 1999-2004
-// (c) Andy Rushton 2004-2009
-// License: BSD License, see ../docs/license.html
-
-// Provides a printable representation of the build characteristics in the form:
-
-// version, platform, compiler, variant
-
-// Where
-// version is the version of STLplus
-// platform is the target operating system
-// compiler is the compilation system and version that the function was compiled with
-// variant is the kind of build - debug or release
-
-// Example:
-// STLplus version 3.0, Generic Unix, gcc v3.4, debug
-
-////////////////////////////////////////////////////////////////////////////////
-#include "portability_fixes.hpp"
-#include <string>
-
-namespace stlplus
-{
-
- std::string build(void);
-
-}
-////////////////////////////////////////////////////////////////////////////////
-#endif
+#ifndef STLPLUS_BUILD\r
+#define STLPLUS_BUILD\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+// Author: Andy Rushton\r
+// Copyright: (c) Southampton University 1999-2004\r
+// (c) Andy Rushton 2004 onwards\r
+// License: BSD License, see ../docs/license.html\r
+\r
+// Provides a printable representation of the build characteristics in the form:\r
+\r
+// version, platform, compiler, variant\r
+\r
+// Where\r
+// version is the version of STLplus\r
+// platform is the target operating system\r
+// compiler is the compilation system and version that the function was compiled with\r
+// variant is the kind of build - debug or release\r
+\r
+// Example:\r
+// STLplus version 3.0, Generic Unix, gcc v3.4, debug\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "portability_fixes.hpp"\r
+#include <string>\r
+\r
+namespace stlplus\r
+{\r
+\r
+ // STLplus version in the form "STLplus version 3.0" - see version.hpp for a way of getting just the version number\r
+ std::string stlplus_version(void);\r
+\r
+ // platform is the target operating system in the form "Windows" or "Generic Unix"\r
+ std::string platform(void);\r
+\r
+ // compiler_name is the short name of the compiler, e.g. "gcc" or "MSVC"\r
+ std::string compiler_name(void);\r
+ // compiler_version is the version string of the compiler e.g. "3.4" for gcc or "15.00" for MSVC\r
+ std::string compiler_version(void);\r
+ // compiler is the compilation system and version above combined into a human- readable form e.g. "gcc v3.4"\r
+ std::string compiler(void);\r
+\r
+ // variant is the kind of build - "debug" or "release"\r
+ std::string variant(void);\r
+\r
+ // build is all of the above combined into a human-readable string\r
+ std::string build(void);\r
+\r
+}\r
+////////////////////////////////////////////////////////////////////////////////\r
+#endif\r
-////////////////////////////////////////////////////////////////////////////////
-
-// Author: Andy Rushton
-// Copyright: (c) Southampton University 1999-2004
-// (c) Andy Rushton 2004-2009
-// License: BSD License, see ../docs/license.html
-
-////////////////////////////////////////////////////////////////////////////////
-
-#include "debug.hpp"
-#include "dprintf.hpp"
-#include <string.h>
-#include <stdlib.h>
-#include <stdio.h>
-
-////////////////////////////////////////////////////////////////////////////////
-
-namespace stlplus
-{
-
- ////////////////////////////////////////////////////////////////////////////////
-
- static std::string format(const char* file, int line, const char* function, const char* message)
- {
- return dformat("%s:%d:%s: assertion failed: %s",
- (file ? file : ""),
- line,
- (function ? function : "") ,
- (message ? message : ""));
- }
-
- ////////////////////////////////////////////////////////////////////////////////
-
- assert_failed::assert_failed(const char* file, int line, const char* function, const char* message)
- throw() :
- std::logic_error(format(file, line, function, message))
- {
- }
-
- assert_failed::~assert_failed(void) throw()
- {
- }
-
- ////////////////////////////////////////////////////////////////////////////////
-
- static unsigned _debug_depth = 0;
- static bool _debug_global = false;
- static bool _debug_set = false;
- static bool _debug_recurse = false;
- static bool _debug_read = false;
- static char* _debug_match = 0;
- static const debug_trace* debug_last = 0;
-
- void debug_global(const char* file, int line, const char* function, bool state)
- {
- _debug_global = state;
- fprintf(stderr, "%s:%i:[%i]%s ", file, line, _debug_depth, function ? function : "");
- fprintf(stderr, "debug global : %s\n", _debug_global ? "on" : "off");
- }
-
- void debug_assert_fail(const char* file, int line, const char* function, const char* test)
- throw(assert_failed)
- {
- fprintf(stderr, "%s:%i:[%i]%s: assertion failed: %s\n",
- file, line, _debug_depth, function ? function : "", test);
- if (debug_last) debug_last->stackdump();
- throw assert_failed(file, line, function, test);
- }
-
- ////////////////////////////////////////////////////////////////////////////////
-
- debug_trace::debug_trace(const char* f, int l, const char* fn) :
- m_file(f), m_line(l), m_function(fn ? fn : ""),
- m_depth(0), m_last(debug_last), m_dbg(false), m_old(false)
- {
- if (!_debug_read)
- {
- _debug_match = getenv("DEBUG");
- _debug_recurse = getenv("DEBUG_LOCAL") == 0;
- _debug_read = true;
- }
- m_dbg = _debug_set || (_debug_match && (!_debug_match[0] || (strcmp(_debug_match, m_file) == 0)));
- m_old = _debug_set;
- if (m_dbg && _debug_recurse)
- _debug_set = true;
- m_depth = ++_debug_depth;
- debug_last = this;
- if (debug()) report(std::string("entering ") + (m_function ? m_function : ""));
- }
-
- debug_trace::~debug_trace(void)
- {
- if (debug()) report("leaving");
- --_debug_depth;
- _debug_set = m_old;
- debug_last = m_last;
- }
-
- const char* debug_trace::file(void) const
- {
- return m_file;
- }
-
- int debug_trace::line(void) const
- {
- return m_line;
- }
-
- bool debug_trace::debug(void) const
- {
- return m_dbg || _debug_global;
- }
-
- void debug_trace::debug_on(int l, bool recurse)
- {
- m_dbg = true;
- m_old = _debug_set;
- if (recurse)
- _debug_set = true;
- report(l, std::string("debug on") + (recurse ? " recursive" : ""));
- }
-
- void debug_trace::debug_off(int l)
- {
- if (debug()) report(l, std::string("debug off"));
- m_dbg = false;
- _debug_set = m_old;
- }
-
- void debug_trace::prefix(int l) const
- {
- fprintf(stderr, "%s:%i:[%i]%s ", m_file, l, m_depth, m_function ? m_function : "");
- }
-
- void debug_trace::do_report(int l, const std::string& message) const
- {
- prefix(l);
- fprintf(stderr, "%s\n", message.c_str());
- fflush(stderr);
- }
-
- void debug_trace::do_report(const std::string& message) const
- {
- do_report(m_line, message);
- }
-
- void debug_trace::report(int l, const std::string& message) const
- {
- do_report(l, message);
- }
-
- void debug_trace::report(const std::string& message) const
- {
- report(m_line, message);
- }
-
- void debug_trace::error(int l, const std::string& message) const
- {
- do_report(l, "ERROR: " + message);
- }
-
- void debug_trace::error(const std::string& message) const
- {
- error(m_line, message);
- }
-
- void debug_trace::stackdump(int l, const std::string& message) const
- {
- do_report(l, message);
- stackdump();
- }
-
- void debug_trace::stackdump(const std::string& message) const
- {
- stackdump(m_line, message);
- }
-
- void debug_trace::stackdump(void) const
- {
- for (const debug_trace* item = this; item; item = item->m_last)
- item->do_report("...called from here");
- }
-
-
- ////////////////////////////////////////////////////////////////////////////////
-
-} // end namespace stlplus
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+// Author: Andy Rushton\r
+// Copyright: (c) Southampton University 1999-2004\r
+// (c) Andy Rushton 2004 onwards\r
+// License: BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+#include "debug.hpp"\r
+#include "dprintf.hpp"\r
+#include <string.h>\r
+#include <stdlib.h>\r
+#include <stdio.h>\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+\r
+ static std::string format(const char* file, int line, const char* function, const char* message)\r
+ {\r
+ return dformat("%s:%d:%s: assertion failed: %s",\r
+ (file ? file : ""),\r
+ line,\r
+ (function ? function : "") ,\r
+ (message ? message : ""));\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+\r
+ assert_failed::assert_failed(const char* file, int line, const char* function, const char* message)\r
+ throw() : \r
+ std::logic_error(format(file, line, function, message))\r
+ {\r
+ }\r
+\r
+ assert_failed::~assert_failed(void) throw()\r
+ {\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+\r
+ static unsigned _debug_depth = 0;\r
+ static bool _debug_global = false;\r
+ static bool _debug_set = false;\r
+ static bool _debug_recurse = false;\r
+ static bool _debug_read = false;\r
+ static char* _debug_match = 0;\r
+ static const debug_trace* debug_last = 0;\r
+\r
+ void debug_global(const char* file, int line, const char* function, bool state)\r
+ {\r
+ _debug_global = state;\r
+ fprintf(stderr, "%s:%i:[%i]%s ", file, line, _debug_depth, function ? function : "");\r
+ fprintf(stderr, "debug global : %s\n", _debug_global ? "on" : "off");\r
+ }\r
+\r
+ void debug_assert_fail(const char* file, int line, const char* function, const char* test) \r
+ throw(assert_failed)\r
+ {\r
+ fprintf(stderr, "%s:%i:[%i]%s: assertion failed: %s\n", \r
+ file, line, _debug_depth, function ? function : "", test);\r
+ if (debug_last) debug_last->stackdump();\r
+ throw assert_failed(file, line, function, test);\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+\r
+ debug_trace::debug_trace(const char* f, int l, const char* fn) :\r
+ m_file(f), m_line(l), m_function(fn ? fn : ""), \r
+ m_depth(0), m_last(debug_last), m_dbg(false), m_old(false)\r
+ {\r
+ if (!_debug_read)\r
+ {\r
+ _debug_match = getenv("DEBUG");\r
+ _debug_recurse = getenv("DEBUG_LOCAL") == 0;\r
+ _debug_read = true;\r
+ }\r
+ m_dbg = _debug_set || (_debug_match && (!_debug_match[0] || (strcmp(_debug_match, m_file) == 0)));\r
+ m_old = _debug_set;\r
+ if (m_dbg && _debug_recurse)\r
+ _debug_set = true;\r
+ m_depth = ++_debug_depth;\r
+ debug_last = this;\r
+ if (debug()) report(std::string("entering ") + (m_function ? m_function : ""));\r
+ }\r
+\r
+ debug_trace::~debug_trace(void)\r
+ {\r
+ if (debug()) report("leaving");\r
+ --_debug_depth;\r
+ _debug_set = m_old;\r
+ debug_last = m_last;\r
+ }\r
+\r
+ const char* debug_trace::file(void) const\r
+ {\r
+ return m_file;\r
+ }\r
+\r
+ int debug_trace::line(void) const\r
+ {\r
+ return m_line;\r
+ }\r
+\r
+ bool debug_trace::debug(void) const\r
+ {\r
+ return m_dbg || _debug_global;\r
+ }\r
+\r
+ void debug_trace::debug_on(int l, bool recurse)\r
+ {\r
+ m_dbg = true;\r
+ m_old = _debug_set;\r
+ if (recurse)\r
+ _debug_set = true;\r
+ report(l, std::string("debug on") + (recurse ? " recursive" : ""));\r
+ }\r
+\r
+ void debug_trace::debug_off(int l)\r
+ {\r
+ if (debug()) report(l, std::string("debug off"));\r
+ m_dbg = false;\r
+ _debug_set = m_old;\r
+ }\r
+\r
+ void debug_trace::prefix(int l) const\r
+ {\r
+ fprintf(stderr, "%s:%i:[%i]%s ", m_file, l, m_depth, m_function ? m_function : "");\r
+ }\r
+\r
+ void debug_trace::do_report(int l, const std::string& message) const\r
+ {\r
+ prefix(l);\r
+ fprintf(stderr, "%s\n", message.c_str());\r
+ fflush(stderr);\r
+ }\r
+\r
+ void debug_trace::do_report(const std::string& message) const\r
+ {\r
+ do_report(m_line, message);\r
+ }\r
+\r
+ void debug_trace::report(int l, const std::string& message) const\r
+ {\r
+ do_report(l, message);\r
+ }\r
+\r
+ void debug_trace::report(const std::string& message) const\r
+ {\r
+ report(m_line, message);\r
+ }\r
+\r
+ void debug_trace::error(int l, const std::string& message) const\r
+ {\r
+ do_report(l, "ERROR: " + message);\r
+ }\r
+\r
+ void debug_trace::error(const std::string& message) const\r
+ {\r
+ error(m_line, message);\r
+ }\r
+\r
+ void debug_trace::stackdump(int l, const std::string& message) const\r
+ {\r
+ do_report(l, message);\r
+ stackdump();\r
+ }\r
+\r
+ void debug_trace::stackdump(const std::string& message) const\r
+ {\r
+ stackdump(m_line, message);\r
+ }\r
+\r
+ void debug_trace::stackdump(void) const\r
+ {\r
+ for (const debug_trace* item = this; item; item = item->m_last)\r
+ item->do_report("...called from here");\r
+ }\r
+\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
-#ifndef STLPLUS_DEBUG
-#define STLPLUS_DEBUG
-////////////////////////////////////////////////////////////////////////////////
-
-// Author: Andy Rushton
-// Copyright: (c) Southampton University 1999-2004
-// (c) Andy Rushton 2004-2009
-// License: BSD License, see ../docs/license.html
-
-// Set of simple debug utilities, all of which are switched off by the
-// NDEBUG compiler directive
-
-////////////////////////////////////////////////////////////////////////////////
-
-#include "portability_fixes.hpp"
-#include <stdexcept>
-#include <string>
-
-////////////////////////////////////////////////////////////////////////////////
-// Problem with missing __FUNCTION__ macro
-////////////////////////////////////////////////////////////////////////////////
-// this macro is used in debugging but was missing in Visual Studio prior to version 7
-// it also has a different name in Borland
-
-#if defined(_MSC_VER) && (_MSC_VER < 1300)
-#define __FUNCTION__ 0
-#endif
-
-#ifdef __BORLANDC__
-#define __FUNCTION__ __FUNC__
-#endif
-
-////////////////////////////////////////////////////////////////////////////////
-// Exception thrown if an assertion fails
-
-namespace stlplus
-{
-
- class assert_failed : public std::logic_error
- {
- public:
- assert_failed(const char* file, int line, const char* function, const char* message) throw();
- ~assert_failed(void) throw();
- };
-
-} // end namespace stlplus
-
- ////////////////////////////////////////////////////////////////////////////////
- // The macros used in debugging
-
-#ifndef NDEBUG
-
-#define DEBUG_TRACE stlplus::debug_trace stlplus_debug_trace(__FILE__,__LINE__,__FUNCTION__)
-#define IF_DEBUG(stmts) {if (stlplus_debug_trace.debug()){stlplus_debug_trace.prefix(__LINE__);stmts;}}
-#define DEBUG_REPORT(str) IF_DEBUG(stlplus_debug_trace.report(__LINE__,str))
-#define DEBUG_ERROR(str) stlplus_debug_trace.error(__LINE__,str)
-#define DEBUG_STACKDUMP(str) stlplus_debug_trace.stackdump(__LINE__,str)
-#define DEBUG_ON stlplus_debug_trace.debug_on(__LINE__,true)
-#define DEBUG_ON_LOCAL stlplus_debug_trace.debug_on(__LINE__,false)
-#define DEBUG_ON_GLOBAL stlplus::debug_global(__FILE__,__LINE__,__FUNCTION__,true)
-#define DEBUG_OFF_GLOBAL stlplus::debug_global(__FILE__,__LINE__,__FUNCTION__,false)
-#define DEBUG_OFF stlplus_debug_trace.debug_off(__LINE__)
-#define DEBUG_ASSERT(test) if (!(test))stlplus::debug_assert_fail(__FILE__,__LINE__,__FUNCTION__,#test)
-
-#else
-
-#define DEBUG_TRACE
-#define IF_DEBUG(stmts)
-#define DEBUG_REPORT(str)
-#define DEBUG_ERROR(str)
-#define DEBUG_STACKDUMP(str)
-#define DEBUG_ON
-#define DEBUG_ON_LOCAL
-#define DEBUG_ON_GLOBAL
-#define DEBUG_OFF_GLOBAL
-#define DEBUG_OFF
-#define DEBUG_ASSERT(test)
-
-#endif
-
-////////////////////////////////////////////////////////////////////////////////
-// infrastructure - don't use directly
-
-namespace stlplus
-{
-
- void debug_global(const char* file, int line, const char* function, bool state = true);
- void debug_assert_fail(const char* file, int line, const char* function, const char* test) throw(assert_failed);
-
- class debug_trace
- {
- public:
- debug_trace(const char* f, int l, const char* fn);
- ~debug_trace(void);
- const char* file(void) const;
- int line(void) const;
- bool debug(void) const;
- void debug_on(int l, bool recurse);
- void debug_off(int l);
- void prefix(int l) const;
- void report(int l, const std::string& message) const;
- void report(const std::string& message) const;
- void error(int l, const std::string& message) const;
- void error(const std::string& message) const;
- void stackdump(int l, const std::string& message) const;
- void stackdump(const std::string& message) const;
- void stackdump(void) const;
-
- private:
- const char* m_file;
- int m_line;
- const char* m_function;
- unsigned m_depth;
- const debug_trace* m_last;
- bool m_dbg;
- bool m_old;
- void do_report(int l, const std::string& message) const;
- void do_report(const std::string& message) const;
-
- // make this class uncopyable
- debug_trace(const debug_trace&);
- debug_trace& operator = (const debug_trace&);
- };
-
-} // end namespace stlplus
-
- ////////////////////////////////////////////////////////////////////////////////
-#endif
+#ifndef STLPLUS_DEBUG\r
+#define STLPLUS_DEBUG\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+// Author: Andy Rushton\r
+// Copyright: (c) Southampton University 1999-2004\r
+// (c) Andy Rushton 2004 onwards\r
+// License: BSD License, see ../docs/license.html\r
+\r
+// Set of simple debug utilities, all of which are switched off by the\r
+// NDEBUG compiler directive\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+#include "portability_fixes.hpp"\r
+#include <stdexcept>\r
+#include <string>\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// Problem with missing __FUNCTION__ macro\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+// this macro is used in debugging but was missing in Visual Studio prior to version 7\r
+#if defined(_MSC_VER) && (_MSC_VER < 1300)\r
+#define __FUNCTION__ 0\r
+#endif\r
+\r
+// old versions of Borland compiler defined a macro __FUNC__ but more recent ones define __FUNCTION__\r
+// I'm not sure at what version this change was made - assumed C++ Builder 6.32\r
+#if defined(__BORLANDC__) && (__BORLANDC__ < 1585)\r
+#define __FUNCTION__ __FUNC__\r
+#endif\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// Exception thrown if an assertion fails\r
+\r
+namespace stlplus\r
+{\r
+\r
+ class assert_failed : public std::logic_error\r
+ {\r
+ public:\r
+ assert_failed(const char* file, int line, const char* function, const char* message) throw();\r
+ ~assert_failed(void) throw();\r
+ };\r
+\r
+} // end namespace stlplus\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // The macros used in debugging\r
+\r
+#ifndef NDEBUG\r
+\r
+#define DEBUG_TRACE stlplus::debug_trace stlplus_debug_trace(__FILE__,__LINE__,__FUNCTION__)\r
+#define IF_DEBUG(stmts) {if (stlplus_debug_trace.debug()){stlplus_debug_trace.prefix(__LINE__);stmts;}}\r
+#define DEBUG_REPORT(str) IF_DEBUG(stlplus_debug_trace.report(__LINE__,str))\r
+#define DEBUG_ERROR(str) stlplus_debug_trace.error(__LINE__,str)\r
+#define DEBUG_STACKDUMP(str) stlplus_debug_trace.stackdump(__LINE__,str)\r
+#define DEBUG_ON stlplus_debug_trace.debug_on(__LINE__,true)\r
+#define DEBUG_ON_LOCAL stlplus_debug_trace.debug_on(__LINE__,false)\r
+#define DEBUG_ON_GLOBAL stlplus::debug_global(__FILE__,__LINE__,__FUNCTION__,true)\r
+#define DEBUG_OFF_GLOBAL stlplus::debug_global(__FILE__,__LINE__,__FUNCTION__,false)\r
+#define DEBUG_OFF stlplus_debug_trace.debug_off(__LINE__)\r
+#define DEBUG_ASSERT(test) if (!(test))stlplus::debug_assert_fail(__FILE__,__LINE__,__FUNCTION__,#test)\r
+\r
+#else\r
+\r
+#define DEBUG_TRACE\r
+#define IF_DEBUG(stmts)\r
+#define DEBUG_REPORT(str)\r
+#define DEBUG_ERROR(str)\r
+#define DEBUG_STACKDUMP(str)\r
+#define DEBUG_ON\r
+#define DEBUG_ON_LOCAL\r
+#define DEBUG_ON_GLOBAL\r
+#define DEBUG_OFF_GLOBAL\r
+#define DEBUG_OFF\r
+#define DEBUG_ASSERT(test)\r
+\r
+#endif\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// infrastructure - don't use directly\r
+\r
+namespace stlplus\r
+{\r
+\r
+ void debug_global(const char* file, int line, const char* function, bool state = true);\r
+ void debug_assert_fail(const char* file, int line, const char* function, const char* test) throw(assert_failed);\r
+\r
+ class debug_trace\r
+ {\r
+ public:\r
+ debug_trace(const char* f, int l, const char* fn);\r
+ ~debug_trace(void);\r
+ const char* file(void) const;\r
+ int line(void) const;\r
+ bool debug(void) const;\r
+ void debug_on(int l, bool recurse);\r
+ void debug_off(int l);\r
+ void prefix(int l) const;\r
+ void report(int l, const std::string& message) const;\r
+ void report(const std::string& message) const;\r
+ void error(int l, const std::string& message) const;\r
+ void error(const std::string& message) const;\r
+ void stackdump(int l, const std::string& message) const;\r
+ void stackdump(const std::string& message) const;\r
+ void stackdump(void) const;\r
+\r
+ private:\r
+ const char* m_file;\r
+ int m_line;\r
+ const char* m_function;\r
+ unsigned m_depth;\r
+ const debug_trace* m_last;\r
+ bool m_dbg;\r
+ bool m_old;\r
+ void do_report(int l, const std::string& message) const;\r
+ void do_report(const std::string& message) const;\r
+\r
+ // make this class uncopyable\r
+ debug_trace(const debug_trace&);\r
+ debug_trace& operator = (const debug_trace&);\r
+ };\r
+\r
+} // end namespace stlplus\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+#endif\r
-////////////////////////////////////////////////////////////////////////////////
-
-// Author: Andy Rushton
-// Copyright: (c) Southampton University 1999-2004
-// (c) Andy Rushton 2004-2009
-// License: BSD License, see ../docs/license.html
-
-// Notes:
-
-// Feb 2007: Rewritten in terms of platform-specific fixes to the
-// buffer-overflow problem. Using native functions for this has the added
-// benefit of giving access to other features of the C-runtime such as Unicode
-// support.
-
-////////////////////////////////////////////////////////////////////////////////
-
-#include "dprintf.hpp"
-#include <stdio.h>
-#include <limits.h>
-#include <float.h>
-#include <ctype.h>
-#include <stdlib.h>
-
-////////////////////////////////////////////////////////////////////////////////
-
-namespace stlplus
-{
-
-////////////////////////////////////////////////////////////////////////////////
-
- int vdprintf(std::string& formatted, const char* format, va_list args)
- {
-#ifdef MSWINDOWS
- int length = 0;
- char* buffer = 0;
- for(int buffer_length = 256; ; buffer_length*=2)
- {
- buffer = (char*)malloc(buffer_length);
- if (!buffer) return -1;
- length = _vsnprintf(buffer, buffer_length-1, format, args);
- if (length >= 0)
- {
- buffer[length] = 0;
- formatted += std::string(buffer);
- free(buffer);
- break;
- }
- free(buffer);
- }
- return length;
-#else
- char* buffer = 0;
- int length = vasprintf(&buffer, format, args);
- if (!buffer) return -1;
- if (length >= 0)
- formatted += std::string(buffer);
- free(buffer);
- return length;
-#endif
- }
-
- int dprintf(std::string& formatted, const char* format, ...)
- {
- va_list args;
- va_start(args, format);
- int result = vdprintf(formatted, format, args);
- va_end(args);
- return result;
- }
-
- std::string vdformat(const char* format, va_list args) throw(std::invalid_argument)
- {
- std::string formatted;
- int length = vdprintf(formatted, format, args);
- if (length < 0) throw std::invalid_argument("dprintf");
- return formatted;
- }
-
- std::string dformat(const char* format, ...) throw(std::invalid_argument)
- {
- std::string formatted;
- va_list args;
- va_start(args, format);
- int length = vdprintf(formatted, format, args);
- va_end(args);
- if (length < 0) throw std::invalid_argument("dprintf");
- return formatted;
- }
-
-////////////////////////////////////////////////////////////////////////////////
-
-} // end namespace stlplus
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+// Author: Andy Rushton\r
+// Copyright: (c) Southampton University 1999-2004\r
+// (c) Andy Rushton 2004 onwards\r
+// License: BSD License, see ../docs/license.html\r
+\r
+// Notes:\r
+\r
+// Feb 2007: Rewritten in terms of platform-specific fixes to the\r
+// buffer-overflow problem. Using native functions for this has the added\r
+// benefit of giving access to other features of the C-runtime such as Unicode\r
+// support.\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+#include "dprintf.hpp"\r
+#include <stdio.h>\r
+#include <limits.h>\r
+#include <float.h>\r
+#include <ctype.h>\r
+#include <stdlib.h>\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+ int vdprintf(std::string& formatted, const char* format, va_list args)\r
+ {\r
+#ifdef MSWINDOWS\r
+ int length = 0;\r
+ char* buffer = 0;\r
+ for(int buffer_length = 256; ; buffer_length*=2)\r
+ {\r
+ buffer = (char*)malloc(buffer_length);\r
+ if (!buffer) return -1;\r
+ length = _vsnprintf(buffer, buffer_length-1, format, args);\r
+ if (length >= 0)\r
+ {\r
+ buffer[length] = 0;\r
+ formatted += std::string(buffer);\r
+ free(buffer);\r
+ break;\r
+ }\r
+ free(buffer);\r
+ }\r
+ return length;\r
+#else\r
+ char* buffer = 0;\r
+ int length = vasprintf(&buffer, format, args);\r
+ if (!buffer) return -1;\r
+ if (length >= 0)\r
+ formatted += std::string(buffer);\r
+ free(buffer);\r
+ return length;\r
+#endif\r
+ }\r
+\r
+ int dprintf(std::string& formatted, const char* format, ...)\r
+ {\r
+ va_list args;\r
+ va_start(args, format);\r
+ int result = vdprintf(formatted, format, args);\r
+ va_end(args);\r
+ return result;\r
+ }\r
+\r
+ std::string vdformat(const char* format, va_list args) throw(std::invalid_argument)\r
+ {\r
+ std::string formatted;\r
+ int length = vdprintf(formatted, format, args);\r
+ if (length < 0) throw std::invalid_argument("dprintf");\r
+ return formatted;\r
+ }\r
+\r
+ std::string dformat(const char* format, ...) throw(std::invalid_argument)\r
+ {\r
+ std::string formatted;\r
+ va_list args;\r
+ va_start(args, format);\r
+ int length = vdprintf(formatted, format, args);\r
+ va_end(args);\r
+ if (length < 0) throw std::invalid_argument("dprintf");\r
+ return formatted;\r
+ }\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
-#ifndef STLPLUS_DPRINTF
-#define STLPLUS_DPRINTF
-////////////////////////////////////////////////////////////////////////////////
-
-// Author: Andy Rushton
-// Copyright: (c) Southampton University 1999-2004
-// (c) Andy Rushton 2004-2009
-// License: BSD License, see ../docs/license.html
-
-// Provides an sprintf-like function acting on STL strings. The 'd' in dprintf
-// stands for "dynamic" in that the string is a dynamic string whereas a char*
-// buffer would be static (in size that is, not static in C terms).
-
-// The obvious solution to the problem of in-memory formatted output is to use
-// sprintf(), but this is a potentially dangerous operation since it will quite
-// happily charge off the end of the string it is printing to and thereby
-// corrupt memory. This kind of buffer-overflow vulnerability is the source of
-// most security failures exploited by virus-writers. It means that sprintf
-// should *never* be used and should be made obsolete.
-
-// In any case, using arbitrary-sized fixed-length buffers is not part of any
-// quality-orientated design philosophy.
-
-// Most operating systems now have a safe version of sprintf, but this is
-// non-standard. The functions in this file are platform-independent interfaces
-// to the underlying safe implementation.
-
-// I would like to make this set of functions obsolete too, since I believe the
-// C runtime should be deprecated in favour of C++ runtime which uses dynamic
-// strings and can handle exceptions. However, there is as yet no C++
-// equivalent functionality to some of the string-handling available through
-// the printf-like functions, so it has to stay for now.
-
-// int dprintf (std::string& buffer, const char* format, ...);
-
-// Formats the message by appending to the std::string buffer according to
-// the formatting codes in the format string. The return int is the number
-// of characters generated by this call, i.e. the increase in the length of
-// the std::string.
-
-// int vdprintf (std::string& buffer, const char* format, va_list args);
-
-// As above, but using a pre-initialised va_args argument list. Useful for
-// nesting dprintf calls within variable argument functions.
-
-// std::string dformat (const char* format, ...);
-
-// Similar to dprintf() above, except the result is formatted into a new
-// std::string which is returned by the function. Very useful for inline
-// calls within an iostream expression.
-
-// e.g. cout << "Total: " << dformat("%6i",t) << endl;
-
-// std::string vdformat (const char* format, va_list);
-
-// As above, but using a pre-initialised va_args argument list. Useful for nesting
-// dformat calls within variable argument functions.
-
-// The format string supports the following format codes as in the C runtime library:
-
-// % [ flags ] [ field ] [ . precision ] [ modifier ] [ conversion ]
-
-// flags:
-// - - left justified
-// + - print sign for +ve numbers
-// ' ' - leading space where + sign would be
-// 0 - leading zeros to width of field
-// # - alternate format
-
-// field:
-// a numeric argument specifying the field width - default = 0
-// * means take the next va_arg as the field width - if negative then left justify
-
-// precision:
-// a numeric argument the meaning of which depends on the conversion -
-// - %s - max characters from a string - default = strlen()
-// - %e, %f - decimal places to be displayed - default = 6
-// - %g - significant digits to be displayed - default = 6
-// - all integer conversions - minimum digits to display - default = 0
-// * means take the next va_arg as the field width - if negative then left justify
-
-// modifier:
-// h - short or unsigned short
-// l - long or unsigned long
-// L - long double
-
-// conversions:
-// d, i - short/int/long as decimal
-// u - short/int/long as unsigned decimal
-// o - short/int/long as unsigned octal - # adds leading 0
-// x, X - short/int/long as unsigned hexadecimal - # adds leading 0x
-// c - char
-// s - char*
-// f - double/long double as fixed point
-// e, E - double/long double as floating point
-// g, G - double/long double as fixed point/floating point depending on value
-// p - void* as unsigned hexadecimal
-// % - literal %
-// n - int* as recipient of length of formatted string so far
-
-////////////////////////////////////////////////////////////////////////////////
-#include "portability_fixes.hpp"
-#include <string>
-#include <stdexcept>
-#include <stdarg.h>
-
-namespace stlplus
-{
-
- // format by appending to a string and return the increase in length
- // if there is an error, return a negative number and leave the string unchanged
- int dprintf (std::string& formatted, const char* format, ...);
- int vdprintf (std::string& formatted, const char* format, va_list args);
-
- // format into a new string and return the result
- // if there is an error, throw an exception
- std::string dformat (const char* format, ...) throw(std::invalid_argument);
- std::string vdformat (const char* format, va_list) throw(std::invalid_argument);
-
-} // end namespace stlplus
-
-#endif
+#ifndef STLPLUS_DPRINTF\r
+#define STLPLUS_DPRINTF\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+// Author: Andy Rushton\r
+// Copyright: (c) Southampton University 1999-2004\r
+// (c) Andy Rushton 2004 onwards\r
+// License: BSD License, see ../docs/license.html\r
+\r
+// Provides an sprintf-like function acting on STL strings. The 'd' in dprintf\r
+// stands for "dynamic" in that the string is a dynamic string whereas a char*\r
+// buffer would be static (in size that is, not static in C terms).\r
+\r
+// The obvious solution to the problem of in-memory formatted output is to use\r
+// sprintf(), but this is a potentially dangerous operation since it will quite\r
+// happily charge off the end of the string it is printing to and thereby\r
+// corrupt memory. This kind of buffer-overflow vulnerability is the source of\r
+// most security failures exploited by virus-writers. It means that sprintf\r
+// should *never* be used and should be made obsolete.\r
+\r
+// In any case, using arbitrary-sized fixed-length buffers is not part of any\r
+// quality-orientated design philosophy.\r
+\r
+// Most operating systems now have a safe version of sprintf, but this is\r
+// non-standard. The functions in this file are platform-independent interfaces\r
+// to the underlying safe implementation.\r
+\r
+// I would like to make this set of functions obsolete too, since I believe the\r
+// C runtime should be deprecated in favour of C++ runtime which uses dynamic\r
+// strings and can handle exceptions. However, there is as yet no C++\r
+// equivalent functionality to some of the string-handling available through\r
+// the printf-like functions, so it has to stay for now.\r
+\r
+// int dprintf (std::string& buffer, const char* format, ...);\r
+\r
+// Formats the message by appending to the std::string buffer according to\r
+// the formatting codes in the format string. The return int is the number\r
+// of characters generated by this call, i.e. the increase in the length of\r
+// the std::string.\r
+\r
+// int vdprintf (std::string& buffer, const char* format, va_list args);\r
+\r
+// As above, but using a pre-initialised va_args argument list. Useful for\r
+// nesting dprintf calls within variable argument functions.\r
+\r
+// std::string dformat (const char* format, ...);\r
+\r
+// Similar to dprintf() above, except the result is formatted into a new\r
+// std::string which is returned by the function. Very useful for inline\r
+// calls within an iostream expression.\r
+\r
+// e.g. cout << "Total: " << dformat("%6i",t) << endl;\r
+\r
+// std::string vdformat (const char* format, va_list);\r
+ \r
+// As above, but using a pre-initialised va_args argument list. Useful for nesting\r
+// dformat calls within variable argument functions.\r
+\r
+// The format string supports the following format codes as in the C runtime library:\r
+\r
+// % [ flags ] [ field ] [ . precision ] [ modifier ] [ conversion ]\r
+\r
+// flags:\r
+// - - left justified\r
+// + - print sign for +ve numbers\r
+// ' ' - leading space where + sign would be\r
+// 0 - leading zeros to width of field\r
+// # - alternate format\r
+\r
+// field:\r
+// a numeric argument specifying the field width - default = 0\r
+// * means take the next va_arg as the field width - if negative then left justify\r
+\r
+// precision:\r
+// a numeric argument the meaning of which depends on the conversion -\r
+// - %s - max characters from a string - default = strlen()\r
+// - %e, %f - decimal places to be displayed - default = 6\r
+// - %g - significant digits to be displayed - default = 6\r
+// - all integer conversions - minimum digits to display - default = 0\r
+// * means take the next va_arg as the field width - if negative then left justify\r
+\r
+// modifier:\r
+// h - short or unsigned short\r
+// l - long or unsigned long\r
+// L - long double\r
+\r
+// conversions:\r
+// d, i - short/int/long as decimal\r
+// u - short/int/long as unsigned decimal\r
+// o - short/int/long as unsigned octal - # adds leading 0\r
+// x, X - short/int/long as unsigned hexadecimal - # adds leading 0x\r
+// c - char\r
+// s - char*\r
+// f - double/long double as fixed point\r
+// e, E - double/long double as floating point\r
+// g, G - double/long double as fixed point/floating point depending on value\r
+// p - void* as unsigned hexadecimal\r
+// % - literal %\r
+// n - int* as recipient of length of formatted string so far\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "portability_fixes.hpp"\r
+#include <string>\r
+#include <stdexcept>\r
+#include <stdarg.h>\r
+\r
+namespace stlplus\r
+{\r
+\r
+ // format by appending to a string and return the increase in length\r
+ // if there is an error, return a negative number and leave the string unchanged\r
+ int dprintf (std::string& formatted, const char* format, ...);\r
+ int vdprintf (std::string& formatted, const char* format, va_list args);\r
+\r
+ // format into a new string and return the result\r
+ // if there is an error, throw an exception\r
+ std::string dformat (const char* format, ...) throw(std::invalid_argument);\r
+ std::string vdformat (const char* format, va_list) throw(std::invalid_argument);\r
+\r
+} // end namespace stlplus\r
+\r
+#endif\r
-////////////////////////////////////////////////////////////////////////////////
-
-// Author: Andy Rushton
-// Copyright: (c) Southampton University 1999-2004
-// (c) Andy Rushton 2004-2009
-// License: BSD License, see ../docs/license.html
-
-////////////////////////////////////////////////////////////////////////////////
-#include "dynaload.hpp"
-
-#ifdef MSWINDOWS
-#include <windows.h>
-#else
-#include <dlfcn.h>
-#endif
-
-////////////////////////////////////////////////////////////////////////////////
-
-#ifdef MSWINDOWS
-
-static std::string last_error(void)
-{
- // get the last error code - if none, return the empty string
- DWORD err = GetLastError();
- if (err == 0) return std::string();
- // get the system message for this error code
- char* message;
- FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
- 0,
- err,
- MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
- (LPTSTR)&message,
- 0,0);
- std::string result = message;
- LocalFree(message);
- // the error message is for some perverse reason newline terminated - remove this
- if (result[result.size()-1] == '\n')
- result.erase(result.end()-1);
- if (result[result.size()-1] == '\r')
- result.erase(result.end()-1);
- return result;
-}
-
-#else
-
-static std::string last_error(void)
-{
- return std::string(dlerror());
-}
-
-#endif
-
-////////////////////////////////////////////////////////////////////////////////
-
-namespace stlplus
-{
-
- ////////////////////////////////////////////////////////////////////////////////
- // library management
-
- // construct the object but do not load
- dynaload::dynaload(void) : m_handle(0)
- {
- }
-
- // construct and load
- dynaload::dynaload(const std::string& library) : m_handle(0)
- {
- load(library);
- }
-
- // destroy and unload if loaded
- dynaload::~dynaload(void)
- {
- unload();
- }
-
- // load the library - return success or fail
- bool dynaload::load(const std::string& library)
- {
-#ifdef MSWINDOWS
- m_handle = (void*)LoadLibrary(library.c_str());
-#elif defined(CYGWIN)
- m_handle = dlopen(library.c_str(),RTLD_NOW);
-#else
- std::string full_library = std::string("lib") + library + std::string(".so");
- m_handle = dlopen(full_library.c_str(),RTLD_NOW);
-#endif
- if (!m_handle)
- {
- m_error = load_error;
- m_text = last_error();
- }
- return loaded();
- }
-
- // unload the library if loaded
- bool dynaload::unload(void)
- {
- if (!loaded()) return false;
-#ifdef MSWINDOWS
- int status = FreeLibrary((HINSTANCE)m_handle) ? 0 : 1;
-#else
- int status = dlclose(m_handle);
-#endif
- if (status != 0)
- {
- m_error = unload_error;
- m_text = last_error();
- }
- return status == 0;
- }
-
- // test whether the library is loaded
- bool dynaload::loaded(void) const
- {
- return m_handle != 0;
- }
-
- ////////////////////////////////////////////////////////////////////////////
- // symbol management
-
- // test whether a function is exported by the library
- // does not set the error flag if fails
- bool dynaload::present(const std::string& name)
- {
- if (!loaded()) return false;
-#ifdef MSWINDOWS
- void* fn = (void*)GetProcAddress((HINSTANCE)m_handle,name.c_str());
-#else
- void* fn = dlsym(m_handle,name.c_str());
-#endif
- return fn != 0;
- }
-
- // get the function as a generic pointer
- void* dynaload::symbol(const std::string& name)
- {
- if (!loaded()) return 0;
-#ifdef MSWINDOWS
- void* fn = (void*)GetProcAddress((HINSTANCE)m_handle,name.c_str());
-#else
- void* fn = dlsym(m_handle,name.c_str());
-#endif
- if (!fn)
- {
- m_error = symbol_error;
- m_text = last_error();
- }
- return fn;
- }
-
- ////////////////////////////////////////////////////////////////////////////
- // error management
-
- // test whether there has been an error
- bool dynaload::error(void) const
- {
- return m_error != no_error;
- }
-
- // clear an error once it has been handled (or ignored)
- void dynaload::clear_error(void)
- {
- m_error = no_error;
- m_text = std::string();
- }
-
- // get the type of the error as indicated by the enum error_t
- dynaload::error_t dynaload::error_type(void) const
- {
- return m_error;
- }
-
- // get the text of the error as provided by the OS
- std::string dynaload::error_text(void) const
- {
- return m_text;
- }
-
-
-////////////////////////////////////////////////////////////////////////////////
-
-} // end namespace stlplus
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+// Author: Andy Rushton\r
+// Copyright: (c) Southampton University 1999-2004\r
+// (c) Andy Rushton 2004 onwards\r
+// License: BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "dynaload.hpp"\r
+\r
+#ifdef MSWINDOWS\r
+#include <windows.h>\r
+#else\r
+#include <dlfcn.h>\r
+#endif\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+#ifdef MSWINDOWS\r
+\r
+static std::string last_error(void)\r
+{\r
+ // get the last error code - if none, return the empty string\r
+ DWORD err = GetLastError();\r
+ if (err == 0) return std::string();\r
+ // get the system message for this error code\r
+ char* message;\r
+ FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,\r
+ 0,\r
+ err,\r
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),\r
+ (LPTSTR)&message,\r
+ 0,0);\r
+ std::string result = message;\r
+ LocalFree(message);\r
+ // the error message is for some perverse reason newline terminated - remove this\r
+ if (result[result.size()-1] == '\n')\r
+ result.erase(result.end()-1);\r
+ if (result[result.size()-1] == '\r')\r
+ result.erase(result.end()-1);\r
+ return result;\r
+}\r
+\r
+#else\r
+\r
+static std::string last_error(void)\r
+{\r
+ return std::string(dlerror());\r
+}\r
+\r
+#endif\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // library management\r
+\r
+ // construct the object but do not load\r
+ dynaload::dynaload(void) : m_handle(0) \r
+ {\r
+ }\r
+\r
+ // construct and load\r
+ dynaload::dynaload(const std::string& library) : m_handle(0)\r
+ {\r
+ load(library);\r
+ }\r
+\r
+ // destroy and unload if loaded\r
+ dynaload::~dynaload(void)\r
+ {\r
+ unload();\r
+ }\r
+\r
+ // load the library - return success or fail\r
+ bool dynaload::load(const std::string& library)\r
+ {\r
+#ifdef MSWINDOWS\r
+ m_handle = (void*)LoadLibraryA(library.c_str());\r
+#elif defined(CYGWIN)\r
+ m_handle = dlopen(library.c_str(),RTLD_NOW);\r
+#else\r
+ std::string full_library = std::string("lib") + library + std::string(".so");\r
+ m_handle = dlopen(full_library.c_str(),RTLD_NOW);\r
+#endif\r
+ if (!m_handle)\r
+ {\r
+ m_error = load_error;\r
+ m_text = last_error();\r
+ }\r
+ return loaded();\r
+ }\r
+\r
+ // unload the library if loaded\r
+ bool dynaload::unload(void)\r
+ {\r
+ if (!loaded()) return false;\r
+#ifdef MSWINDOWS\r
+ int status = FreeLibrary((HINSTANCE)m_handle) ? 0 : 1;\r
+#else\r
+ int status = dlclose(m_handle);\r
+#endif\r
+ if (status != 0)\r
+ {\r
+ m_error = unload_error;\r
+ m_text = last_error();\r
+ }\r
+ return status == 0;\r
+ }\r
+\r
+ // test whether the library is loaded\r
+ bool dynaload::loaded(void) const\r
+ {\r
+ return m_handle != 0;\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////\r
+ // symbol management\r
+\r
+ // test whether a function is exported by the library\r
+ // does not set the error flag if fails\r
+ bool dynaload::present(const std::string& name)\r
+ {\r
+ if (!loaded()) return false;\r
+#ifdef MSWINDOWS\r
+ void* fn = (void*)GetProcAddress((HINSTANCE)m_handle,name.c_str());\r
+#else\r
+ void* fn = dlsym(m_handle,name.c_str());\r
+#endif\r
+ return fn != 0;\r
+ }\r
+\r
+ // get the function as a generic pointer\r
+ void* dynaload::symbol(const std::string& name)\r
+ {\r
+ if (!loaded()) return 0;\r
+#ifdef MSWINDOWS\r
+ void* fn = (void*)GetProcAddress((HINSTANCE)m_handle,name.c_str());\r
+#else\r
+ void* fn = dlsym(m_handle,name.c_str());\r
+#endif\r
+ if (!fn)\r
+ {\r
+ m_error = symbol_error;\r
+ m_text = last_error();\r
+ }\r
+ return fn;\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////\r
+ // error management\r
+\r
+ // test whether there has been an error\r
+ bool dynaload::error(void) const\r
+ {\r
+ return m_error != no_error;\r
+ }\r
+\r
+ // clear an error once it has been handled (or ignored)\r
+ void dynaload::clear_error(void)\r
+ {\r
+ m_error = no_error;\r
+ m_text = std::string();\r
+ }\r
+\r
+ // get the type of the error as indicated by the enum error_t\r
+ dynaload::error_t dynaload::error_type(void) const\r
+ {\r
+ return m_error;\r
+ }\r
+\r
+ // get the text of the error as provided by the OS\r
+ std::string dynaload::error_text(void) const\r
+ {\r
+ return m_text;\r
+ }\r
+\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
-#ifndef STLPLUS_DYNALOAD
-#define STLPLUS_DYNALOAD
-////////////////////////////////////////////////////////////////////////////////
-
-// Author: Andy Rushton
-// Copyright: (c) Southampton University 1999-2004
-// (c) Andy Rushton 2004-2009
-// License: BSD License, see ../docs/license.html
-
-// A portable interface to the dynamic loader - i.e. the system for loading
-// dynamic libraries or shared libraries during the running of a program,
-// rather than by linking
-
-////////////////////////////////////////////////////////////////////////////////
-#include "portability_fixes.hpp"
-#include <string>
-
-namespace stlplus
-{
-
- //////////////////////////////////////////////////////////////////////////////
- // dynaload class manages a dynamic loadable library and unloads it on destruction
-
- class dynaload
- {
- public:
-
- ////////////////////////////////////////////////////////////////////////////
- // library management
-
- // construct the object but do not load
- dynaload(void);
-
- // construct and load
- dynaload(const std::string& library);
-
- // destroy and unload if loaded
- ~dynaload(void);
-
- // load the library - return success or fail
- bool load(const std::string& library);
-
- // unload the library if loaded
- bool unload(void);
-
- // test whether the library is loaded
- bool loaded(void) const;
-
- ////////////////////////////////////////////////////////////////////////////
- // symbol management
-
- // test whether a function is exported by the library
- bool present(const std::string& name);
-
- // get the function as a generic pointer
- void* symbol(const std::string& name);
-
- ////////////////////////////////////////////////////////////////////////////
- // error management
-
- // enum values to indicate type of error
- enum error_t {no_error, load_error, unload_error, symbol_error};
-
- // test whether there has been an error
- bool error(void) const;
-
- // clear an error once it has been handled (or ignored)
- void clear_error(void);
-
- // get the type of the error as indicated by the enum error_t
- error_t error_type(void) const;
-
- // get the text of the error as provided by the OS
- std::string error_text(void) const;
-
- ////////////////////////////////////////////////////////////////////////////
-
- private:
- void* m_handle;
- error_t m_error;
- std::string m_text;
- };
-
-}
-
-#endif
+#ifndef STLPLUS_DYNALOAD\r
+#define STLPLUS_DYNALOAD\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+// Author: Andy Rushton\r
+// Copyright: (c) Southampton University 1999-2004\r
+// (c) Andy Rushton 2004 onwards\r
+// License: BSD License, see ../docs/license.html\r
+\r
+// A portable interface to the dynamic loader - i.e. the system for loading\r
+// dynamic libraries or shared libraries during the running of a program,\r
+// rather than by linking\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "portability_fixes.hpp"\r
+#include <string>\r
+\r
+namespace stlplus\r
+{\r
+\r
+ //////////////////////////////////////////////////////////////////////////////\r
+ // dynaload class manages a dynamic loadable library and unloads it on destruction\r
+\r
+ class dynaload\r
+ {\r
+ public:\r
+\r
+ ////////////////////////////////////////////////////////////////////////////\r
+ // library management\r
+\r
+ // construct the object but do not load\r
+ dynaload(void);\r
+\r
+ // construct and load\r
+ dynaload(const std::string& library);\r
+\r
+ // destroy and unload if loaded\r
+ ~dynaload(void);\r
+\r
+ // load the library - return success or fail\r
+ bool load(const std::string& library);\r
+\r
+ // unload the library if loaded\r
+ bool unload(void);\r
+\r
+ // test whether the library is loaded\r
+ bool loaded(void) const;\r
+\r
+ ////////////////////////////////////////////////////////////////////////////\r
+ // symbol management\r
+\r
+ // test whether a function is exported by the library\r
+ bool present(const std::string& name);\r
+\r
+ // get the function as a generic pointer\r
+ void* symbol(const std::string& name);\r
+\r
+ ////////////////////////////////////////////////////////////////////////////\r
+ // error management\r
+\r
+ // enum values to indicate type of error\r
+ enum error_t {no_error, load_error, unload_error, symbol_error};\r
+\r
+ // test whether there has been an error\r
+ bool error(void) const;\r
+\r
+ // clear an error once it has been handled (or ignored)\r
+ void clear_error(void);\r
+\r
+ // get the type of the error as indicated by the enum error_t\r
+ error_t error_type(void) const;\r
+\r
+ // get the text of the error as provided by the OS\r
+ std::string error_text(void) const;\r
+\r
+ ////////////////////////////////////////////////////////////////////////////\r
+\r
+ private:\r
+ void* m_handle;\r
+ error_t m_error;\r
+ std::string m_text;\r
+ };\r
+\r
+}\r
+\r
+#endif\r
-////////////////////////////////////////////////////////////////////////////////
-
-// Author: Andy Rushton
-// Copyright: (c) Southampton University 1999-2004
-// (c) Andy Rushton 2004-2009
-// License: BSD License, see ../docs/license.html
-
-// This is a portable interface to the file system.
-
-// The idea is that you write all file system access code using these functions,
-// which are ported to all platforms that we are interested in. Therefore your
-// code is inherently portable.
-
-// Native Windows version: switched on by macro _WIN32 which is defined by VC++/Borland/Mingw compilers
-// Unix/Gnu version: default variant, no compiler directives are required but _WIN32 must be absent
-// Cygwin/Gnu version: as Unix version but with additional support for Windows drive letters
-
-////////////////////////////////////////////////////////////////////////////////
-#include "file_system.hpp"
-#include "wildcard.hpp"
-#include <stdio.h>
-#include <stdlib.h>
-#include <time.h>
-#include <algorithm>
-#include <ctype.h>
-
-#ifdef MSWINDOWS
-#include <windows.h>
-#include <dos.h>
-#include <direct.h>
-#include <fcntl.h>
-#include <io.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#else
-#include <dirent.h>
-#include <fcntl.h>
-#include <sys/param.h>
-#include <unistd.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#endif
-
-////////////////////////////////////////////////////////////////////////////////
-
-namespace stlplus
-{
-
-////////////////////////////////////////////////////////////////////////////////
-// definitions of separators
-
-#ifdef MSWINDOWS
- static const char* separator_set = "\\/";
- static const char preferred_separator = '\\';
-#else
- static const char* separator_set = "/";
- static const char preferred_separator = '/';
-#endif
-
- static bool is_separator (char ch)
- {
- for (int i = 0; separator_set[i]; i++)
- {
- if (separator_set[i] == ch)
- return true;
- }
- return false;
- }
-
-////////////////////////////////////////////////////////////////////////////////
-// implement string comparison of paths - Unix is case-sensitive, Windoze is case-insensitive
-
-#ifdef MSWINDOWS
-
- static std::string lowercase(const std::string& val)
- {
- std::string text = val;
- for (unsigned i = 0; i < text.size(); i++)
- text[i] = tolower(text[i]);
- return text;
- }
-
-#endif
-
- bool path_compare(const std::string& l, const std::string& r)
- {
-#ifdef MSWINDOWS
- return lowercase(l) == lowercase(r);
-#else
- return l == r;
-#endif
- }
-
-////////////////////////////////////////////////////////////////////////////////
-// Internal data structure used to hold the different parts of a filespec
-
- class file_specification
- {
- private:
- bool m_relative; // true = relative, false = absolute
- std::string m_drive; // drive - drive letter (e.g. "c:") or the path for an UNC (e.g. "\\somewhere")
- // empty if not known or on Unix
- std::vector<std::string> m_path; // the subdirectory path to follow from the drive
- std::string m_filename; // the filename
- public:
- file_specification(void) : m_relative(false) {}
- ~file_specification(void) {}
-
- bool initialise_folder(const std::string& spec);
- bool initialise_file(const std::string& spec);
- bool simplify(void);
- bool make_absolute(const std::string& root = folder_current_full());
- bool make_absolute(const file_specification& root);
- bool make_relative(const std::string& root = folder_current_full());
- bool make_relative(const file_specification& root);
- bool relative(void) const {return m_relative;}
- bool absolute(void) const {return !relative();}
- void set_relative(void) {m_relative = true;}
- void set_absolute(void) {m_relative = false;}
-
- const std::string& drive(void) const {return m_drive;}
- std::string& drive(void) {return m_drive;}
- void set_drive(const std::string& drive) {m_drive = drive;}
-
- const std::vector<std::string>& path(void) const {return m_path;}
- std::vector<std::string>& path(void) {return m_path;}
- void set_path(const std::vector<std::string>& path) {m_path = path;}
-
- void add_subpath(const std::string& subpath) {m_path.push_back(subpath);}
- unsigned subpath_size(void) const {return m_path.size();}
- const std::string& subpath_element(unsigned i) const {return m_path[i];}
- void subpath_erase(unsigned i) {m_path.erase(m_path.begin()+i);}
-
- const std::string& file(void) const {return m_filename;}
- std::string& file(void) {return m_filename;}
- void set_file(const std::string& file) {m_filename = file;}
-
- std::string image(void) const;
- };
-
- bool file_specification::initialise_folder(const std::string& folder_spec)
- {
- std::string spec = folder_spec;
- m_relative = true;
- m_drive.erase();
- m_path.clear();
- m_filename.erase();
- unsigned i = 0;
-#ifdef MSWINDOWS
- // first split off the drive letter or UNC prefix on Windows
- if (spec.size() >= 2 && isalpha(spec[0]) && spec[1] == ':')
- {
- // found a drive letter
- i = 2;
- m_drive = spec.substr(0, 2);
- m_relative = false;
- // if there is a drive but no path or a relative path, get the current
- // path for this drive and prepend it to the path
- if (i == spec.size() || !is_separator(spec[i]))
- {
- // getdcwd requires the drive number (1..26) not the letter (A..Z)
- char path [MAX_PATH+1];
- int drivenum = toupper(m_drive[0]) - 'A' + 1;
- if (_getdcwd(drivenum, path, MAX_PATH+1))
- {
- // the path includes the drive so we have the drive info twice
- // need to prepend this absolute path to the spec such that any remaining relative path is still retained
- if (!is_separator(path[strlen(path)-1])) spec.insert(2, 1, preferred_separator);
- spec.insert(2, path+2);
- }
- else
- {
- // non-existent drive - fill in just the root directory
- spec.insert(2, 1, preferred_separator);
- }
- }
- }
- else if (spec.size() >= 2 && is_separator(spec[0]) && is_separator(spec[1]))
- {
- // found an UNC prefix
- i = 2;
- // find the end of the prefix by scanning for the next seperator or the end of the spec
- while (i < spec.size() && !is_separator(spec[i])) i++;
- m_drive = spec.substr(0, i);
- m_relative = false;
- }
-#endif
-#ifdef CYGWIN
- // first split off the drive letter or UNC prefix on Windows - the Cygwin environment supports these too
- if (spec.size() >= 2 && isalpha(spec[0]) && spec[1] == ':')
- {
- // found a drive letter
- i = 2;
- m_drive = spec.substr(0, 2);
- m_relative = false;
- // if there is a drive but no path or a relative path, get the current
- // path for this drive and prepend it to the path
- if (i == spec.size() || !is_separator(spec[i]))
- {
- // non-existent drive - fill in just the root directory
- spec.insert(2, 1, preferred_separator);
- }
- }
- else if (spec.size() >= 2 && is_separator(spec[0]) && is_separator(spec[1]))
- {
- // found an UNC prefix
- i = 2;
- // find the end of the prefix by scanning for the next seperator or the end of the spec
- while (i < spec.size() && !is_separator(spec[i])) i++;
- m_drive = spec.substr(0, i);
- m_relative = false;
- }
-#endif
- // check whether the path is absolute or relative and discard the leading / if absolute
- if (i < spec.size() && is_separator(spec[i]))
- {
- m_relative = false;
- i++;
-#ifdef MSWINDOWS
- // if there's no drive, fill it in on Windows since absolute paths must have a drive
- if (m_drive.empty())
- {
- m_drive += (char)(_getdrive() - 1 + 'A');
- m_drive += ':';
- }
-#endif
- }
- // now extract the path elements - note that a trailing / is not significant since /a/b/c/ === /a/b/c
- // also note that the leading / has been discarded - all paths are relative
- // if absolute() is set, then paths are relative to the drive, else they are relative to the current path
- unsigned start = i;
- while(i <= spec.size())
- {
- if (i == spec.size())
- {
- // path element terminated by the end of the string
- // discard this element if it is zero length because that represents the trailing /
- if (i != start)
- m_path.push_back(spec.substr(start, i-start));
- }
- else if (is_separator(spec[i]))
- {
- // path element terminated by a separator
- m_path.push_back(spec.substr(start, i-start));
- start = i+1;
- }
- i++;
- }
- // TODO - some error handling?
- return true;
- }
-
- bool file_specification::initialise_file(const std::string& spec)
- {
- m_filename.erase();
- // remove last element as the file and then treat the rest as a folder
- unsigned i = spec.size();
- while (--i)
- {
- if (is_separator(spec[i]))
- break;
-#ifdef MSWINDOWS
- // on windoze you can say a:fred.txt so the colon separates the path from the filename
- else if (i == 1 && spec[i] == ':')
- break;
-#endif
- }
- bool result = initialise_folder(spec.substr(0,i+1));
- m_filename = spec.substr(i+1,spec.size()-i-1);
- // TODO - some error handling?
- return result;
- }
-
- bool file_specification::simplify(void)
- {
- // simplify the path by removing unnecessary . and .. entries - Note that zero-length entries are treated like .
- for (unsigned i = 0; i < m_path.size(); )
- {
- if (m_path[i].empty() || m_path[i].compare(".") == 0)
- {
- // found . or null
- // these both mean do nothing - so simply delete this element
- m_path.erase(m_path.begin()+i);
- }
- else if (m_path[i].compare("..") == 0)
- {
- // found ..
- if (i == 0 && !m_relative)
- {
- // up from the root does nothing so can be deleted
- m_path.erase(m_path.begin()+i);
- i++;
- }
- else if (i == 0 || m_path[i-1].compare("..") == 0)
- {
- // the first element of a relative path or the previous element is .. then keep it
- i++;
- }
- else
- {
- // otherwise delete this element and the previous one
- m_path.erase(m_path.begin()+i);
- m_path.erase(m_path.begin()+i-1);
- i--;
- }
- }
- // keep all other elements
- else
- i++;
- }
- // TODO - error checking?
- return true;
- }
-
- bool file_specification::make_absolute(const std::string& root)
- {
- // test whether already an absolute path in which case there's nothing to do
- if (absolute()) return true;
- // now simply call the other version of make_absolute
- file_specification rootspec;
- rootspec.initialise_folder(root);
- return make_absolute(rootspec);
- }
-
- bool file_specification::make_absolute(const file_specification& rootspec)
- {
- // test whether already an absolute path in which case there's nothing to do
- if (absolute()) return true;
- // initialise the result with the root and make the root absolute
- file_specification result = rootspec;
- result.make_absolute();
- // now append this's relative path and filename to the root's absolute path
- for (unsigned i = 0; i < subpath_size(); i++)
- result.add_subpath(subpath_element(i));
- result.set_file(file());
- // now the result is the absolute path, so transfer it to this
- *this = result;
- // and simplify to get rid of any unwanted .. or . elements
- simplify();
- return true;
- }
-
- bool file_specification::make_relative(const std::string& root)
- {
- // test whether already an relative path in which case there's nothing to do
- if (relative()) return true;
- // now simply call the other version of make_relative
- file_specification rootspec;
- rootspec.initialise_folder(root);
- return make_relative(rootspec);
- }
-
- bool file_specification::make_relative(const file_specification& rootspec)
- {
- // test whether already an relative path in which case there's nothing to do
- if (relative()) return true;
- // initialise the result with the root and make the root absolute
- file_specification absolute_root = rootspec;
- absolute_root.make_absolute();
- // now compare elements of the absolute root with elements of this to find the common path
- // if the drives are different, no conversion can take place and the result must be absolute, else clear the drive
- if (!path_compare(drive(), absolute_root.drive())) return true;
- set_drive("");
- // first remove leading elements that are identical to the corresponding element in root
- unsigned i = 0;
- while(subpath_size() > 0 &&
- i < absolute_root.subpath_size() &&
- path_compare(subpath_element(0), absolute_root.subpath_element(i)))
- {
- subpath_erase(0);
- i++;
- }
- // now add a .. prefix for every element in root that is different from this
- while (i < absolute_root.subpath_size())
- {
- m_path.insert(m_path.begin(), "..");
- i++;
- }
- set_relative();
- return true;
- }
-
- std::string file_specification::image(void) const
- {
- std::string result = m_drive;
- if (absolute())
- result += preferred_separator;
- if (!m_path.empty())
- {
- for (unsigned i = 0; i < m_path.size(); i++)
- {
- if (i != 0) result += std::string(1,preferred_separator);
- result += m_path[i];
- }
- }
- else if (relative())
- result += '.';
- // add a trailing / to the last directory element
- if (result.empty() || !is_separator(result[result.size()-1]))
- result += preferred_separator;
- if (!m_filename.empty())
- result += m_filename;
- return result;
- }
-
-////////////////////////////////////////////////////////////////////////////////
-// classifying functions
-
-#ifdef MSWINDOWS
-// file type tests are not defined for some reason on Windows despite them providing the stat() function!
-#define R_OK 4
-#define W_OK 2
-#endif
-
- bool is_present (const std::string& thing)
- {
- // strip off any trailing separator because that will cause the stat function to fail
- std::string path = thing;
- if (!path.empty() && is_separator(path[path.size()-1]))
- path.erase(path.size()-1,1);
- // now test if this thing exists using the built-in stat function
- struct stat buf;
- return stat(path.c_str(), &buf) == 0;
- }
-
- bool is_folder (const std::string& thing)
- {
- // strip off any trailing separator because that will cause the stat function to fail
- std::string path = thing;
- if (!path.empty() && is_separator(path[path.size()-1]))
- path.erase(path.size()-1,1);
- // now test if this thing exists using the built-in stat function and if so, is it a folder
- struct stat buf;
- if (!(stat(path.c_str(), &buf) == 0)) {return false;}
- return (buf.st_mode & S_IFDIR) != 0;
- }
-
- bool is_file (const std::string& thing)
- {
- // strip off any trailing separator because that will cause the stat function to fail
- std::string path = thing;
- if (!path.empty() && is_separator(path[path.size()-1]))
- path.erase(path.size()-1,1);
- // now test if this thing exists using the built-in stat function and if so, is it a file
- struct stat buf;
- if (!(stat(path.c_str(), &buf) == 0)) {return false;}
- return (buf.st_mode & S_IFREG) != 0;
- }
-
-////////////////////////////////////////////////////////////////////////////////
-// file functions
-
- bool file_exists (const std::string& filespec)
- {
- return is_file(filespec);
- }
-
- bool file_readable (const std::string& filespec)
- {
- // a file is readable if it exists and can be read
- if (!file_exists(filespec)) return false;
- return access(filespec.c_str(),R_OK)==0;
- }
-
- bool file_writable (const std::string& filespec)
- {
- // a file is writable if it exists as a file and is writable or if it doesn't exist but could be created and would be writable
- if (is_present(filespec))
- {
- if (!is_file(filespec)) return false;
- return access(filespec.c_str(),W_OK)==0;
- }
- std::string dir = folder_part(filespec);
- if (dir.empty()) dir = ".";
- return folder_writable(dir);
- }
-
- size_t file_size (const std::string& filespec)
- {
- struct stat buf;
- if (!(stat(filespec.c_str(), &buf) == 0)) return 0;
- return buf.st_size;
- }
-
- bool file_delete (const std::string& filespec)
- {
- if (!is_file(filespec)) return false;
- return remove(filespec.c_str())==0;
- }
-
- bool file_rename (const std::string& old_filespec, const std::string& new_filespec)
- {
- if (!is_file(old_filespec)) return false;
- return rename(old_filespec.c_str(), new_filespec.c_str())==0;
- }
-
- bool file_copy (const std::string& old_filespec, const std::string& new_filespec)
- {
- if (!is_file(old_filespec)) return false;
- // do an exact copy - to do this, use binary mode
- bool result = true;
- FILE* old_file = fopen(old_filespec.c_str(),"rb");
- FILE* new_file = fopen(new_filespec.c_str(),"wb");
- if (!old_file)
- result = false;
- else if (!new_file)
- result = false;
- else
- {
- for (int byte = getc(old_file); byte != EOF; byte = getc(old_file))
- putc(byte,new_file);
- }
- if (old_file) fclose(old_file);
- if (new_file) fclose(new_file);
- return result;
- }
-
- bool file_move (const std::string& old_filespec, const std::string& new_filespec)
- {
- // try to move the file by renaming - if that fails then do a copy and delete the original
- if (file_rename(old_filespec, new_filespec))
- return true;
- if (!file_copy(old_filespec, new_filespec))
- return false;
- // I'm not sure what to do if the delete fails - is that an error?
- // I've made it an error and then delete the copy so that the original state is recovered
- if (file_delete(old_filespec))
- return true;
- file_delete(new_filespec);
- return false;
- }
-
- time_t file_created (const std::string& filespec)
- {
- struct stat buf;
- if (!(stat(filespec.c_str(), &buf) == 0)) return 0;
- return buf.st_ctime;
- }
-
- time_t file_modified (const std::string& filespec)
- {
- struct stat buf;
- if (!(stat(filespec.c_str(), &buf) == 0)) return 0;
- return buf.st_mtime;
- }
-
- time_t file_accessed (const std::string& filespec)
- {
- struct stat buf;
- if (!(stat(filespec.c_str(), &buf) == 0)) return 0;
- return buf.st_atime;
- }
-
- std::string create_filespec (const std::string& directory, const std::string& filename)
- {
- std::string result = directory;
- // if directory is empty then no directory part will be added
- // add trailing slash if the directory was specified and does not have a trailing slash
- if (!result.empty() && !is_separator(result[result.size()-1]))
- result += preferred_separator;
- // if filename is null or empty, nothing will be added so the path is then a directory path
- result += filename;
- return result;
- }
-
- std::string create_filespec (const std::string& directory, const std::string& basename, const std::string& extension)
- {
- return create_filespec(directory, create_filename(basename, extension));
- }
-
- std::string create_filename(const std::string& basename, const std::string& extension)
- {
- std::string name = basename;
- // extension is optional - so the dot is also optional
- if (!extension.empty())
- {
- if (extension[0] != '.') name += '.';
- name += extension;
- }
- return name;
- }
-
-////////////////////////////////////////////////////////////////////////////////
-// folder functions
-
- bool folder_create (const std::string& directory)
- {
-#ifdef MSWINDOWS
- return mkdir(directory.c_str()) == 0;
-#else
- return mkdir(directory.c_str(), 0777) == 0;
-#endif
- }
-
- bool folder_exists (const std::string& directory)
- {
- return is_folder(directory);
- }
-
- bool folder_readable (const std::string& directory)
- {
- // a folder is readable if it exists and has read access
- std::string dir = directory;
- if (dir.empty()) dir = ".";
- if (!folder_exists(dir)) return false;
- return access(dir.c_str(),R_OK)==0;
- }
-
- bool folder_writable (const std::string& directory)
- {
- // a folder is writable if it exists and has write access
- std::string dir = directory;
- if (dir.empty()) dir = ".";
- if (!folder_exists(dir)) return false;
- return access(dir.c_str(),W_OK)==0;
- }
-
- bool folder_delete (const std::string& directory, bool recurse)
- {
- std::string dir = directory;
- if (dir.empty()) dir = ".";
- if (!folder_exists(dir)) return false;
- bool result = true;
- // depth-first traversal ensures that directory contents are deleted before trying to delete the directory itself
- if (recurse)
- {
- std::vector<std::string> subdirectories = folder_subdirectories(dir);
- for (std::vector<std::string>::size_type d = 0; d < subdirectories.size(); ++d)
- if (!folder_delete(folder_down(dir,subdirectories[d]),true))
- result = false;
- std::vector<std::string> files = folder_files(dir);
- for (std::vector<std::string>::size_type f = 0; f < files.size(); ++f)
- if (!file_delete(create_filespec(dir, files[f])))
- result = false;
- }
- if (rmdir(dir.c_str())!=0) result = false;
- return result;
- }
-
- bool folder_rename (const std::string& old_directory, const std::string& new_directory)
- {
- if (!folder_exists(old_directory)) return false;
- return rename(old_directory.c_str(), new_directory.c_str())==0;
- }
-
- bool folder_empty(const std::string& directory)
- {
- std::string dir = directory.empty() ? std::string(".") : directory;
- bool result = true;
-#ifdef MSWINDOWS
- std::string wildcard = create_filespec(dir, "*.*");
- long handle = -1;
- _finddata_t fileinfo;
- for (bool OK = (handle = _findfirst((char*)wildcard.c_str(), &fileinfo)) != -1; OK; OK = (_findnext(handle, &fileinfo)==0))
- {
- std::string strentry = fileinfo.name;
- if (strentry.compare(".")!=0 && strentry.compare("..")!=0)
- {
- result = false;
- break;
- }
- }
- _findclose(handle);
-#else
- DIR* d = opendir(dir.c_str());
- if (d)
- {
- for (dirent* entry = readdir(d); entry; entry = readdir(d))
- {
- std::string strentry = entry->d_name;
- if (strentry.compare(".")!=0 && strentry.compare("..")!=0)
- {
- result = false;
- break;
- }
- }
- closedir(d);
- }
-#endif
- return result;
- }
-
- bool folder_set_current(const std::string& folder)
- {
- if (!folder_exists(folder))
- return false;
-#ifdef MSWINDOWS
- // Windose implementation - this returns non-zero for success
- return (SetCurrentDirectoryA(folder.c_str()) != 0);
-#else
- // Unix implementation - this returns zero for success
- return (chdir(folder.c_str()) == 0);
-#endif
- }
-
- std::string folder_current (void)
- {
- return ".";
- }
-
- std::string folder_current_full(void)
- {
- // It's not clear from the documentation whether the buffer for a path should be one byte longer
- // than the maximum path length to allow for the null termination, so I have made it so anyway
-#ifdef MSWINDOWS
- char abspath [MAX_PATH+1];
- return std::string(_fullpath(abspath, ".", MAX_PATH+1));
-#else
- char pathname [MAXPATHLEN+1];
- getcwd(pathname,MAXPATHLEN+1);
- return std::string(pathname);
-#endif
- }
-
- std::string folder_down (const std::string& directory, const std::string& subdirectory)
- {
- file_specification spec;
- spec.initialise_folder(directory);
- spec.add_subpath(subdirectory);
- return spec.image();
- }
-
- std::string folder_up (const std::string& directory, unsigned levels)
- {
- file_specification spec;
- spec.initialise_folder(directory);
- for (unsigned i = 0; i < levels; i++)
- spec.add_subpath("..");
- spec.simplify();
- return spec.image();
- }
-
- std::vector<std::string> folder_subdirectories (const std::string& directory)
- {
- return folder_wildcard(directory, "*", true, false);
- }
-
- std::vector<std::string> folder_files (const std::string& directory)
- {
- return folder_wildcard(directory, "*", false, true);
- }
-
- std::vector<std::string> folder_all(const std::string& directory)
- {
- return folder_wildcard(directory, "*", true, true);
- }
-
- std::vector<std::string> folder_wildcard (const std::string& directory, const std::string& wild, bool subdirs, bool files)
- {
- std::string dir = directory.empty() ? std::string(".") : directory;
- std::vector<std::string> results;
-#ifdef MSWINDOWS
- std::string wildcard = create_filespec(dir, wild);
- long handle = -1;
- _finddata_t fileinfo;
- for (bool OK = (handle = _findfirst((char*)wildcard.c_str(), &fileinfo)) != -1; OK; OK = (_findnext(handle, &fileinfo)==0))
- {
- std::string strentry = fileinfo.name;
- if (strentry.compare(".")!=0 && strentry.compare("..")!=0)
- if ((subdirs && (fileinfo.attrib & _A_SUBDIR)) || (files && !(fileinfo.attrib & _A_SUBDIR)))
- results.push_back(strentry);
- }
- _findclose(handle);
-#else
- DIR* d = opendir(dir.c_str());
- if (d)
- {
- for (dirent* entry = readdir(d); entry; entry = readdir(d))
- {
- std::string strentry = entry->d_name;
- if (strentry.compare(".")!=0 && strentry.compare("..")!=0)
- {
- std::string subpath = create_filespec(dir, strentry);
- if (((subdirs && is_folder(subpath)) || (files && is_file(subpath))) && (wildcard(wild, strentry)))
- results.push_back(strentry);
- }
- }
- closedir(d);
- }
-#endif
- return results;
- }
-
- std::string folder_home (void)
- {
- if (getenv("HOME"))
- return std::string(getenv("HOME"));
-#ifdef MSWINDOWS
- if (getenv("HOMEDRIVE") || getenv("HOMEPATH"))
- return std::string(getenv("HOMEDRIVE")) + std::string(getenv("HOMEPATH"));
- return "C:\\";
-#else
- if (getenv("USER"))
- return folder_down("/home", std::string(getenv("USER")));
- if (getenv("USERNAME"))
- return folder_down("/home", std::string(getenv("USERNAME")));
- return "";
-#endif
- }
-
-////////////////////////////////////////////////////////////////////////////////
-// path functions convert between full and relative paths
-
- bool is_full_path(const std::string& path)
- {
- file_specification spec;
- spec.initialise_folder(path.empty() ? std::string(".") : path);
- return spec.absolute();
- }
-
- bool is_relative_path(const std::string& path)
- {
- file_specification spec;
- spec.initialise_folder(path.empty() ? std::string(".") : path);
- return spec.relative();
- }
-
- static std::string full_path(const std::string& root, const std::string& path)
- {
- // convert path to a full path using root as the start point for relative paths
- // decompose the path and test whether it is already an absolute path, in which case just return it
- file_specification spec;
- spec.initialise_folder(path.empty() ? std::string(".") : path);
- if (spec.absolute()) return spec.image();
- // okay, so the path is relative after all, so we need to combine it with the root path
- // decompose the root path and check whether it is relative
- file_specification rootspec;
- rootspec.initialise_folder(root.empty() ? std::string(".") : root);
- if (rootspec.relative())
- rootspec.make_absolute();
- // Now do the conversion of the path relative to the root
- spec.make_absolute(rootspec);
- return spec.image();
- }
-
- static std::string relative_path(const std::string& root, const std::string& path)
- {
- // convert path to a relative path, using the root path as its starting point
- // first convert both paths to full paths relative to CWD
- file_specification rootspec;
- rootspec.initialise_folder(root.empty() ? std::string(".") : root);
- if (rootspec.relative())
- rootspec.make_absolute();
- file_specification spec;
- spec.initialise_folder(path.empty() ? std::string(".") : path);
- if (spec.relative())
- spec.make_absolute();
- // now make path spec relative to the root spec
- spec.make_relative(rootspec);
- return spec.image();
- }
-
- std::string folder_to_path (const std::string& path, const std::string& directory)
- {
- return full_path(path, directory);
- }
-
- std::string filespec_to_path (const std::string& path, const std::string& spec)
- {
- return create_filespec(folder_to_path(path, folder_part(spec)),filename_part(spec));
- }
-
- std::string folder_to_path(const std::string& folder)
- {
- return folder_to_path(folder_current(), folder);
- }
-
- std::string filespec_to_path(const std::string& filespec)
- {
- return filespec_to_path(folder_current(), filespec);
- }
-
- std::string folder_to_relative_path(const std::string& root, const std::string& folder)
- {
- return relative_path(root, folder);
- }
-
- std::string filespec_to_relative_path(const std::string& root, const std::string& spec)
- {
- return create_filespec(folder_to_relative_path(root, folder_part(spec)),filename_part(spec));
- }
-
- std::string folder_to_relative_path(const std::string& folder)
- {
- return folder_to_relative_path(folder_current(), folder);
- }
-
- std::string filespec_to_relative_path(const std::string& filespec)
- {
- return filespec_to_relative_path(folder_current(), filespec);
- }
-
- std::string folder_append_separator(const std::string& folder)
- {
- std::string result = folder;
- if (result.empty() || !is_separator(result[result.size()-1]))
- result += preferred_separator;
- return result;
- }
-
-////////////////////////////////////////////////////////////////////////////////
-
- std::string basename_part (const std::string& spec)
- {
- std::string fname = filename_part(spec);
- // scan back through filename until a '.' is found and remove suffix
- // the whole filename is the basename if there is no '.'
- std::string::size_type i = fname.find_last_of('.');
- // observe Unix convention that a dot at the start of a filename is part of the basename, not the extension
- if (i != 0 && i != std::string::npos)
- fname.erase(i, fname.size()-i);
- return fname;
- }
-
- std::string filename_part (const std::string& spec)
- {
- // scan back through filename until a preferred_separator is found and remove prefix;
- // if there is no preferred_separator then remove nothing, i.e. the whole filespec is filename
- unsigned i = spec.size();
- while (i--)
- {
- if (is_separator(spec[i]))
- return spec.substr(i+1,spec.size()-i-1);
- }
- return spec;
- }
-
- std::string extension_part (const std::string& spec)
- {
- std::string fname = filename_part(spec);
- // scan back through filename until a '.' is found and remove prefix;
- std::string::size_type i = fname.find_last_of('.');
- // observe Unix convention that a dot at the start of a filename is part of the name, not the extension;
- if (i != 0 && i != std::string::npos)
- fname.erase(0, i+1);
- else
- fname.erase();
- return fname;
- }
-
- std::string folder_part (const std::string& spec)
- {
- // scan back through filename until a separator is found and remove prefix
- // if there is no separator, remove the whole
- unsigned i = spec.size();
- while (i--)
- {
- if (is_separator(spec[i]))
- return spec.substr(0,i);
- }
- return std::string();
- }
-
- std::vector<std::string> filespec_elements(const std::string& filespec)
- {
- file_specification spec;
- spec.initialise_file(filespec);
- std::vector<std::string> result = spec.path();
- if (!spec.drive().empty()) result.insert(result.begin(),spec.drive());
- if (!spec.file().empty()) result.push_back(spec.file());
- return result;
- }
-
- std::vector<std::string> folder_elements(const std::string& folder)
- {
- file_specification spec;
- spec.initialise_folder(folder);
- std::vector<std::string> result = spec.path();
- if (!spec.drive().empty()) result.insert(result.begin(),spec.drive());
- return result;
- }
-
-////////////////////////////////////////////////////////////////////////////////
-// mimic the command lookup used by the shell
-
-// Windows looks at the following locations:
-// 1) application root
-// 2) current directory
-// 3) 32-bit system directory
-// 4) 16-bit system directory
-// 5) windows system directory
-// 6) %path%
-// currently only (2) and (6) has been implemented although many system folders are on the path anyway
-// also implement the implied .exe extension on commands with no path (see CreateProcess documentation)
-// TODO - PATHEXT handling to find non-exe executables
-
- std::string path_lookup (const std::string& command)
- {
- std::string path = std::string(".") + PATH_SPLITTER + getenv("PATH");
- return lookup(command, path);
- }
-
- std::string lookup (const std::string& command, const std::string& path, const std::string& splitter)
- {
- // first check whether the command is already a path and check whether it exists
- if (!folder_part(command).empty())
- {
- if (file_exists(command))
- return command;
- }
- else
- {
- // command is just a name - so do path lookup
- // split the path into its elements
- std::vector<std::string> paths;
- if (!path.empty())
- {
- for(std::string::size_type offset = 0;;)
- {
- std::string::size_type found = path.find(splitter, offset);
- if (found != std::string::npos)
- {
- paths.push_back(path.substr(offset, found-offset));
- offset = found + splitter.size();
- }
- else
- {
- paths.push_back(path.substr(offset, path.size()-offset));
- break;
- }
- }
- }
- // now lookup each path to see if it its the matching one
- for (unsigned i = 0; i < paths.size(); i++)
- {
- std::string spec = create_filespec(paths[i], command);
- if (file_exists(spec))
- {
- return spec;
- }
- }
- }
-#ifdef MSWINDOWS
- // if there is no extension, try recursing on each possible extension
- // TODO iterate through PATHEXT
- if (extension_part(command).empty())
- return lookup(create_filespec(folder_part(command), basename_part(command), "exe"), path, splitter);
-#endif
- // if path lookup failed, return empty string to indicate error
- return std::string();
- }
-
-////////////////////////////////////////////////////////////////////////////////
-
- std::string install_path(const std::string& argv0)
- {
- std::string bin_directory = folder_part(argv0);
- if (bin_directory.empty())
- {
- // do path lookup to find the executable path
- bin_directory = folder_part(path_lookup(argv0));
- }
- return bin_directory;
- }
-
-////////////////////////////////////////////////////////////////////////////////
-
-} // end namespace stlplus
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+// Author: Andy Rushton\r
+// Copyright: (c) Southampton University 1999-2004\r
+// (c) Andy Rushton 2004 onwards\r
+// License: BSD License, see ../docs/license.html\r
+\r
+// This is a portable interface to the file system.\r
+\r
+// The idea is that you write all file system access code using these functions,\r
+// which are ported to all platforms that we are interested in. Therefore your\r
+// code is inherently portable.\r
+\r
+// Native Windows version: switched on by macro _WIN32 which is defined by VC++/Borland/Mingw compilers\r
+// Unix/Gnu version: default variant, no compiler directives are required but _WIN32 must be absent\r
+// Cygwin/Gnu version: as Unix version but with additional support for Windows drive letters\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "file_system.hpp"\r
+#include "wildcard.hpp"\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <time.h>\r
+#include <algorithm>\r
+#include <ctype.h>\r
+\r
+#ifdef MSWINDOWS\r
+#include <windows.h>\r
+#include <dos.h>\r
+#include <direct.h>\r
+#include <fcntl.h>\r
+#include <io.h>\r
+#include <sys/types.h>\r
+#include <sys/stat.h>\r
+#else\r
+#include <dirent.h>\r
+#include <fcntl.h>\r
+#include <sys/param.h>\r
+#include <unistd.h>\r
+#include <sys/types.h>\r
+#include <sys/stat.h>\r
+#endif\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// definitions of separators\r
+\r
+#ifdef MSWINDOWS\r
+ static const char* separator_set = "\\/";\r
+ static const char preferred_separator = '\\';\r
+#else\r
+ static const char* separator_set = "/";\r
+ static const char preferred_separator = '/';\r
+#endif\r
+\r
+ static bool is_separator (char ch)\r
+ {\r
+ for (int i = 0; separator_set[i]; i++)\r
+ {\r
+ if (separator_set[i] == ch)\r
+ return true;\r
+ }\r
+ return false;\r
+ }\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// implement string comparison of paths - Unix is case-sensitive, Windoze is case-insensitive\r
+\r
+#ifdef MSWINDOWS\r
+\r
+ static std::string lowercase(const std::string& val)\r
+ {\r
+ std::string text = val;\r
+ for (unsigned i = 0; i < text.size(); i++)\r
+ text[i] = tolower(text[i]);\r
+ return text;\r
+ }\r
+\r
+#endif\r
+\r
+ bool path_compare(const std::string& l, const std::string& r)\r
+ {\r
+#ifdef MSWINDOWS\r
+ return lowercase(l) == lowercase(r);\r
+#else\r
+ return l == r;\r
+#endif\r
+ }\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// Internal data structure used to hold the different parts of a filespec\r
+\r
+ class file_specification\r
+ {\r
+ private:\r
+ bool m_relative; // true = relative, false = absolute\r
+ std::string m_drive; // drive - drive letter (e.g. "c:") or the path for an UNC (e.g. "\\somewhere")\r
+ // empty if not known or on Unix\r
+ std::vector<std::string> m_path; // the subdirectory path to follow from the drive\r
+ std::string m_filename; // the filename\r
+ public:\r
+ file_specification(void) : m_relative(false) {}\r
+ ~file_specification(void) {}\r
+\r
+ bool initialise_folder(const std::string& spec);\r
+ bool initialise_file(const std::string& spec);\r
+ bool simplify(void);\r
+ bool make_absolute(const std::string& root = folder_current_full());\r
+ bool make_absolute(const file_specification& root);\r
+ bool make_relative(const std::string& root = folder_current_full());\r
+ bool make_relative(const file_specification& root);\r
+ bool relative(void) const {return m_relative;}\r
+ bool absolute(void) const {return !relative();}\r
+ void set_relative(void) {m_relative = true;}\r
+ void set_absolute(void) {m_relative = false;}\r
+\r
+ const std::string& drive(void) const {return m_drive;}\r
+ std::string& drive(void) {return m_drive;}\r
+ void set_drive(const std::string& drive) {m_drive = drive;}\r
+\r
+ const std::vector<std::string>& path(void) const {return m_path;}\r
+ std::vector<std::string>& path(void) {return m_path;}\r
+ void set_path(const std::vector<std::string>& path) {m_path = path;}\r
+\r
+ void add_subpath(const std::string& subpath) {m_path.push_back(subpath);}\r
+ unsigned subpath_size(void) const {return m_path.size();}\r
+ const std::string& subpath_element(unsigned i) const {return m_path[i];}\r
+ void subpath_erase(unsigned i) {m_path.erase(m_path.begin()+i);}\r
+\r
+ const std::string& file(void) const {return m_filename;}\r
+ std::string& file(void) {return m_filename;}\r
+ void set_file(const std::string& file) {m_filename = file;}\r
+\r
+ std::string image(void) const;\r
+ };\r
+\r
+ bool file_specification::initialise_folder(const std::string& folder_spec)\r
+ {\r
+ std::string spec = folder_spec;\r
+ m_relative = true;\r
+ m_drive.erase();\r
+ m_path.clear();\r
+ m_filename.erase();\r
+ unsigned i = 0;\r
+#ifdef MSWINDOWS\r
+ // first split off the drive letter or UNC prefix on Windows\r
+ if (spec.size() >= 2 && isalpha(spec[0]) && spec[1] == ':')\r
+ {\r
+ // found a drive letter\r
+ i = 2;\r
+ m_drive = spec.substr(0, 2);\r
+ m_relative = false;\r
+ // if there is a drive but no path or a relative path, get the current\r
+ // path for this drive and prepend it to the path\r
+ if (i == spec.size() || !is_separator(spec[i]))\r
+ {\r
+ // getdcwd requires the drive number (1..26) not the letter (A..Z)\r
+ char path [MAX_PATH+1];\r
+ int drivenum = toupper(m_drive[0]) - 'A' + 1;\r
+ if (_getdcwd(drivenum, path, MAX_PATH+1))\r
+ {\r
+ // the path includes the drive so we have the drive info twice\r
+ // need to prepend this absolute path to the spec such that any remaining relative path is still retained\r
+ if (!is_separator(path[strlen(path)-1])) spec.insert(2, 1, preferred_separator);\r
+ spec.insert(2, path+2);\r
+ }\r
+ else\r
+ {\r
+ // non-existent drive - fill in just the root directory\r
+ spec.insert(2, 1, preferred_separator);\r
+ }\r
+ }\r
+ }\r
+ else if (spec.size() >= 2 && is_separator(spec[0]) && is_separator(spec[1]))\r
+ {\r
+ // found an UNC prefix\r
+ i = 2;\r
+ // find the end of the prefix by scanning for the next seperator or the end of the spec\r
+ while (i < spec.size() && !is_separator(spec[i])) i++;\r
+ m_drive = spec.substr(0, i);\r
+ m_relative = false;\r
+ }\r
+#endif\r
+#ifdef CYGWIN\r
+ // first split off the drive letter or UNC prefix on Windows - the Cygwin environment supports these too\r
+ if (spec.size() >= 2 && isalpha(spec[0]) && spec[1] == ':')\r
+ {\r
+ // found a drive letter\r
+ i = 2;\r
+ m_drive = spec.substr(0, 2);\r
+ m_relative = false;\r
+ // if there is a drive but no path or a relative path, get the current\r
+ // path for this drive and prepend it to the path\r
+ if (i == spec.size() || !is_separator(spec[i]))\r
+ {\r
+ // non-existent drive - fill in just the root directory\r
+ spec.insert(2, 1, preferred_separator);\r
+ }\r
+ }\r
+ else if (spec.size() >= 2 && is_separator(spec[0]) && is_separator(spec[1]))\r
+ {\r
+ // found an UNC prefix\r
+ i = 2;\r
+ // find the end of the prefix by scanning for the next seperator or the end of the spec\r
+ while (i < spec.size() && !is_separator(spec[i])) i++;\r
+ m_drive = spec.substr(0, i);\r
+ m_relative = false;\r
+ }\r
+#endif\r
+ // check whether the path is absolute or relative and discard the leading / if absolute\r
+ if (i < spec.size() && is_separator(spec[i]))\r
+ {\r
+ m_relative = false;\r
+ i++;\r
+#ifdef MSWINDOWS\r
+ // if there's no drive, fill it in on Windows since absolute paths must have a drive\r
+ if (m_drive.empty())\r
+ {\r
+ m_drive += (char)(_getdrive() - 1 + 'A');\r
+ m_drive += ':';\r
+ }\r
+#endif\r
+ }\r
+ // now extract the path elements - note that a trailing / is not significant since /a/b/c/ === /a/b/c\r
+ // also note that the leading / has been discarded - all paths are relative\r
+ // if absolute() is set, then paths are relative to the drive, else they are relative to the current path\r
+ unsigned start = i;\r
+ while(i <= spec.size())\r
+ {\r
+ if (i == spec.size())\r
+ {\r
+ // path element terminated by the end of the string\r
+ // discard this element if it is zero length because that represents the trailing /\r
+ if (i != start)\r
+ m_path.push_back(spec.substr(start, i-start));\r
+ }\r
+ else if (is_separator(spec[i]))\r
+ {\r
+ // path element terminated by a separator\r
+ m_path.push_back(spec.substr(start, i-start));\r
+ start = i+1;\r
+ }\r
+ i++;\r
+ }\r
+ // TODO - some error handling?\r
+ return true;\r
+ }\r
+\r
+ bool file_specification::initialise_file(const std::string& spec)\r
+ {\r
+ m_filename.erase();\r
+ // remove last element as the file and then treat the rest as a folder\r
+ unsigned i = spec.size();\r
+ while (--i)\r
+ {\r
+ if (is_separator(spec[i]))\r
+ break;\r
+#ifdef MSWINDOWS\r
+ // on windoze you can say a:fred.txt so the colon separates the path from the filename\r
+ else if (i == 1 && spec[i] == ':')\r
+ break;\r
+#endif\r
+ }\r
+ bool result = initialise_folder(spec.substr(0,i+1));\r
+ m_filename = spec.substr(i+1,spec.size()-i-1);\r
+ // TODO - some error handling?\r
+ return result;\r
+ }\r
+\r
+ bool file_specification::simplify(void)\r
+ {\r
+ // simplify the path by removing unnecessary . and .. entries - Note that zero-length entries are treated like .\r
+ for (unsigned i = 0; i < m_path.size(); )\r
+ {\r
+ if (m_path[i].empty() || m_path[i].compare(".") == 0)\r
+ {\r
+ // found . or null\r
+ // these both mean do nothing - so simply delete this element\r
+ m_path.erase(m_path.begin()+i);\r
+ }\r
+ else if (m_path[i].compare("..") == 0)\r
+ {\r
+ // found ..\r
+ if (i == 0 && !m_relative)\r
+ {\r
+ // up from the root does nothing so can be deleted\r
+ m_path.erase(m_path.begin()+i);\r
+ i++;\r
+ }\r
+ else if (i == 0 || m_path[i-1].compare("..") == 0)\r
+ {\r
+ // the first element of a relative path or the previous element is .. then keep it\r
+ i++;\r
+ }\r
+ else\r
+ {\r
+ // otherwise delete this element and the previous one\r
+ m_path.erase(m_path.begin()+i);\r
+ m_path.erase(m_path.begin()+i-1);\r
+ i--;\r
+ }\r
+ }\r
+ // keep all other elements\r
+ else\r
+ i++;\r
+ }\r
+ // TODO - error checking?\r
+ return true;\r
+ }\r
+\r
+ bool file_specification::make_absolute(const std::string& root)\r
+ {\r
+ // test whether already an absolute path in which case there's nothing to do\r
+ if (absolute()) return true;\r
+ // now simply call the other version of make_absolute\r
+ file_specification rootspec;\r
+ rootspec.initialise_folder(root);\r
+ return make_absolute(rootspec);\r
+ }\r
+\r
+ bool file_specification::make_absolute(const file_specification& rootspec)\r
+ {\r
+ // test whether already an absolute path in which case there's nothing to do\r
+ if (absolute()) return true;\r
+ // initialise the result with the root and make the root absolute\r
+ file_specification result = rootspec;\r
+ result.make_absolute();\r
+ // now append this's relative path and filename to the root's absolute path\r
+ for (unsigned i = 0; i < subpath_size(); i++)\r
+ result.add_subpath(subpath_element(i));\r
+ result.set_file(file());\r
+ // now the result is the absolute path, so transfer it to this\r
+ *this = result;\r
+ // and simplify to get rid of any unwanted .. or . elements\r
+ simplify();\r
+ return true;\r
+ }\r
+\r
+ bool file_specification::make_relative(const std::string& root)\r
+ {\r
+ // test whether already an relative path in which case there's nothing to do\r
+ if (relative()) return true;\r
+ // now simply call the other version of make_relative\r
+ file_specification rootspec;\r
+ rootspec.initialise_folder(root);\r
+ return make_relative(rootspec);\r
+ }\r
+\r
+ bool file_specification::make_relative(const file_specification& rootspec)\r
+ {\r
+ // test whether already an relative path in which case there's nothing to do\r
+ if (relative()) return true;\r
+ // initialise the result with the root and make the root absolute\r
+ file_specification absolute_root = rootspec;\r
+ absolute_root.make_absolute();\r
+ // now compare elements of the absolute root with elements of this to find the common path\r
+ // if the drives are different, no conversion can take place and the result must be absolute, else clear the drive\r
+ if (!path_compare(drive(), absolute_root.drive())) return true;\r
+ set_drive("");\r
+ // first remove leading elements that are identical to the corresponding element in root\r
+ unsigned i = 0;\r
+ while(subpath_size() > 0 && \r
+ i < absolute_root.subpath_size() && \r
+ path_compare(subpath_element(0), absolute_root.subpath_element(i)))\r
+ {\r
+ subpath_erase(0);\r
+ i++;\r
+ }\r
+ // now add a .. prefix for every element in root that is different from this\r
+ while (i < absolute_root.subpath_size())\r
+ {\r
+ m_path.insert(m_path.begin(), "..");\r
+ i++;\r
+ }\r
+ set_relative();\r
+ return true;\r
+ }\r
+\r
+ std::string file_specification::image(void) const\r
+ {\r
+ std::string result = m_drive;\r
+ if (absolute())\r
+ result += preferred_separator;\r
+ if (!m_path.empty())\r
+ {\r
+ for (unsigned i = 0; i < m_path.size(); i++)\r
+ {\r
+ if (i != 0) result += std::string(1,preferred_separator);\r
+ result += m_path[i];\r
+ }\r
+ }\r
+ else if (relative())\r
+ result += '.';\r
+ // add a trailing / to the last directory element\r
+ if (result.empty() || !is_separator(result[result.size()-1]))\r
+ result += preferred_separator;\r
+ if (!m_filename.empty())\r
+ result += m_filename;\r
+ return result;\r
+ }\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// classifying functions\r
+\r
+// Under both Windows and Unix, the stat function is used for classification\r
+\r
+// Under Linux, the following classifications are defined\r
+// source: Linux man page for stat(2) http://linux.die.net/man/2/stat\r
+// S_IFMT 0170000 bitmask for the file type bitfields\r
+// S_IFSOCK 0140000 socket (Note this overlaps with S_IFDIR)\r
+// S_IFLNK 0120000 symbolic link\r
+// S_IFREG 0100000 regular file\r
+// S_IFBLK 0060000 block device\r
+// S_IFDIR 0040000 directory\r
+// S_IFCHR 0020000 character device\r
+// S_IFIFO 0010000 FIFO\r
+// There are also some Posix-standard macros:\r
+// S_ISREG(m) is it a regular file? \r
+// S_ISDIR(m) directory? \r
+// S_ISCHR(m) character device? \r
+// S_ISBLK(m) block device? \r
+// S_ISFIFO(m) FIFO (named pipe)? \r
+// S_ISLNK(m) symbolic link? (Not in POSIX.1-1996.) \r
+// S_ISSOCK(m) socket? (Not in POSIX.1-1996.)\r
+// Under Windows, the following are defined:\r
+// source: Header file sys/stat.h distributed with Visual Studio 10\r
+// _S_IFMT (S_IFMT) 0xF000 file type mask\r
+// _S_IFREG (S_IFREG) 0x8000 regular\r
+// _S_IFDIR (S_IFDIR) 0x4000 directory\r
+// _S_IFCHR (S_IFCHR) 0x2000 character special\r
+// _S_IFIFO 0x1000 pipe\r
+\r
+#ifdef MSWINDOWS\r
+// file type tests are not defined for some reason on Windows despite them providing the stat() function!\r
+#define R_OK 4\r
+#define W_OK 2\r
+// Posix-style macros for Windows\r
+#ifndef S_ISREG\r
+#define S_ISREG(mode) ((mode & _S_IFMT) == _S_IFREG)\r
+#endif\r
+#ifndef S_ISDIR\r
+#define S_ISDIR(mode) ((mode & _S_IFMT) == _S_IFDIR)\r
+#endif\r
+#ifndef S_ISCHR\r
+#define S_ISCHR(mode) ((mode & _S_IFMT) == _S_IFCHR)\r
+#endif\r
+#ifndef S_ISBLK\r
+#define S_ISBLK(mode) (false)\r
+#endif\r
+#ifndef S_ISFIFO\r
+#define S_ISFIFO(mode) ((mode & _S_IFMT) == _S_IFIFO)\r
+#endif\r
+#ifndef S_ISLNK\r
+#define S_ISLNK(mode) (false)\r
+#endif\r
+#ifndef S_ISSOCK\r
+#define S_ISSOCK(mode) (false)\r
+#endif\r
+#endif\r
+\r
+ bool is_present (const std::string& thing)\r
+ {\r
+ // strip off any trailing separator because that will cause the stat function to fail\r
+ std::string path = thing;\r
+ if (!path.empty() && is_separator(path[path.size()-1]))\r
+ path.erase(path.size()-1,1);\r
+ // now test if this thing exists using the built-in stat function\r
+ struct stat buf;\r
+ return stat(path.c_str(), &buf) == 0;\r
+ }\r
+\r
+ bool is_folder (const std::string& thing)\r
+ {\r
+ // strip off any trailing separator because that will cause the stat function to fail\r
+ std::string path = thing;\r
+ if (!path.empty() && is_separator(path[path.size()-1]))\r
+ path.erase(path.size()-1,1);\r
+ // now test if this thing exists using the built-in stat function and if so, is it a folder\r
+ struct stat buf;\r
+ if (!(stat(path.c_str(), &buf) == 0))\r
+ return false;\r
+ // If the object is present, see if it is a directory\r
+ // this is the Posix-approved way of testing\r
+ return S_ISDIR(buf.st_mode);\r
+ }\r
+\r
+ bool is_file (const std::string& thing)\r
+ {\r
+ // strip off any trailing separator because that will cause the stat function to fail\r
+ std::string path = thing;\r
+ if (!path.empty() && is_separator(path[path.size()-1]))\r
+ path.erase(path.size()-1,1);\r
+ // now test if this thing exists using the built-in stat function and if so, is it a file\r
+ struct stat buf;\r
+ if (!(stat(path.c_str(), &buf) == 0))\r
+ return false;\r
+ // If the object is present, see if it is a file or file-like object\r
+ // Note that devices are neither folders nor files\r
+ // this is the Posix-approved way of testing\r
+ return S_ISREG(buf.st_mode) || S_ISLNK(buf.st_mode) || S_ISSOCK(buf.st_mode) || S_ISFIFO(buf.st_mode);\r
+ }\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// file functions\r
+\r
+ bool file_exists (const std::string& filespec)\r
+ {\r
+ return is_file(filespec);\r
+ }\r
+\r
+ bool file_readable (const std::string& filespec)\r
+ {\r
+ // a file is readable if it exists and can be read\r
+ if (!file_exists(filespec)) return false;\r
+ return access(filespec.c_str(),R_OK)==0;\r
+ }\r
+\r
+ bool file_writable (const std::string& filespec)\r
+ {\r
+ // a file is writable if it exists as a file and is writable or if\r
+ // it doesn't exist but could be created and would be writable\r
+ if (is_present(filespec))\r
+ {\r
+ if (!is_file(filespec)) return false;\r
+ return access(filespec.c_str(),W_OK)==0;\r
+ }\r
+ std::string dir = folder_part(filespec);\r
+ if (dir.empty()) dir = ".";\r
+ return folder_writable(dir);\r
+ }\r
+\r
+ size_t file_size (const std::string& filespec)\r
+ {\r
+ struct stat buf;\r
+ if (!(stat(filespec.c_str(), &buf) == 0)) return 0;\r
+ return buf.st_size;\r
+ }\r
+\r
+ bool file_delete (const std::string& filespec)\r
+ {\r
+ if (!is_file(filespec)) return false;\r
+ return remove(filespec.c_str())==0;\r
+ }\r
+\r
+ bool file_rename (const std::string& old_filespec, const std::string& new_filespec)\r
+ {\r
+ if (!is_file(old_filespec)) return false;\r
+ return rename(old_filespec.c_str(), new_filespec.c_str())==0;\r
+ }\r
+\r
+ bool file_copy (const std::string& old_filespec, const std::string& new_filespec)\r
+ {\r
+ if (!is_file(old_filespec)) return false;\r
+ // do an exact copy - to do this, use binary mode\r
+ bool result = true;\r
+ FILE* old_file = fopen(old_filespec.c_str(),"rb");\r
+ FILE* new_file = fopen(new_filespec.c_str(),"wb");\r
+ if (!old_file)\r
+ result = false;\r
+ else if (!new_file)\r
+ result = false;\r
+ else\r
+ {\r
+ for (int byte = getc(old_file); byte != EOF; byte = getc(old_file))\r
+ putc(byte,new_file);\r
+ }\r
+ if (old_file) fclose(old_file);\r
+ if (new_file) fclose(new_file);\r
+ return result;\r
+ }\r
+\r
+ bool file_move (const std::string& old_filespec, const std::string& new_filespec)\r
+ {\r
+ // try to move the file by renaming - if that fails then do a copy and delete the original\r
+ if (file_rename(old_filespec, new_filespec))\r
+ return true;\r
+ if (!file_copy(old_filespec, new_filespec))\r
+ return false;\r
+ // I'm not sure what to do if the delete fails - is that an error?\r
+ // I've made it an error and then delete the copy so that the original state is recovered\r
+ if (file_delete(old_filespec))\r
+ return true;\r
+ file_delete(new_filespec);\r
+ return false;\r
+ }\r
+\r
+ time_t file_created (const std::string& filespec)\r
+ {\r
+ struct stat buf;\r
+ if (!(stat(filespec.c_str(), &buf) == 0)) return 0;\r
+ return buf.st_ctime;\r
+ }\r
+\r
+ time_t file_modified (const std::string& filespec)\r
+ {\r
+ struct stat buf;\r
+ if (!(stat(filespec.c_str(), &buf) == 0)) return 0;\r
+ return buf.st_mtime;\r
+ }\r
+\r
+ time_t file_accessed (const std::string& filespec)\r
+ {\r
+ struct stat buf;\r
+ if (!(stat(filespec.c_str(), &buf) == 0)) return 0;\r
+ return buf.st_atime;\r
+ }\r
+\r
+ std::string create_filespec (const std::string& directory, const std::string& filename)\r
+ {\r
+ std::string result = directory;\r
+ // if directory is empty then no directory part will be added\r
+ // add trailing slash if the directory was specified and does not have a trailing slash\r
+ if (!result.empty() && !is_separator(result[result.size()-1]))\r
+ result += preferred_separator;\r
+ // if filename is null or empty, nothing will be added so the path is then a directory path\r
+ result += filename;\r
+ return result;\r
+ }\r
+\r
+ std::string create_filespec (const std::string& directory, const std::string& basename, const std::string& extension)\r
+ {\r
+ return create_filespec(directory, create_filename(basename, extension));\r
+ }\r
+\r
+ std::string create_filename(const std::string& basename, const std::string& extension)\r
+ {\r
+ std::string name = basename;\r
+ // extension is optional - so the dot is also optional\r
+ if (!extension.empty())\r
+ {\r
+ if (extension[0] != '.') name += '.';\r
+ name += extension;\r
+ }\r
+ return name;\r
+ }\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// folder functions\r
+\r
+ bool folder_create (const std::string& directory)\r
+ {\r
+#ifdef MSWINDOWS\r
+ return mkdir(directory.c_str()) == 0;\r
+#else\r
+ return mkdir(directory.c_str(), 0777) == 0;\r
+#endif\r
+ }\r
+\r
+ bool folder_exists (const std::string& directory)\r
+ {\r
+ return is_folder(directory);\r
+ }\r
+\r
+ bool folder_readable (const std::string& directory)\r
+ {\r
+ // a folder is readable if it exists and has read access\r
+ std::string dir = directory;\r
+ if (dir.empty()) dir = ".";\r
+ if (!folder_exists(dir)) return false;\r
+ return access(dir.c_str(),R_OK)==0;\r
+ }\r
+\r
+ bool folder_writable (const std::string& directory)\r
+ {\r
+ // a folder is writable if it exists and has write access\r
+ std::string dir = directory;\r
+ if (dir.empty()) dir = ".";\r
+ if (!folder_exists(dir)) return false;\r
+ return access(dir.c_str(),W_OK)==0;\r
+ }\r
+\r
+ bool folder_delete (const std::string& directory, bool recurse)\r
+ {\r
+ std::string dir = directory;\r
+ if (dir.empty()) dir = ".";\r
+ if (!folder_exists(dir)) return false;\r
+ bool result = true;\r
+ // depth-first traversal ensures that directory contents are deleted before trying to delete the directory itself\r
+ if (recurse)\r
+ {\r
+ std::vector<std::string> subdirectories = folder_subdirectories(dir);\r
+ for (std::vector<std::string>::size_type d = 0; d < subdirectories.size(); ++d)\r
+ if (!folder_delete(folder_down(dir,subdirectories[d]),true)) \r
+ result = false;\r
+ std::vector<std::string> files = folder_files(dir);\r
+ for (std::vector<std::string>::size_type f = 0; f < files.size(); ++f)\r
+ if (!file_delete(create_filespec(dir, files[f]))) \r
+ result = false;\r
+ }\r
+ if (rmdir(dir.c_str())!=0) result = false;\r
+ return result;\r
+ }\r
+\r
+ bool folder_rename (const std::string& old_directory, const std::string& new_directory)\r
+ {\r
+ if (!folder_exists(old_directory)) return false;\r
+ return rename(old_directory.c_str(), new_directory.c_str())==0;\r
+ }\r
+\r
+ bool folder_empty(const std::string& directory)\r
+ {\r
+ std::string dir = directory.empty() ? std::string(".") : directory;\r
+ bool result = true;\r
+#ifdef MSWINDOWS\r
+ std::string wildcard = create_filespec(dir, "*.*");\r
+ long handle = -1;\r
+ _finddata_t fileinfo;\r
+ for (bool OK = (handle = _findfirst((char*)wildcard.c_str(), &fileinfo)) != -1; OK; OK = (_findnext(handle, &fileinfo)==0))\r
+ {\r
+ std::string strentry = fileinfo.name;\r
+ if (strentry.compare(".")!=0 && strentry.compare("..")!=0)\r
+ {\r
+ result = false;\r
+ break;\r
+ }\r
+ }\r
+ _findclose(handle);\r
+#else\r
+ DIR* d = opendir(dir.c_str());\r
+ if (d)\r
+ {\r
+ for (dirent* entry = readdir(d); entry; entry = readdir(d))\r
+ {\r
+ std::string strentry = entry->d_name;\r
+ if (strentry.compare(".")!=0 && strentry.compare("..")!=0)\r
+ {\r
+ result = false;\r
+ break;\r
+ }\r
+ }\r
+ closedir(d);\r
+ }\r
+#endif\r
+ return result;\r
+ }\r
+\r
+ bool folder_set_current(const std::string& folder)\r
+ {\r
+ if (!folder_exists(folder))\r
+ return false;\r
+#ifdef MSWINDOWS\r
+ // Windose implementation - this returns non-zero for success\r
+ return (SetCurrentDirectoryA(folder.c_str()) != 0);\r
+#else\r
+ // Unix implementation - this returns zero for success\r
+ return (chdir(folder.c_str()) == 0);\r
+#endif\r
+ }\r
+\r
+ std::string folder_current (void)\r
+ {\r
+ return ".";\r
+ }\r
+\r
+ std::string folder_current_full(void)\r
+ {\r
+ // It's not clear from the documentation whether the buffer for a path should be one byte longer\r
+ // than the maximum path length to allow for the null termination, so I have made it so anyway\r
+#ifdef MSWINDOWS\r
+ char abspath [MAX_PATH+1];\r
+ return std::string(_fullpath(abspath, ".", MAX_PATH+1));\r
+#else\r
+ char pathname [MAXPATHLEN+1];\r
+ char* result = getcwd(pathname,MAXPATHLEN+1);\r
+ if (!result)\r
+ {\r
+ // should really report the error from errno\r
+ return std::string();\r
+ }\r
+ return std::string(result);\r
+#endif\r
+ }\r
+\r
+ std::string folder_down (const std::string& directory, const std::string& subdirectory)\r
+ {\r
+ file_specification spec;\r
+ spec.initialise_folder(directory);\r
+ spec.add_subpath(subdirectory);\r
+ return spec.image();\r
+ }\r
+\r
+ std::string folder_up (const std::string& directory, unsigned levels)\r
+ {\r
+ file_specification spec;\r
+ spec.initialise_folder(directory);\r
+ for (unsigned i = 0; i < levels; i++)\r
+ spec.add_subpath("..");\r
+ spec.simplify();\r
+ return spec.image();\r
+ }\r
+\r
+ std::vector<std::string> folder_subdirectories (const std::string& directory)\r
+ {\r
+ return folder_wildcard(directory, "*", true, false);\r
+ }\r
+\r
+ std::vector<std::string> folder_files (const std::string& directory)\r
+ {\r
+ return folder_wildcard(directory, "*", false, true);\r
+ }\r
+\r
+ std::vector<std::string> folder_all(const std::string& directory)\r
+ {\r
+ return folder_wildcard(directory, "*", true, true);\r
+ }\r
+\r
+ std::vector<std::string> folder_wildcard (const std::string& directory, const std::string& wild, bool subdirs, bool files)\r
+ {\r
+ std::string dir = directory.empty() ? std::string(".") : directory;\r
+ std::vector<std::string> results;\r
+#ifdef MSWINDOWS\r
+ std::string wildcard = create_filespec(dir, wild);\r
+ long handle = -1;\r
+ _finddata_t fileinfo;\r
+ for (bool OK = (handle = _findfirst((char*)wildcard.c_str(), &fileinfo)) != -1; OK; OK = (_findnext(handle, &fileinfo)==0))\r
+ {\r
+ std::string strentry = fileinfo.name;\r
+ if (strentry.compare(".")!=0 && strentry.compare("..")!=0)\r
+ if ((subdirs && (fileinfo.attrib & _A_SUBDIR)) || (files && !(fileinfo.attrib & _A_SUBDIR)))\r
+ results.push_back(strentry);\r
+ }\r
+ _findclose(handle);\r
+#else\r
+ DIR* d = opendir(dir.c_str());\r
+ if (d)\r
+ {\r
+ for (dirent* entry = readdir(d); entry; entry = readdir(d))\r
+ {\r
+ std::string strentry = entry->d_name;\r
+ if (strentry.compare(".")!=0 && strentry.compare("..")!=0)\r
+ {\r
+ std::string subpath = create_filespec(dir, strentry);\r
+ if (((subdirs && is_folder(subpath)) || (files && is_file(subpath))) && (wildcard(wild, strentry)))\r
+ results.push_back(strentry);\r
+ }\r
+ }\r
+ closedir(d);\r
+ }\r
+#endif\r
+ return results;\r
+ }\r
+\r
+ std::string folder_home (void)\r
+ {\r
+ if (getenv("HOME"))\r
+ return std::string(getenv("HOME"));\r
+#ifdef MSWINDOWS\r
+ if (getenv("HOMEDRIVE") || getenv("HOMEPATH"))\r
+ return std::string(getenv("HOMEDRIVE")) + std::string(getenv("HOMEPATH"));\r
+ return "C:\\";\r
+#else\r
+ if (getenv("USER"))\r
+ return folder_down("/home", std::string(getenv("USER")));\r
+ if (getenv("USERNAME"))\r
+ return folder_down("/home", std::string(getenv("USERNAME")));\r
+ return "";\r
+#endif\r
+ }\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// path functions convert between full and relative paths\r
+\r
+ bool is_full_path(const std::string& path)\r
+ {\r
+ file_specification spec;\r
+ spec.initialise_folder(path.empty() ? std::string(".") : path);\r
+ return spec.absolute();\r
+ }\r
+\r
+ bool is_relative_path(const std::string& path)\r
+ {\r
+ file_specification spec;\r
+ spec.initialise_folder(path.empty() ? std::string(".") : path);\r
+ return spec.relative();\r
+ }\r
+\r
+ static std::string full_path(const std::string& root, const std::string& path)\r
+ {\r
+ // convert path to a full path using root as the start point for relative paths\r
+ // decompose the path and test whether it is already an absolute path, in which case just return it\r
+ file_specification spec;\r
+ spec.initialise_folder(path.empty() ? std::string(".") : path);\r
+ if (spec.absolute()) return spec.image();\r
+ // okay, so the path is relative after all, so we need to combine it with the root path\r
+ // decompose the root path and check whether it is relative\r
+ file_specification rootspec;\r
+ rootspec.initialise_folder(root.empty() ? std::string(".") : root);\r
+ if (rootspec.relative())\r
+ rootspec.make_absolute();\r
+ // Now do the conversion of the path relative to the root\r
+ spec.make_absolute(rootspec);\r
+ return spec.image();\r
+ }\r
+\r
+ static std::string relative_path(const std::string& root, const std::string& path)\r
+ {\r
+ // convert path to a relative path, using the root path as its starting point\r
+ // first convert both paths to full paths relative to CWD\r
+ file_specification rootspec;\r
+ rootspec.initialise_folder(root.empty() ? std::string(".") : root);\r
+ if (rootspec.relative())\r
+ rootspec.make_absolute();\r
+ file_specification spec;\r
+ spec.initialise_folder(path.empty() ? std::string(".") : path);\r
+ if (spec.relative())\r
+ spec.make_absolute();\r
+ // now make path spec relative to the root spec\r
+ spec.make_relative(rootspec);\r
+ return spec.image();\r
+ }\r
+\r
+ std::string folder_to_path (const std::string& path, const std::string& directory)\r
+ {\r
+ return full_path(path, directory);\r
+ }\r
+\r
+ std::string filespec_to_path (const std::string& path, const std::string& spec)\r
+ {\r
+ return create_filespec(folder_to_path(path, folder_part(spec)),filename_part(spec));\r
+ }\r
+\r
+ std::string folder_to_path(const std::string& folder)\r
+ {\r
+ return folder_to_path(folder_current(), folder);\r
+ }\r
+\r
+ std::string filespec_to_path(const std::string& filespec)\r
+ {\r
+ return filespec_to_path(folder_current(), filespec);\r
+ }\r
+\r
+ std::string folder_to_relative_path(const std::string& root, const std::string& folder)\r
+ {\r
+ return relative_path(root, folder);\r
+ }\r
+\r
+ std::string filespec_to_relative_path(const std::string& root, const std::string& spec)\r
+ {\r
+ return create_filespec(folder_to_relative_path(root, folder_part(spec)),filename_part(spec));\r
+ }\r
+\r
+ std::string folder_to_relative_path(const std::string& folder)\r
+ {\r
+ return folder_to_relative_path(folder_current(), folder);\r
+ }\r
+\r
+ std::string filespec_to_relative_path(const std::string& filespec)\r
+ {\r
+ return filespec_to_relative_path(folder_current(), filespec);\r
+ }\r
+\r
+ std::string folder_append_separator(const std::string& folder)\r
+ {\r
+ std::string result = folder;\r
+ if (result.empty() || !is_separator(result[result.size()-1]))\r
+ result += preferred_separator;\r
+ return result;\r
+ }\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+ std::string basename_part (const std::string& spec)\r
+ {\r
+ std::string fname = filename_part(spec);\r
+ // scan back through filename until a '.' is found and remove suffix\r
+ // the whole filename is the basename if there is no '.'\r
+ std::string::size_type i = fname.find_last_of('.');\r
+ // observe Unix convention that a dot at the start of a filename is part of the basename, not the extension\r
+ if (i != 0 && i != std::string::npos)\r
+ fname.erase(i, fname.size()-i);\r
+ return fname;\r
+ }\r
+\r
+ std::string filename_part (const std::string& spec)\r
+ {\r
+ // scan back through filename until a preferred_separator is found and remove prefix;\r
+ // if there is no preferred_separator then remove nothing, i.e. the whole filespec is filename\r
+ unsigned i = spec.size();\r
+ while (i--)\r
+ {\r
+ if (is_separator(spec[i]))\r
+ return spec.substr(i+1,spec.size()-i-1);\r
+ }\r
+ return spec;\r
+ }\r
+\r
+ std::string extension_part (const std::string& spec)\r
+ {\r
+ std::string fname = filename_part(spec);\r
+ // scan back through filename until a '.' is found and remove prefix;\r
+ std::string::size_type i = fname.find_last_of('.');\r
+ // observe Unix convention that a dot at the start of a filename is part of the name, not the extension;\r
+ if (i != 0 && i != std::string::npos)\r
+ fname.erase(0, i+1);\r
+ else\r
+ fname.erase();\r
+ return fname;\r
+ }\r
+\r
+ std::string folder_part (const std::string& spec)\r
+ {\r
+ // scan back through filename until a separator is found and remove prefix\r
+ // if there is no separator, remove the whole\r
+ unsigned i = spec.size();\r
+ while (i--)\r
+ {\r
+ if (is_separator(spec[i]))\r
+ return spec.substr(0,i);\r
+ }\r
+ return std::string();\r
+ }\r
+\r
+ std::vector<std::string> filespec_elements(const std::string& filespec)\r
+ {\r
+ file_specification spec;\r
+ spec.initialise_file(filespec);\r
+ std::vector<std::string> result = spec.path();\r
+ if (!spec.drive().empty()) result.insert(result.begin(),spec.drive());\r
+ if (!spec.file().empty()) result.push_back(spec.file());\r
+ return result;\r
+ }\r
+\r
+ std::vector<std::string> folder_elements(const std::string& folder)\r
+ {\r
+ file_specification spec;\r
+ spec.initialise_folder(folder);\r
+ std::vector<std::string> result = spec.path();\r
+ if (!spec.drive().empty()) result.insert(result.begin(),spec.drive());\r
+ return result;\r
+ }\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// mimic the command lookup used by the shell\r
+\r
+// Windows looks at the following locations:\r
+// 1) application root\r
+// 2) current directory\r
+// 3) 32-bit system directory\r
+// 4) 16-bit system directory\r
+// 5) windows system directory\r
+// 6) %path%\r
+// currently only (2) and (6) has been implemented although many system folders are on the path anyway\r
+// also implement the implied .exe extension on commands with no path (see CreateProcess documentation)\r
+// TODO - PATHEXT handling to find non-exe executables\r
+\r
+ std::string path_lookup (const std::string& command)\r
+ {\r
+ std::string path = std::string(".") + PATH_SPLITTER + getenv("PATH");\r
+ return lookup(command, path);\r
+ }\r
+\r
+ std::string lookup (const std::string& command, const std::string& path, const std::string& splitter)\r
+ {\r
+ // first check whether the command is already a path and check whether it exists\r
+ if (!folder_part(command).empty())\r
+ {\r
+ if (file_exists(command))\r
+ return command;\r
+ }\r
+ else\r
+ {\r
+ // command is just a name - so do path lookup\r
+ // split the path into its elements\r
+ std::vector<std::string> paths;\r
+ if (!path.empty())\r
+ {\r
+ for(std::string::size_type offset = 0;;)\r
+ {\r
+ std::string::size_type found = path.find(splitter, offset);\r
+ if (found != std::string::npos)\r
+ {\r
+ paths.push_back(path.substr(offset, found-offset));\r
+ offset = found + splitter.size();\r
+ }\r
+ else\r
+ {\r
+ paths.push_back(path.substr(offset, path.size()-offset));\r
+ break;\r
+ }\r
+ }\r
+ }\r
+ // now lookup each path to see if it its the matching one\r
+ for (unsigned i = 0; i < paths.size(); i++)\r
+ {\r
+ std::string spec = create_filespec(paths[i], command);\r
+ if (file_exists(spec))\r
+ {\r
+ return spec;\r
+ }\r
+ }\r
+ }\r
+#ifdef MSWINDOWS\r
+ // if there is no extension, try recursing on each possible extension\r
+ // TODO iterate through PATHEXT\r
+ if (extension_part(command).empty())\r
+ return lookup(create_filespec(folder_part(command), basename_part(command), "exe"), path, splitter);\r
+#endif\r
+ // if path lookup failed, return empty string to indicate error\r
+ return std::string();\r
+ }\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+ std::string install_path(const std::string& argv0)\r
+ {\r
+ std::string bin_directory = folder_part(argv0);\r
+ if (bin_directory.empty())\r
+ {\r
+ // do path lookup to find the executable path\r
+ bin_directory = folder_part(path_lookup(argv0));\r
+ }\r
+ return bin_directory;\r
+ }\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
-#ifndef STLPLUS_FILE_SYSTEM
-#define STLPLUS_FILE_SYSTEM
-////////////////////////////////////////////////////////////////////////////////
-
-// Author: Andy Rushton
-// Copyright: (c) Southampton University 1999-2004
-// (c) Andy Rushton 2004-2009
-// License: BSD License, see ../docs/license.html
-
-// Simplified access to the File system
-
-// All file system access and filename manipulation should be done
-// with this package. Then it is only necessary to port this package
-// to port all file handling.
-
-////////////////////////////////////////////////////////////////////////////////
-#include "portability_fixes.hpp"
-#include <string>
-#include <vector>
-#include <time.h>
-////////////////////////////////////////////////////////////////////////////////
-
-namespace stlplus
-{
-
- ////////////////////////////////////////////////////////////////////////////////
- // implement string comparison of paths - Unix is case-sensitive, Windows is case-insensitive
-
- bool path_compare(const std::string& l, const std::string& r);
-
- ////////////////////////////////////////////////////////////////////////////////
- // classifying functions
-
- // test for whether there's something (i.e. folder or file) with this name
- bool is_present(const std::string& thing);
- // test for whether there's something present and its a folder
- bool is_folder(const std::string& thing);
- // test for whether there's something present and its a file
- bool is_file(const std::string& thing);
-
- ////////////////////////////////////////////////////////////////////////////////
- // file functions
-
- // tests whether there's a file of this name
- bool file_exists(const std::string& filespec);
- // tests whether the file is readable - i.e. exists and has read mode set
- bool file_readable(const std::string& filespec);
- // tests whether file is writable - either it exists and is writable or doesn't exist but is in a writable folder
- bool file_writable(const std::string& filespec);
- // the size of the file in bytes - 0 if doesn't exist
- size_t file_size(const std::string& filespec);
- // delete the file - returns true if the delete succeeded
- bool file_delete(const std::string& filespec);
- // rename the file - returns true if the rename succeeded
- bool file_rename (const std::string& old_filespec, const std::string& new_filespec);
- // make an exact copy of the file - returns true if it succeeded
- bool file_copy (const std::string& old_filespec, const std::string& new_filespec);
- // move the file - tries to rename, if that fails, tries to copy - returns true if either of these succeeded
- bool file_move (const std::string& old_filespec, const std::string& new_filespec);
-
- // get the file's time stamps as a time_t - see the stlplus time.hpp package
-
- // time the file was originally created
- time_t file_created(const std::string& filespec);
- // time the file was last modified
- time_t file_modified(const std::string& filespec);
- // time the file was accessed
- time_t file_accessed(const std::string& filespec);
-
- // platform-specific string handling to combine a directory and filename into a path
-
- // combine a folder with a filename (basename.extension)
- std::string create_filespec(const std::string& folder, const std::string& filename);
- // combine a folder, a basename and an extension - extension does not need the .
- std::string create_filespec(const std::string& folder, const std::string& basename, const std::string& extension);
- // combine a basename and an extension - extension does not need the .
- std::string create_filename(const std::string& basename, const std::string& extension);
-
- ////////////////////////////////////////////////////////////////////////////////
- // folder functions
-
- // craete a folder - returns true if successful
- bool folder_create(const std::string& folder);
- // tests for whether the folder exists, i.e. there is something of that name and its a folder
- bool folder_exists(const std::string& folder);
- // test whether the folder contents are readable
- bool folder_readable(const std::string& folder);
- // tests whether the folder can be written to - for example to create a new file
- bool folder_writable(const std::string& folder);
- // delete the folder, optionally deleting the contents first - only succeeds if everything could be deleted
- bool folder_delete(const std::string& folder, bool recurse = false);
- // rename the folder - this probably only works within a disk/partition
- bool folder_rename (const std::string& old_directory, const std::string& new_directory);
- // test whether the folder is empty (of files)
- bool folder_empty(const std::string& folder);
-
- // set the current folder
- bool folder_set_current(const std::string& folder);
-
- // platform-specific string handling to retrieve some special locations
- // these do not check whether the folder exists, they just process strings
-
- // get the current folder
- std::string folder_current(void);
- // get the current folder as a full path
- std::string folder_current_full(void);
- // get the home folder - $HOME or %HOMEDRIVE%%HOMEPATH%
- std::string folder_home(void);
- // go down a level in the folder hierarchy
- std::string folder_down(const std::string& folder, const std::string& subfolder);
- // go up a level in the folder hierarchy
- std::string folder_up(const std::string& folder, unsigned levels = 1);
-
- // get folder contents
-
- // the set of all subdirectories
- std::vector<std::string> folder_subdirectories(const std::string& folder);
- // the set of all files
- std::vector<std::string> folder_files(const std::string& folder);
- // the set of all folders and files
- std::vector<std::string> folder_all(const std::string& folder);
- // the set of all folder contents matching a wildcard string
- // if folders is true, include folders; if files is true, include files
- std::vector<std::string> folder_wildcard(const std::string& folder,
- const std::string& wildcard,
- bool folders = true,
- bool files = true);
-
- ////////////////////////////////////////////////////////////////////////////////
- // path functions
-
- // string manipulations of paths
-
- // test whether a string represents a full path or a relative one
- bool is_full_path(const std::string& path);
- bool is_relative_path(const std::string& path);
-
- // convert to a full path relative to the root path
- std::string folder_to_path(const std::string& root, const std::string& folder);
- std::string filespec_to_path(const std::string& root, const std::string& filespec);
-
- // convert to a full path relative to the current working directory
- std::string folder_to_path(const std::string& folder);
- std::string filespec_to_path(const std::string& filespec);
-
- // convert to a relative path relative to the root path
- std::string folder_to_relative_path(const std::string& root, const std::string& folder);
- std::string filespec_to_relative_path(const std::string& root, const std::string& filespec);
-
- // convert to a relative path relative to the current working directory
- std::string folder_to_relative_path(const std::string& folder);
- std::string filespec_to_relative_path(const std::string& filespec);
-
- // append a folder separator to the path to make it absolutely clear that it is a folder
- std::string folder_append_separator(const std::string& folder);
-
- ////////////////////////////////////////////////////////////////////////////////
- // access functions split a filespec into its elements
-
- // get the basename - that is, the name of the file without folder or extension parts
- std::string basename_part(const std::string& filespec);
- // get the filename - that is, the name of the file without folder part but with extension
- std::string filename_part(const std::string& filespec);
- // get the extension - that is the part of the filename after the . (and excluding the .)
- std::string extension_part(const std::string& filespec);
- // get the folder part - that is the filespec with the filename removed
- std::string folder_part(const std::string& filespec);
-
- // split a path into a vector of elements - i.e. split at the folder separator
- std::vector<std::string> folder_elements(const std::string& folder);
- std::vector<std::string> filespec_elements(const std::string& filespec);
-
- ////////////////////////////////////////////////////////////////////////////////
- // Path lookup functions
-
-#ifdef MSWINDOWS
-#define PATH_SPLITTER ";"
-#else
-#define PATH_SPLITTER ":"
-#endif
-
- // The lookup normally carried out by the shell to find a command in a
- // directory in the PATH. Give this function the name of a command and it
- // will return the full path. It returns an empty string on failure.
- std::string path_lookup (const std::string& command);
-
- // Generalised form of the above, takes a second argument
- // - the list to search. This can be used to do other path lookups,
- // such as LD_LIBRARY_PATH. The third argument specifies the splitter -
- // the default value of PATH_SPLITTER is appropriate for environment variables.
- std::string lookup (const std::string& file, const std::string& path, const std::string& splitter = PATH_SPLITTER);
-
- // utility function for finding the folder that contains the current executable
- // the argument is the argv[0] parameter passed to main
- std::string install_path(const std::string& argv0);
-
- ////////////////////////////////////////////////////////////////////////////////
-
-} // end namespace stlplus
-
-#endif
+#ifndef STLPLUS_FILE_SYSTEM\r
+#define STLPLUS_FILE_SYSTEM\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+// Author: Andy Rushton\r
+// Copyright: (c) Southampton University 1999-2004\r
+// (c) Andy Rushton 2004 onwards\r
+// License: BSD License, see ../docs/license.html\r
+\r
+// Simplified access to the File system\r
+\r
+// All file system access and filename manipulation should be done\r
+// with this package. Then it is only necessary to port this package\r
+// to port all file handling.\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "portability_fixes.hpp"\r
+#include <string>\r
+#include <vector>\r
+#include <time.h>\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // implement string comparison of paths - Unix is case-sensitive, Windows is case-insensitive\r
+\r
+ bool path_compare(const std::string& l, const std::string& r);\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // classifying functions\r
+\r
+ // test for whether there's something (i.e. folder or file) with this name\r
+ bool is_present(const std::string& thing);\r
+ // test for whether there's something present and its a folder\r
+ bool is_folder(const std::string& thing);\r
+ // test for whether there's something present and its a file\r
+ // a file can be a regular file, a symbolic link, a FIFO or a socket, but not a device\r
+ bool is_file(const std::string& thing);\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // file functions\r
+\r
+ // tests whether there's a file of this name\r
+ bool file_exists(const std::string& filespec);\r
+ // tests whether the file is readable - i.e. exists and has read mode set\r
+ bool file_readable(const std::string& filespec);\r
+ // tests whether file is writable - either it exists and is writable or doesn't exist but is in a writable folder\r
+ bool file_writable(const std::string& filespec);\r
+ // the size of the file in bytes - 0 if doesn't exist\r
+ size_t file_size(const std::string& filespec);\r
+ // delete the file - returns true if the delete succeeded\r
+ bool file_delete(const std::string& filespec);\r
+ // rename the file - returns true if the rename succeeded\r
+ bool file_rename (const std::string& old_filespec, const std::string& new_filespec);\r
+ // make an exact copy of the file - returns true if it succeeded\r
+ bool file_copy (const std::string& old_filespec, const std::string& new_filespec);\r
+ // move the file - tries to rename, if that fails, tries to copy - returns true if either of these succeeded\r
+ bool file_move (const std::string& old_filespec, const std::string& new_filespec);\r
+\r
+ // get the file's time stamps as a time_t - see the stlplus time.hpp package\r
+\r
+ // time the file was originally created\r
+ time_t file_created(const std::string& filespec);\r
+ // time the file was last modified\r
+ time_t file_modified(const std::string& filespec);\r
+ // time the file was accessed\r
+ time_t file_accessed(const std::string& filespec);\r
+\r
+ // platform-specific string handling to combine a directory and filename into a path\r
+\r
+ // combine a folder with a filename (basename.extension)\r
+ std::string create_filespec(const std::string& folder, const std::string& filename);\r
+ // combine a folder, a basename and an extension - extension does not need the .\r
+ std::string create_filespec(const std::string& folder, const std::string& basename, const std::string& extension);\r
+ // combine a basename and an extension - extension does not need the .\r
+ std::string create_filename(const std::string& basename, const std::string& extension);\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // folder functions\r
+\r
+ // craete a folder - returns true if successful\r
+ bool folder_create(const std::string& folder);\r
+ // tests for whether the folder exists, i.e. there is something of that name and its a folder\r
+ bool folder_exists(const std::string& folder);\r
+ // test whether the folder contents are readable\r
+ bool folder_readable(const std::string& folder);\r
+ // tests whether the folder can be written to - for example to create a new file\r
+ bool folder_writable(const std::string& folder);\r
+ // delete the folder, optionally deleting the contents first - only succeeds if everything could be deleted\r
+ bool folder_delete(const std::string& folder, bool recurse = false);\r
+ // rename the folder - this probably only works within a disk/partition\r
+ bool folder_rename (const std::string& old_directory, const std::string& new_directory);\r
+ // test whether the folder is empty (of files)\r
+ bool folder_empty(const std::string& folder);\r
+\r
+ // set the current folder\r
+ bool folder_set_current(const std::string& folder);\r
+\r
+ // platform-specific string handling to retrieve some special locations\r
+ // these do not check whether the folder exists, they just process strings\r
+\r
+ // get the current folder\r
+ std::string folder_current(void);\r
+ // get the current folder as a full path\r
+ std::string folder_current_full(void);\r
+ // get the home folder - $HOME or %HOMEDRIVE%%HOMEPATH%\r
+ std::string folder_home(void);\r
+ // go down a level in the folder hierarchy\r
+ std::string folder_down(const std::string& folder, const std::string& subfolder);\r
+ // go up a level in the folder hierarchy\r
+ std::string folder_up(const std::string& folder, unsigned levels = 1);\r
+\r
+ // get folder contents\r
+\r
+ // the set of all subdirectories\r
+ std::vector<std::string> folder_subdirectories(const std::string& folder);\r
+ // the set of all files\r
+ std::vector<std::string> folder_files(const std::string& folder);\r
+ // the set of all folders and files\r
+ std::vector<std::string> folder_all(const std::string& folder);\r
+ // the set of all folder contents matching a wildcard string\r
+ // if folders is true, include folders; if files is true, include files\r
+ std::vector<std::string> folder_wildcard(const std::string& folder,\r
+ const std::string& wildcard,\r
+ bool folders = true,\r
+ bool files = true);\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // path functions\r
+\r
+ // string manipulations of paths\r
+\r
+ // test whether a string represents a full path or a relative one\r
+ bool is_full_path(const std::string& path);\r
+ bool is_relative_path(const std::string& path);\r
+\r
+ // convert to a full path relative to the root path\r
+ std::string folder_to_path(const std::string& root, const std::string& folder);\r
+ std::string filespec_to_path(const std::string& root, const std::string& filespec);\r
+\r
+ // convert to a full path relative to the current working directory\r
+ std::string folder_to_path(const std::string& folder);\r
+ std::string filespec_to_path(const std::string& filespec);\r
+\r
+ // convert to a relative path relative to the root path\r
+ std::string folder_to_relative_path(const std::string& root, const std::string& folder);\r
+ std::string filespec_to_relative_path(const std::string& root, const std::string& filespec);\r
+\r
+ // convert to a relative path relative to the current working directory\r
+ std::string folder_to_relative_path(const std::string& folder);\r
+ std::string filespec_to_relative_path(const std::string& filespec);\r
+\r
+ // append a folder separator to the path to make it absolutely clear that it is a folder\r
+ std::string folder_append_separator(const std::string& folder);\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // access functions split a filespec into its elements\r
+\r
+ // get the basename - that is, the name of the file without folder or extension parts\r
+ std::string basename_part(const std::string& filespec);\r
+ // get the filename - that is, the name of the file without folder part but with extension\r
+ std::string filename_part(const std::string& filespec);\r
+ // get the extension - that is the part of the filename after the . (and excluding the .)\r
+ std::string extension_part(const std::string& filespec);\r
+ // get the folder part - that is the filespec with the filename removed\r
+ std::string folder_part(const std::string& filespec);\r
+\r
+ // split a path into a vector of elements - i.e. split at the folder separator\r
+ std::vector<std::string> folder_elements(const std::string& folder);\r
+ std::vector<std::string> filespec_elements(const std::string& filespec);\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // Path lookup functions\r
+\r
+#ifdef MSWINDOWS\r
+#define PATH_SPLITTER ";"\r
+#else\r
+#define PATH_SPLITTER ":"\r
+#endif\r
+\r
+ // The lookup normally carried out by the shell to find a command in a\r
+ // directory in the PATH. Give this function the name of a command and it\r
+ // will return the full path. It returns an empty string on failure.\r
+ std::string path_lookup (const std::string& command);\r
+\r
+ // Generalised form of the above, takes a second argument\r
+ // - the list to search. This can be used to do other path lookups,\r
+ // such as LD_LIBRARY_PATH. The third argument specifies the splitter -\r
+ // the default value of PATH_SPLITTER is appropriate for environment variables.\r
+ std::string lookup (const std::string& file, const std::string& path, const std::string& splitter = PATH_SPLITTER);\r
+\r
+ // utility function for finding the folder that contains the current executable\r
+ // the argument is the argv[0] parameter passed to main\r
+ std::string install_path(const std::string& argv0);\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
+\r
+#endif\r
-////////////////////////////////////////////////////////////////////////////////
-
-// Author: Andy Rushton
-// Copyright: (c) Southampton University 1999-2004
-// (c) Andy Rushton 2004-2009
-// License: BSD License, see ../docs/license.html
-
-// The integer is represented as a sequence of bytes. They are stored such that
-// element 0 is the lsB, which makes sense when seen as an integer offset but
-// is counter-intuitive when you think that a string is usually read from left
-// to right, 0 to size-1, in which case the lsB is on the *left*.
-
-// This solution is compatible with 32-bit and 64-bit machines with either
-// little-endian or big-endian representations of integers.
-
-// Problem: I'm using std::string, which is an array of char. However, char is
-// not well-defined - it could be signed or unsigned.
-
-// In fact, there's no requirement for a char to even be one byte - it can be
-// any size of one byte or more. However, it's just impossible to make any
-// progress with that naffness (thanks to the C non-standardisation committee)
-// and the practice is that char on every platform/compiler I've ever come
-// across is that char = byte.
-
-// The algorithms here use unsigned char to represent bit-patterns so I have to
-// be careful to type-cast from char to unsigned char a lot. I use a typedef to
-// make life easier.
-
-////////////////////////////////////////////////////////////////////////////////
-#include "inf.hpp"
-#include <ctype.h>
-////////////////////////////////////////////////////////////////////////////////
-
-namespace stlplus
-{
-
- ////////////////////////////////////////////////////////////////////////////////
- // choose a sensible C type for a byte
-
- typedef unsigned char byte;
-
- ////////////////////////////////////////////////////////////////////////////////
- // local functions
-
- // removes leading bytes that don't contribute to the value to create the minimum string representation
- static void reduce_string(std::string& data)
- {
- while(data.size() > 1 &&
- ((byte(data[data.size()-1]) == byte(0) && byte(data[data.size()-2]) < byte(128)) ||
- (byte(data[data.size()-1]) == byte(255) && byte(data[data.size()-2]) >= byte(128))))
- {
- data.erase(data.end()-1);
- }
- }
-
- // generic implementations of type conversions from integer type to internal representation
- // data: integer value for conversion
- // result: internal representation
-
- template <typename T>
- static void convert_from_signed(const T& data, std::string& result)
- {
- result.erase();
- bool lsb_first = little_endian();
- byte* address = (byte*)&data;
- for (size_t i = 0; i < sizeof(T); i++)
- {
- size_t offset = (lsb_first ? i : (sizeof(T) - i - 1));
- result.append(1,address[offset]);
- }
- reduce_string(result);
- }
-
- template <typename T>
- static void convert_from_unsigned(const T& data, std::string& result)
- {
- result.erase();
- bool lsb_first = little_endian();
- byte* address = (byte*)&data;
- for (size_t i = 0; i < sizeof(T); i++)
- {
- size_t offset = (lsb_first ? i : (sizeof(T) - i - 1));
- result.append(1,address[offset]);
- }
- // inf is signed - so there is a possible extra sign bit to add
- result.append(1,std::string::value_type(0));
- reduce_string(result);
- }
-
- // generic implementations of type conversions from internal representation to an integer type
- // data : string representation of integer
- // result: integer result of conversion
- // return: flag indicating success - false = overflow
-
- template <class T>
- bool convert_to_signed(const std::string& data, T& result)
- {
- bool lsb_first = little_endian();
- byte* address = (byte*)&result;
- for (size_t i = 0; i < sizeof(T); i++)
- {
- size_t offset = lsb_first ? i : (sizeof(T) - i - 1);
- if (i < data.size())
- address[offset] = byte(data[i]);
- else if (data.empty() || (byte(data[data.size()-1]) < byte(128)))
- address[offset] = byte(0);
- else
- address[offset] = byte(255);
- }
- return data.size() <= sizeof(T);
- }
-
- template <class T>
- bool convert_to_unsigned(const std::string& data, T& result)
- {
- bool lsb_first = little_endian();
- byte* address = (byte*)&result;
- for (size_t i = 0; i < sizeof(T); i++)
- {
- size_t offset = lsb_first ? i : (sizeof(T) - i - 1);
- if (i < data.size())
- address[offset] = byte(data[i]);
- else
- address[offset] = byte(0);
- }
- return data.size() <= sizeof(T);
- }
-
- ////////////////////////////////////////////////////////////////////////////////
- // Conversions to string
-
- static char to_char [] = "0123456789abcdefghijklmnopqrstuvwxyz";
- static int from_char [] =
- {
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
- -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
- 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1,
- -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
- 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
- -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
- };
-
- static void convert_to_string(const stlplus::inf& data, std::string& result, unsigned radix = 10)
- throw(std::invalid_argument)
- {
- // only support the C-style radixes plus 0b for binary
- if (radix != 2 && radix != 8 && radix != 10 && radix != 16)
- throw std::invalid_argument("invalid radix value");
- inf local_i = data;
- // untangle all the options
- bool binary = radix == 2;
- bool octal = radix == 8;
- bool hex = radix == 16;
- // the C representations for binary, octal and hex use 2's-complement representation
- // all other represenations use sign-magnitude
- if (hex || octal || binary)
- {
- // bit-pattern representation
- // this is the binary representation optionally shown in octal or hex
- // first generate the binary by masking the bits
- for (unsigned j = local_i.bits(); j--; )
- result += (local_i.bit(j) ? '1' : '0');
- // the result is now the full width of the type - e.g. int will give a 32-bit result
- // now interpret this as either binary, octal or hex and add the prefix
- if (binary)
- {
- // trim down to the smallest string that preserves the value
- while (true)
- {
- // do not trim to less than 1 bit (sign only)
- if (result.size() <= 1) break;
- // only trim if it doesn't change the sign and therefore the value
- if (result[0] != result[1]) break;
- result.erase(0,1);
- }
- // add the prefix
- result.insert((std::string::size_type)0, "0b");
- }
- else if (octal)
- {
- // the result is currently binary
- // trim down to the smallest string that preserves the value
- while (true)
- {
- // do not trim to less than 2 bits (sign plus 1-bit magnitude)
- if (result.size() <= 2) break;
- // only trim if it doesn't change the sign and therefore the value
- if (result[0] != result[1]) break;
- result.erase(0,1);
- }
- // also ensure that the binary is a multiple of 3 bits to make the conversion to octal easier
- while (result.size() % 3 != 0)
- result.insert((std::string::size_type)0, 1, result[0]);
- // now convert to octal
- std::string octal_result;
- for (unsigned i = 0; i < result.size()/3; i++)
- {
- // yuck - ugly or what?
- if (result[i*3] == '0')
- {
- if (result[i*3+1] == '0')
- {
- if (result[i*3+2] == '0')
- octal_result += '0';
- else
- octal_result += '1';
- }
- else
- {
- if (result[i*3+2] == '0')
- octal_result += '2';
- else
- octal_result += '3';
- }
- }
- else
- {
- if (result[i*3+1] == '0')
- {
- if (result[i*3+2] == '0')
- octal_result += '4';
- else
- octal_result += '5';
- }
- else
- {
- if (result[i*3+2] == '0')
- octal_result += '6';
- else
- octal_result += '7';
- }
- }
- }
- result = octal_result;
- // add the prefix
- result.insert((std::string::size_type)0, "0");
- }
- else
- {
- // similar to octal
- while (true)
- {
- // do not trim to less than 2 bits (sign plus 1-bit magnitude)
- if (result.size() <= 2) break;
- // only trim if it doesn't change the sign and therefore the value
- if (result[0] != result[1]) break;
- result.erase(0,1);
- }
- // pad to a multiple of 4 characters
- while (result.size() % 4 != 0)
- result.insert((std::string::size_type)0, 1, result[0]);
- // now convert to hex
- std::string hex_result;
- for (unsigned i = 0; i < result.size()/4; i++)
- {
- // yuck - ugly or what?
- if (result[i*4] == '0')
- {
- if (result[i*4+1] == '0')
- {
- if (result[i*4+2] == '0')
- {
- if (result[i*4+3] == '0')
- hex_result += '0';
- else
- hex_result += '1';
- }
- else
- {
- if (result[i*4+3] == '0')
- hex_result += '2';
- else
- hex_result += '3';
- }
- }
- else
- {
- if (result[i*4+2] == '0')
- {
- if (result[i*4+3] == '0')
- hex_result += '4';
- else
- hex_result += '5';
- }
- else
- {
- if (result[i*4+3] == '0')
- hex_result += '6';
- else
- hex_result += '7';
- }
- }
- }
- else
- {
- if (result[i*4+1] == '0')
- {
- if (result[i*4+2] == '0')
- {
- if (result[i*4+3] == '0')
- hex_result += '8';
- else
- hex_result += '9';
- }
- else
- {
- if (result[i*4+3] == '0')
- hex_result += 'a';
- else
- hex_result += 'b';
- }
- }
- else
- {
- if (result[i*4+2] == '0')
- {
- if (result[i*4+3] == '0')
- hex_result += 'c';
- else
- hex_result += 'd';
- }
- else
- {
- if (result[i*4+3] == '0')
- hex_result += 'e';
- else
- hex_result += 'f';
- }
- }
- }
- }
- result = hex_result;
- // add the prefix
- result.insert((std::string::size_type)0, "0x");
- }
- }
- else
- {
- // convert to sign-magnitude
- // the representation is:
- // [sign]magnitude
- bool negative = local_i.negative();
- local_i.abs();
- // create a representation of the magnitude by successive division
- inf inf_radix(radix);
- do
- {
- std::pair<inf,inf> divided = local_i.divide(inf_radix);
- unsigned remainder = divided.second.to_unsigned();
- char digit = to_char[remainder];
- result.insert((std::string::size_type)0, 1, digit);
- local_i = divided.first;
- }
- while(!local_i.zero());
- // add the prefixes
- // add a sign only for negative values
- if (negative)
- result.insert((std::string::size_type)0, 1, '-');
- }
- }
-
- ////////////////////////////////////////////////////////////////////////////////
- // Conversions FROM string
-
- void convert_from_string(const std::string& str, inf& result, unsigned radix = 10) throw(std::invalid_argument)
- {
- result = 0;
- // only support the C-style radixes plus 0b for binary
- // a radix of 0 means deduce the radix from the input - assume 10
- if (radix != 0 && radix != 2 && radix != 8 && radix != 10 && radix != 16)
- throw std::invalid_argument("invalid radix value");
- unsigned i = 0;
- // the radix passed as a parameter is just the default - it can be
- // overridden by the C prefix
- // Note: a leading zero is the C-style prefix for octal - I only make this
- // override the default when the default radix is not specified
- // first check for a C-style prefix
- bool c_style = false;
- if (i < str.size() && str[i] == '0')
- {
- // binary or hex
- if (i+1 < str.size() && tolower(str[i+1]) == 'x')
- {
- c_style = true;
- radix = 16;
- i += 2;
- }
- else if (i+1 < str.size() && tolower(str[i+1]) == 'b')
- {
- c_style = true;
- radix = 2;
- i += 2;
- }
- else if (radix == 0)
- {
- c_style = true;
- radix = 8;
- i += 1;
- }
- }
- if (radix == 0)
- radix = 10;
- if (c_style)
- {
- // the C style formats are bit patterns not integer values - these need
- // to be sign-extended to get the right value
- std::string binary;
- if (radix == 2)
- {
- for (unsigned j = i; j < str.size(); j++)
- {
- switch(str[j])
- {
- case '0':
- binary += '0';
- break;
- case '1':
- binary += '1';
- break;
- default:
- throw std::invalid_argument("invalid binary character in string " + str);
- }
- }
- }
- else if (radix == 8)
- {
- for (unsigned j = i; j < str.size(); j++)
- {
- switch(str[j])
- {
- case '0':
- binary += "000";
- break;
- case '1':
- binary += "001";
- break;
- case '2':
- binary += "010";
- break;
- case '3':
- binary += "011";
- break;
- case '4':
- binary += "100";
- break;
- case '5':
- binary += "101";
- break;
- case '6':
- binary += "110";
- break;
- case '7':
- binary += "111";
- break;
- default:
- throw std::invalid_argument("invalid octal character in string " + str);
- }
- }
- }
- else
- {
- for (unsigned j = i; j < str.size(); j++)
- {
- switch(tolower(str[j]))
- {
- case '0':
- binary += "0000";
- break;
- case '1':
- binary += "0001";
- break;
- case '2':
- binary += "0010";
- break;
- case '3':
- binary += "0011";
- break;
- case '4':
- binary += "0100";
- break;
- case '5':
- binary += "0101";
- break;
- case '6':
- binary += "0110";
- break;
- case '7':
- binary += "0111";
- break;
- case '8':
- binary += "1000";
- break;
- case '9':
- binary += "1001";
- break;
- case 'a':
- binary += "1010";
- break;
- case 'b':
- binary += "1011";
- break;
- case 'c':
- binary += "1100";
- break;
- case 'd':
- binary += "1101";
- break;
- case 'e':
- binary += "1110";
- break;
- case 'f':
- binary += "1111";
- break;
- default:
- throw std::invalid_argument("invalid hex character in string " + str);
- }
- }
- }
- // now convert the value
- result.resize(binary.size());
- for (unsigned j = 0; j < binary.size(); j++)
- result.preset(binary.size() - j - 1, binary[j] == '1');
- }
- else
- {
- // sign-magnitude representation
- // now scan for a sign and find whether this is a negative number
- bool negative = false;
- if (i < str.size())
- {
- switch (str[i])
- {
- case '-':
- negative = true;
- i++;
- break;
- case '+':
- i++;
- break;
- }
- }
- for (; i < str.size(); i++)
- {
- result *= inf(radix);
- unsigned char ascii = (unsigned char)str[i];
- int ch = from_char[ascii] ;
- if (ch == -1)
- throw std::invalid_argument("invalid decimal character in string " + str);
- result += inf(ch);
- }
- if (negative)
- result.negate();
- }
- }
-
- ////////////////////////////////////////////////////////////////////////////////
- // constructors - mostly implemented in terms of the assignment operators
-
- inf::inf(void)
- {
- // void constructor initialises to zero - represented as a single-byte value containing zero
- m_data.append(1,std::string::value_type(0));
- }
-
- inf::inf(short r)
- {
- operator=(r);
- }
-
- inf::inf(unsigned short r)
- {
- operator=(r);
- }
-
- inf::inf(int r)
- {
- operator=(r);
- }
-
- inf::inf(unsigned r)
- {
- operator=(r);
- }
-
- inf::inf(long r)
- {
- operator=(r);
- }
-
- inf::inf(unsigned long r)
- {
- operator=(r);
- }
-
- inf::inf (const std::string& r) throw(std::invalid_argument)
- {
- operator=(r);
- }
-
- inf::inf(const inf& r)
- {
-#ifdef __BORLANDC__
- // work round bug in Borland compiler - copy constructor fails if string
- // contains null characters, so do my own copy
- for (unsigned i = 0; i < r.m_data.size(); i++)
- m_data += r.m_data[i];
-#else
- m_data = r.m_data;
-#endif
- }
-
- ////////////////////////////////////////////////////////////////////////////////
-
- inf::~inf(void)
- {
- }
-
- ////////////////////////////////////////////////////////////////////////////////
- // assignments convert from iteger types to internal representation
-
- inf& inf::operator = (short r)
- {
- convert_from_signed(r, m_data);
- return *this;
- }
-
- inf& inf::operator = (unsigned short r)
- {
- convert_from_unsigned(r, m_data);
- return *this;
- }
-
- inf& inf::operator = (int r)
- {
- convert_from_signed(r, m_data);
- return *this;
- }
-
- inf& inf::operator = (unsigned r)
- {
- convert_from_unsigned(r, m_data);
- return *this;
- }
-
- inf& inf::operator = (long r)
- {
- convert_from_signed(r, m_data);
- return *this;
- }
-
- inf& inf::operator = (unsigned long r)
- {
- convert_from_unsigned(r, m_data);
- return *this;
- }
-
- inf& inf::operator = (const std::string& r) throw(std::invalid_argument)
- {
- convert_from_string(r, *this);
- return *this;
- }
-
- inf& inf::operator = (const inf& r)
- {
- m_data = r.m_data;
- return *this;
- }
-
- ////////////////////////////////////////////////////////////////////////////////
-
- short inf::to_short(bool truncate) const throw(std::overflow_error)
- {
- short result = 0;
- if (!convert_to_signed(m_data, result))
- if (!truncate)
- throw std::overflow_error("stlplus::inf::to_short");
- return result;
- }
-
- unsigned short inf::to_unsigned_short(bool truncate) const throw(std::overflow_error)
- {
- unsigned short result = 0;
- if (!convert_to_unsigned(m_data, result))
- if (!truncate)
- throw std::overflow_error("stlplus::inf::to_unsigned_short");
- return result;
- }
-
- int inf::to_int(bool truncate) const throw(std::overflow_error)
- {
- int result = 0;
- if (!convert_to_signed(m_data, result))
- if (!truncate)
- throw std::overflow_error("stlplus::inf::to_int");
- return result;
- }
-
- unsigned inf::to_unsigned(bool truncate) const throw(std::overflow_error)
- {
- unsigned result = 0;
- if (!convert_to_unsigned(m_data, result))
- if (!truncate)
- throw std::overflow_error("stlplus::inf::to_unsigned");
- return result;
- }
-
- long inf::to_long(bool truncate) const throw(std::overflow_error)
- {
- long result = 0;
- if (!convert_to_signed(m_data, result))
- if (!truncate)
- throw std::overflow_error("stlplus::inf::to_long");
- return result;
- }
-
- unsigned long inf::to_unsigned_long(bool truncate) const throw(std::overflow_error)
- {
- unsigned long result = 0;
- if (!convert_to_unsigned(m_data, result))
- if (!truncate)
- throw std::overflow_error("stlplus::inf::to_unsigned_long");
- return result;
- }
-
- ////////////////////////////////////////////////////////////////////////////////
- // resize the inf regardless of the data
-
- void inf::resize(unsigned bits)
- {
- if (bits == 0) bits = 1;
- unsigned bytes = (bits+7)/8;
- byte extend = negative() ? byte(255) : byte (0);
- while(bytes > m_data.size())
- m_data.append(1,extend);
- }
-
- // reduce the bit count to the minimum needed to preserve the value
-
- void inf::reduce(void)
- {
- reduce_string(m_data);
- }
-
- ////////////////////////////////////////////////////////////////////////////////
- // the number of significant bits in the number
-
- unsigned inf::bits (void) const
- {
- // The number of significant bits in the integer value - this is the number
- // of indexable bits less any redundant sign bits at the msb
- // This does not assume that the inf has been reduced to its minimum form
- unsigned result = indexable_bits();
- bool sign = bit(result-1);
- while (result > 1 && (sign == bit(result-2)))
- result--;
- return result;
- }
-
- unsigned inf::size(void) const
- {
- return bits();
- }
-
- unsigned inf::indexable_bits (void) const
- {
- return 8 * unsigned(m_data.size());
- }
-
- ////////////////////////////////////////////////////////////////////////////////
- // bitwise operations
-
- bool inf::bit (unsigned index) const throw(std::out_of_range)
- {
- if (index >= indexable_bits())
- throw std::out_of_range(std::string("stlplus::inf::bit"));
- // first split the offset into byte offset and bit offset
- unsigned byte_offset = index/8;
- unsigned bit_offset = index%8;
- return (byte(m_data[byte_offset]) & (byte(1) << bit_offset)) != 0;
- }
-
- bool inf::operator [] (unsigned index) const throw(std::out_of_range)
- {
- return bit(index);
- }
-
- void inf::set (unsigned index) throw(std::out_of_range)
- {
- if (index >= indexable_bits())
- throw std::out_of_range(std::string("stlplus::inf::set"));
- // first split the offset into byte offset and bit offset
- unsigned byte_offset = index/8;
- unsigned bit_offset = index%8;
- m_data[byte_offset] |= (byte(1) << bit_offset);
- }
-
- void inf::clear (unsigned index) throw(std::out_of_range)
- {
- if (index >= indexable_bits())
- throw std::out_of_range(std::string("stlplus::inf::clear"));
- // first split the offset into byte offset and bit offset
- unsigned byte_offset = index/8;
- unsigned bit_offset = index%8;
- m_data[byte_offset] &= (~(byte(1) << bit_offset));
- }
-
- void inf::preset (unsigned index, bool value) throw(std::out_of_range)
- {
- if (value)
- set(index);
- else
- clear(index);
- }
-
- inf inf::slice(unsigned low, unsigned high) const throw(std::out_of_range)
- {
- if (low >= indexable_bits())
- throw std::out_of_range(std::string("stlplus::inf::slice: low index"));
- if (high >= indexable_bits())
- throw std::out_of_range(std::string("stlplus::inf::slice: high index"));
- inf result;
- if (high >= low)
- {
- // create a result the right size and filled with sign bits
- std::string::size_type result_size = (high-low+1+7)/8;
- result.m_data.erase();
- byte extend = bit(high) ? byte(255) : byte (0);
- while (result.m_data.size() < result_size)
- result.m_data.append(1,extend);
- // now set the relevant bits
- for (unsigned i = low; i <= high; i++)
- result.preset(i-low, bit(i));
- }
- return result;
- }
-
- ////////////////////////////////////////////////////////////////////////////////
- // testing operations
-
- bool inf::negative (void) const
- {
- return bit(indexable_bits()-1);
- }
-
- bool inf::natural (void) const
- {
- return !negative();
- }
-
- bool inf::positive (void) const
- {
- return natural() && !zero();
- }
-
- bool inf::zero (void) const
- {
- for (std::string::size_type i = 0; i < m_data.size(); i++)
- if (m_data[i] != 0)
- return false;
- return true;
- }
-
- bool inf::non_zero (void) const
- {
- return !zero();
- }
-
- bool inf::operator ! (void) const
- {
- return zero();
- }
-
- ////////////////////////////////////////////////////////////////////////////////
- // comparison operators
-
- bool inf::operator == (const inf& r) const
- {
- // Two infs are equal if they are numerically equal, even if they are
- // different sizes (i.e. they could be non-reduced values).
- // This makes life a little more complicated than if I could assume that values were reduced.
- byte l_extend = negative() ? byte(255) : byte (0);
- byte r_extend = r.negative() ? byte(255) : byte (0);
- std::string::size_type bytes = maximum(m_data.size(),r.m_data.size());
- for (std::string::size_type i = bytes; i--; )
- {
- byte l_byte = (i < m_data.size() ? byte(m_data[i]) : l_extend);
- byte r_byte = (i < r.m_data.size() ? byte(r.m_data[i]) : r_extend);
- if (l_byte != r_byte)
- return false;
- }
- return true;
- }
-
- bool inf::operator != (const inf& r) const
- {
- return !operator==(r);
- }
-
- bool inf::operator < (const inf& r) const
- {
- // This could be implemented in terms of subtraction. However, it can be
- // simplified since there is no need to calculate the accurate difference,
- // just the direction of the difference. I compare from msB down and as
- // soon as a byte difference is found, that defines the ordering. The
- // problem is that in 2's-complement, all negative values are greater than
- // all natural values if you just do a straight unsigned comparison. I
- // handle this by doing a preliminary test for different signs.
-
- // For example, a 3-bit signed type has the coding:
- // 000 = 0
- // ...
- // 011 = 3
- // 100 = -4
- // ...
- // 111 = -1
-
- // So, for natural values, the ordering of the integer values is the
- // ordering of the bit patterns. Similarly, for negative values, the
- // ordering of the integer values is the ordering of the bit patterns
- // However, the bit patterns for the negative values are *greater than*
- // the natural values. This is a side-effect of the naffness of
- // 2's-complement representation
-
- // first handle the case of comparing two values with different signs
- bool l_sign = negative();
- bool r_sign = r.negative();
- if (l_sign != r_sign)
- {
- // one argument must be negative and the other natural
- // the left is less if it is the negative one
- return l_sign;
- }
- // the arguments are the same sign
- // so the ordering is a simple unsigned byte-by-byte comparison
- // However, this is complicated by the possibility that the values could be different lengths
- byte l_extend = l_sign ? byte(255) : byte (0);
- byte r_extend = r_sign ? byte(255) : byte (0);
- std::string::size_type bytes = maximum(m_data.size(),r.m_data.size());
- for (std::string::size_type i = bytes; i--; )
- {
- byte l_byte = (i < m_data.size() ? byte(m_data[i]) : l_extend);
- byte r_byte = (i < r.m_data.size() ? byte(r.m_data[i]) : r_extend);
- if (l_byte != r_byte)
- return l_byte < r_byte;
- }
- // if I get here, the two are equal, so that is not less-than
- return false;
- }
-
- bool inf::operator <= (const inf& r) const
- {
- return !(r < *this);
- }
-
- bool inf::operator > (const inf& r) const
- {
- return r < *this;
- }
-
- bool inf::operator >= (const inf& r) const
- {
- return !(*this < r);
- }
-
- ////////////////////////////////////////////////////////////////////////////////
- // logical operators
-
- inf& inf::invert (void)
- {
- for (std::string::size_type i = 0; i < m_data.size(); i++)
- m_data[i] = ~m_data[i];
- return *this;
- }
-
- inf inf::operator ~ (void) const
- {
- inf result(*this);
- result.invert();
- return result;
- }
-
- inf& inf::operator &= (const inf& r)
- {
- // bitwise AND is extended to the length of the largest argument
- byte l_extend = negative() ? byte(255) : byte (0);
- byte r_extend = r.negative() ? byte(255) : byte (0);
- std::string::size_type bytes = maximum(m_data.size(),r.m_data.size());
- for (std::string::size_type i = 0; i < bytes; i++)
- {
- byte l_byte = (i < m_data.size() ? byte(m_data[i]) : l_extend);
- byte r_byte = (i < r.m_data.size() ? byte(r.m_data[i]) : r_extend);
- byte result = l_byte & r_byte;
- if (i < m_data.size())
- m_data[i] = result;
- else
- m_data.append(1,result);
- }
- // now reduce the result
- reduce();
- return *this;
- }
-
- inf inf::operator & (const inf& r) const
- {
- inf result(*this);
- result &= r;
- return result;
- }
-
- inf& inf::operator |= (const inf& r)
- {
- // bitwise OR is extended to the length of the largest argument
- byte l_extend = negative() ? byte(255) : byte (0);
- byte r_extend = r.negative() ? byte(255) : byte (0);
- std::string::size_type bytes = maximum(m_data.size(),r.m_data.size());
- for (std::string::size_type i = 0; i < bytes; i++)
- {
- byte l_byte = (i < m_data.size() ? byte(m_data[i]) : l_extend);
- byte r_byte = (i < r.m_data.size() ? byte(r.m_data[i]) : r_extend);
- byte result = l_byte | r_byte;
- if (i < m_data.size())
- m_data[i] = result;
- else
- m_data.append(1,result);
- }
- // now reduce the result
- reduce();
- return *this;
- }
-
- inf inf::operator | (const inf& r) const
- {
- inf result(*this);
- result |= r;
- return result;
- }
-
- inf& inf::operator ^= (const inf& r)
- {
- // bitwise XOR is extended to the length of the largest argument
- byte l_extend = negative() ? byte(255) : byte (0);
- byte r_extend = r.negative() ? byte(255) : byte (0);
- std::string::size_type bytes = maximum(m_data.size(),r.m_data.size());
- for (std::string::size_type i = 0; i < bytes; i++)
- {
- byte l_byte = (i < m_data.size() ? byte(m_data[i]) : l_extend);
- byte r_byte = (i < r.m_data.size() ? byte(r.m_data[i]) : r_extend);
- byte result = l_byte ^ r_byte;
- if (i < m_data.size())
- m_data[i] = result;
- else
- m_data.append(1,result);
- }
- // now reduce the result
- reduce();
- return *this;
- }
-
- inf inf::operator ^ (const inf& r) const
- {
- inf result(*this);
- result ^= r;
- return result;
- }
-
- ////////////////////////////////////////////////////////////////////////////////
- // shift operators all preserve the value by increasing the word size
-
- inf& inf::operator <<= (unsigned shift)
- {
- // left shift is a shift towards the msb, with 0s being shifted in at the lsb
- // split this into a byte shift followed by a bit shift
-
- // first expand the value to be big enough for the result
- std::string::size_type new_size = (indexable_bits() + shift + 7) / 8;
- byte extend = negative() ? byte(255) : byte (0);
- while (m_data.size() < new_size)
- m_data.append(1,extend);
- // now do the byte shift
- unsigned byte_shift = shift/8;
- if (byte_shift > 0)
- {
- for (std::string::size_type b = new_size; b--; )
- m_data[b] = (b >= byte_shift) ? m_data[b-byte_shift] : byte(0);
- }
- // and finally the bit shift
- unsigned bit_shift = shift%8;
- if (bit_shift > 0)
- {
- for (std::string::size_type b = new_size; b--; )
- {
- byte current = byte(m_data[b]);
- byte previous = b > 0 ? m_data[b-1] : byte(0);
- m_data[b] = (current << bit_shift) | (previous >> (8 - bit_shift));
- }
- }
- // now reduce the result
- reduce();
- return *this;
- }
-
- inf inf::operator << (unsigned shift) const
- {
- inf result(*this);
- result <<= shift;
- return result;
- }
-
- inf& inf::operator >>= (unsigned shift)
- {
- // right shift is a shift towards the lsb, with sign bits being shifted in at the msb
- // split this into a byte shift followed by a bit shift
-
- // a byte of sign bits
- byte extend = negative() ? byte(255) : byte (0);
- // do the byte shift
- unsigned byte_shift = shift/8;
- if (byte_shift > 0)
- {
- for (std::string::size_type b = 0; b < m_data.size(); b++)
- m_data[b] = (b + byte_shift < m_data.size()) ? m_data[b+byte_shift] : extend;
- }
- // and finally the bit shift
- unsigned bit_shift = shift%8;
- if (bit_shift > 0)
- {
- for (std::string::size_type b = 0; b < m_data.size(); b++)
- {
- byte current = byte(m_data[b]);
- byte next = ((b+1) < m_data.size()) ? m_data[b+1] : extend;
- byte shifted = (current >> bit_shift) | (next << (8 - bit_shift));
- m_data[b] = shifted;
- }
- }
- // now reduce the result
- reduce();
- return *this;
- }
-
- inf inf::operator >> (unsigned shift) const
- {
- inf result(*this);
- result >>= shift;
- return result;
- }
-
- ////////////////////////////////////////////////////////////////////////////////
- // negation operators
-
- inf& inf::negate (void)
- {
- // do 2's-complement negation
- // equivalent to inversion plus one
- invert();
- operator += (inf(1));
- return *this;
- }
-
- inf inf::operator - (void) const
- {
- inf result(*this);
- result.negate();
- return result;
- }
-
- inf& inf::abs(void)
- {
- if (negative()) negate();
- return *this;
- }
-
- inf abs(const inf& i)
- {
- inf result = i;
- result.abs();
- return result;
- }
-
- ////////////////////////////////////////////////////////////////////////////////
- // addition operators
-
- inf& inf::operator += (const inf& r)
- {
- // do 2's-complement addition
- // Note that the addition can give a result that is larger than either argument
- byte carry = 0;
- std::string::size_type max_size = maximum(m_data.size(),r.m_data.size());
- byte l_extend = negative() ? byte(255) : byte (0);
- byte r_extend = r.negative() ? byte(255) : byte (0);
- for (std::string::size_type i = 0; i < max_size; i++)
- {
- byte l_byte = (i < m_data.size() ? byte(m_data[i]) : l_extend);
- byte r_byte = (i < r.m_data.size() ? byte(r.m_data[i]) : r_extend);
- // calculate the addition in a type that is bigger than a byte in order to catch the carry-out
- unsigned short result = ((unsigned short)(l_byte)) + ((unsigned short)(r_byte)) + carry;
- // now truncate the result to get the lsB
- if (i < m_data.size())
- m_data[i] = byte(result);
- else
- m_data.append(1,byte(result));
- // and capture the carry out by grabbing the second byte of the result
- carry = byte(result >> 8);
- }
- // if the result overflowed or underflowed, add an extra byte to catch it
- unsigned short result = ((unsigned short)(l_extend)) + ((unsigned short)(r_extend)) + carry;
- if (byte(result) != (negative() ? byte(255) : byte(0)))
- m_data.append(1,byte(result));
- // now reduce the result
- reduce();
- return *this;
- }
-
- inf inf::operator + (const inf& r) const
- {
- inf result(*this);
- result += r;
- return result;
- }
-
- ////////////////////////////////////////////////////////////////////////////////
- // subtraction operators
-
- inf& inf::operator -= (const inf& r)
- {
- // subtraction is defined in terms of negation and addition
- inf negated = -r;
- operator += (negated);
- return *this;
- }
-
- inf inf::operator - (const inf& r) const
- {
- inf result(*this);
- result -= r;
- return result;
- }
-
- ////////////////////////////////////////////////////////////////////////////////
- // multiplication operators
-
- inf& inf::operator *= (const inf& r)
- {
- // 2's complement multiplication
- // one day I'll do a more efficient version than this based on the underlying representation
- inf left(*this);
- inf right = r;
- // make the right value natural but preserve its sign for later
- bool right_negative = right.negative();
- right.abs();
- // implemented as a series of conditional additions
- operator = (0);
- // left.resize(right.bits() + left.bits() - 1);
- left <<= right.bits()-1;
- for (unsigned i = right.bits(); i--; )
- {
- if (right[i])
- operator += (left);
- left >>= 1;
- }
- if (right_negative)
- negate();
- // now reduce the result
- reduce();
- return *this;
- }
-
- inf inf::operator * (const inf& r) const
- {
- inf result(*this);
- result *= r;
- return result;
- }
-
- ////////////////////////////////////////////////////////////////////////////////
- // division and remainder operators
-
- std::pair<inf,inf> inf::divide(const inf& right) const throw(divide_by_zero)
- {
- if (right.zero())
- throw divide_by_zero("stlplus::inf::divide");
- inf numerator(*this);
- inf denominator = right;
- // make the numerator natural but preserve the sign for later
- bool numerator_negative = numerator.negative();
- numerator.abs();
- // same with the denominator
- bool denominator_negative = denominator.negative();
- denominator.abs();
- // the quotient and remainder will form the result
- // start with the quotiont zero and the remainder equal to the whole of the
- // numerator, then do trial subtraction from this
- inf quotient;
- inf remainder = numerator;
- // there's nothing more to do if the numerator is smaller than the denominator
- // but otherwise do the division
- if (numerator.bits() >= denominator.bits())
- {
- // make the quotient big enough to take the result
- quotient.resize(numerator.bits());
- // start with the numerator shifted to the far left
- unsigned shift = numerator.bits() - denominator.bits();
- denominator <<= shift;
- // do the division by repeated subtraction,
- for (unsigned i = shift+1; i--; )
- {
- if (remainder >= denominator)
- {
- remainder -= denominator;
- quotient.set(i);
- }
- denominator >>= 1;
- }
- }
- // now adjust the signs
- // x/(-y) == (-x)/y == -(x/y)
- if (numerator_negative != denominator_negative)
- quotient.negate();
- quotient.reduce();
- // x%(-y) == x%y and (-x)%y == -(x%y)
- if (numerator_negative)
- remainder.negate();
- remainder.reduce();
- return std::pair<inf,inf>(quotient,remainder);
- }
-
- inf& inf::operator /= (const inf& r) throw(divide_by_zero)
- {
- std::pair<inf,inf> result = divide(r);
- operator=(result.first);
- return *this;
- }
-
- inf inf::operator / (const inf& r) const throw(divide_by_zero)
- {
- std::pair<inf,inf> result = divide(r);
- return result.first;
- }
-
- inf& inf::operator %= (const inf& r) throw(divide_by_zero)
- {
- std::pair<inf,inf> result = divide(r);
- operator=(result.second);
- return *this;
- }
-
- inf inf::operator % (const inf& r) const throw(divide_by_zero)
- {
- std::pair<inf,inf> result = divide(r);
- return result.second;
- }
-
- ////////////////////////////////////////////////////////////////////////////////
- // prefix (void) and postfix (int) operators
-
- inf& inf::operator ++ (void)
- {
- operator += (inf(1));
- return *this;
- }
-
- inf inf::operator ++ (int)
- {
- inf old(*this);
- operator += (inf(1));
- return old;
- }
-
- inf& inf::operator -- (void)
- {
- operator -= (inf(1));
- return *this;
- }
-
- inf inf::operator -- (int)
- {
- inf old(*this);
- operator -= (inf(1));
- return old;
- }
-
- ////////////////////////////////////////////////////////////////////////////////
- // string representation and I/O routines
-
- std::string inf::to_string(unsigned radix) const
- throw(std::invalid_argument)
- {
- std::string result;
- convert_to_string(*this, result, radix);
- return result;
- }
-
- inf& inf::from_string(const std::string& value, unsigned radix)
- throw(std::invalid_argument)
- {
- convert_from_string(value, *this, radix);
- return *this;
- }
-
- std::ostream& operator << (std::ostream& str, const inf& i)
- {
- try
- {
- // get radix
- unsigned radix = 10;
- if (str.flags() & std::ios_base::oct)
- radix = 8;
- if (str.flags() & std::ios_base::hex)
- radix = 16;
- // the field width is handled by iostream, so I don't need to handle it as well
- // generate the string representation then print it
- str << i.to_string(radix);
- }
- catch(const std::invalid_argument)
- {
- str.setstate(std::ios_base::badbit);
- }
- return str;
- }
-
- std::istream& operator >> (std::istream& str, inf& i)
- {
- try
- {
- // get radix
- unsigned radix = 10;
- if (str.flags() & std::ios_base::oct)
- radix = 8;
- if (str.flags() & std::ios_base::hex)
- radix = 16;
- // now get the string image of the value
- std::string image;
- str >> image;
- // and convert to inf
- i.from_string(image, radix);
- }
- catch(const std::invalid_argument)
- {
- str.setstate(std::ios_base::badbit);
- }
- return str;
- }
-
- ////////////////////////////////////////////////////////////////////////////////
- // diagnostic dump
- // just convert to hex
-
- std::string inf::image_debug(void) const
- {
- // create this dump in the human-readable form, i.e. msB to the left
- std::string result = "0x";
- for (std::string::size_type i = m_data.size(); i--; )
- {
- byte current = m_data[i];
- byte msB = (current & byte(0xf0)) >> 4;
- result += to_char[msB];
- byte lsB = (current & byte(0x0f));
- result += to_char[lsB];
- }
- return result;
- }
-
- const std::string& inf::get_bytes(void) const
- {
- return m_data;
- }
-
- void inf::set_bytes(const std::string& data)
- {
- m_data = data;
- }
-
-} // end namespace stlplus
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+// Author: Andy Rushton\r
+// Copyright: (c) Southampton University 1999-2004\r
+// (c) Andy Rushton 2004 onwards\r
+// License: BSD License, see ../docs/license.html\r
+\r
+// The integer is represented as a sequence of bytes. They are stored such that\r
+// element 0 is the lsB, which makes sense when seen as an integer offset but\r
+// is counter-intuitive when you think that a string is usually read from left\r
+// to right, 0 to size-1, in which case the lsB is on the *left*.\r
+\r
+// This solution is compatible with 32-bit and 64-bit machines with either\r
+// little-endian or big-endian representations of integers.\r
+\r
+// Problem: I'm using std::string, which is an array of char. However, char is\r
+// not well-defined - it could be signed or unsigned.\r
+\r
+// In fact, there's no requirement for a char to even be one byte - it can be\r
+// any size of one byte or more. However, it's just impossible to make any\r
+// progress with that naffness (thanks to the C non-standardisation committee)\r
+// and the practice is that char on every platform/compiler I've ever come\r
+// across is that char = byte.\r
+\r
+// The algorithms here use unsigned char to represent bit-patterns so I have to\r
+// be careful to type-cast from char to unsigned char a lot. I use a typedef to\r
+// make life easier.\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "inf.hpp"\r
+#include <ctype.h>\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // choose a sensible C type for a byte\r
+\r
+ typedef unsigned char byte;\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // local functions\r
+\r
+ // removes leading bytes that don't contribute to the value to create the minimum string representation\r
+ static void reduce_string(std::string& data)\r
+ {\r
+ while(data.size() > 1 && \r
+ ((byte(data[data.size()-1]) == byte(0) && byte(data[data.size()-2]) < byte(128)) ||\r
+ (byte(data[data.size()-1]) == byte(255) && byte(data[data.size()-2]) >= byte(128))))\r
+ {\r
+ data.erase(data.end()-1);\r
+ }\r
+ }\r
+\r
+ // generic implementations of type conversions from integer type to internal representation\r
+ // data: integer value for conversion\r
+ // result: internal representation\r
+\r
+ template <typename T>\r
+ static void convert_from_signed(const T& data, std::string& result)\r
+ {\r
+ result.erase();\r
+ bool lsb_first = little_endian();\r
+ byte* address = (byte*)&data;\r
+ for (size_t i = 0; i < sizeof(T); i++)\r
+ {\r
+ size_t offset = (lsb_first ? i : (sizeof(T) - i - 1));\r
+ result.append(1,address[offset]);\r
+ }\r
+ reduce_string(result);\r
+ }\r
+\r
+ template <typename T>\r
+ static void convert_from_unsigned(const T& data, std::string& result)\r
+ {\r
+ result.erase();\r
+ bool lsb_first = little_endian();\r
+ byte* address = (byte*)&data;\r
+ for (size_t i = 0; i < sizeof(T); i++)\r
+ {\r
+ size_t offset = (lsb_first ? i : (sizeof(T) - i - 1));\r
+ result.append(1,address[offset]);\r
+ }\r
+ // inf is signed - so there is a possible extra sign bit to add\r
+ result.append(1,std::string::value_type(0));\r
+ reduce_string(result);\r
+ }\r
+\r
+ // generic implementations of type conversions from internal representation to an integer type\r
+ // data : string representation of integer\r
+ // result: integer result of conversion\r
+ // return: flag indicating success - false = overflow\r
+\r
+ template <class T>\r
+ bool convert_to_signed(const std::string& data, T& result)\r
+ {\r
+ bool lsb_first = little_endian();\r
+ byte* address = (byte*)&result;\r
+ for (size_t i = 0; i < sizeof(T); i++)\r
+ {\r
+ size_t offset = lsb_first ? i : (sizeof(T) - i - 1);\r
+ if (i < data.size())\r
+ address[offset] = byte(data[i]);\r
+ else if (data.empty() || (byte(data[data.size()-1]) < byte(128)))\r
+ address[offset] = byte(0);\r
+ else\r
+ address[offset] = byte(255);\r
+ }\r
+ return data.size() <= sizeof(T);\r
+ }\r
+\r
+ template <class T>\r
+ bool convert_to_unsigned(const std::string& data, T& result)\r
+ {\r
+ bool lsb_first = little_endian();\r
+ byte* address = (byte*)&result;\r
+ for (size_t i = 0; i < sizeof(T); i++)\r
+ {\r
+ size_t offset = lsb_first ? i : (sizeof(T) - i - 1);\r
+ if (i < data.size())\r
+ address[offset] = byte(data[i]);\r
+ else\r
+ address[offset] = byte(0);\r
+ }\r
+ return data.size() <= sizeof(T);\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // Conversions to string\r
+\r
+ static char to_char [] = "0123456789abcdefghijklmnopqrstuvwxyz";\r
+ static int from_char [] = \r
+ {\r
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\r
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\r
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\r
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,\r
+ -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,\r
+ 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1,\r
+ -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,\r
+ 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1,\r
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\r
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\r
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\r
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\r
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\r
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\r
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\r
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1\r
+ };\r
+\r
+ static void convert_to_string(const stlplus::inf& data, std::string& result, unsigned radix = 10)\r
+ throw(std::invalid_argument)\r
+ {\r
+ // only support the C-style radixes plus 0b for binary\r
+ if (radix != 2 && radix != 8 && radix != 10 && radix != 16)\r
+ throw std::invalid_argument("invalid radix value");\r
+ inf local_i = data;\r
+ // untangle all the options\r
+ bool binary = radix == 2;\r
+ bool octal = radix == 8;\r
+ bool hex = radix == 16;\r
+ // the C representations for binary, octal and hex use 2's-complement representation\r
+ // all other represenations use sign-magnitude\r
+ if (hex || octal || binary)\r
+ {\r
+ // bit-pattern representation\r
+ // this is the binary representation optionally shown in octal or hex\r
+ // first generate the binary by masking the bits\r
+ for (unsigned j = local_i.bits(); j--; )\r
+ result += (local_i.bit(j) ? '1' : '0');\r
+ // the result is now the full width of the type - e.g. int will give a 32-bit result\r
+ // now interpret this as either binary, octal or hex and add the prefix\r
+ if (binary)\r
+ {\r
+ // trim down to the smallest string that preserves the value\r
+ while (true)\r
+ {\r
+ // do not trim to less than 1 bit (sign only)\r
+ if (result.size() <= 1) break;\r
+ // only trim if it doesn't change the sign and therefore the value\r
+ if (result[0] != result[1]) break;\r
+ result.erase(0,1);\r
+ }\r
+ // add the prefix\r
+ result.insert((std::string::size_type)0, "0b");\r
+ }\r
+ else if (octal)\r
+ {\r
+ // the result is currently binary\r
+ // trim down to the smallest string that preserves the value\r
+ while (true)\r
+ {\r
+ // do not trim to less than 2 bits (sign plus 1-bit magnitude)\r
+ if (result.size() <= 2) break;\r
+ // only trim if it doesn't change the sign and therefore the value\r
+ if (result[0] != result[1]) break;\r
+ result.erase(0,1);\r
+ }\r
+ // also ensure that the binary is a multiple of 3 bits to make the conversion to octal easier\r
+ while (result.size() % 3 != 0)\r
+ result.insert((std::string::size_type)0, 1, result[0]);\r
+ // now convert to octal\r
+ std::string octal_result;\r
+ for (unsigned i = 0; i < result.size()/3; i++)\r
+ {\r
+ // yuck - ugly or what?\r
+ if (result[i*3] == '0')\r
+ {\r
+ if (result[i*3+1] == '0')\r
+ {\r
+ if (result[i*3+2] == '0')\r
+ octal_result += '0';\r
+ else\r
+ octal_result += '1';\r
+ }\r
+ else\r
+ {\r
+ if (result[i*3+2] == '0')\r
+ octal_result += '2';\r
+ else\r
+ octal_result += '3';\r
+ }\r
+ }\r
+ else\r
+ {\r
+ if (result[i*3+1] == '0')\r
+ {\r
+ if (result[i*3+2] == '0')\r
+ octal_result += '4';\r
+ else\r
+ octal_result += '5';\r
+ }\r
+ else\r
+ {\r
+ if (result[i*3+2] == '0')\r
+ octal_result += '6';\r
+ else\r
+ octal_result += '7';\r
+ }\r
+ }\r
+ }\r
+ result = octal_result;\r
+ // add the prefix\r
+ result.insert((std::string::size_type)0, "0");\r
+ }\r
+ else\r
+ {\r
+ // similar to octal\r
+ while (true)\r
+ {\r
+ // do not trim to less than 2 bits (sign plus 1-bit magnitude)\r
+ if (result.size() <= 2) break;\r
+ // only trim if it doesn't change the sign and therefore the value\r
+ if (result[0] != result[1]) break;\r
+ result.erase(0,1);\r
+ }\r
+ // pad to a multiple of 4 characters\r
+ while (result.size() % 4 != 0)\r
+ result.insert((std::string::size_type)0, 1, result[0]);\r
+ // now convert to hex\r
+ std::string hex_result;\r
+ for (unsigned i = 0; i < result.size()/4; i++)\r
+ {\r
+ // yuck - ugly or what?\r
+ if (result[i*4] == '0')\r
+ {\r
+ if (result[i*4+1] == '0')\r
+ {\r
+ if (result[i*4+2] == '0')\r
+ {\r
+ if (result[i*4+3] == '0')\r
+ hex_result += '0';\r
+ else\r
+ hex_result += '1';\r
+ }\r
+ else\r
+ {\r
+ if (result[i*4+3] == '0')\r
+ hex_result += '2';\r
+ else\r
+ hex_result += '3';\r
+ }\r
+ }\r
+ else\r
+ {\r
+ if (result[i*4+2] == '0')\r
+ {\r
+ if (result[i*4+3] == '0')\r
+ hex_result += '4';\r
+ else\r
+ hex_result += '5';\r
+ }\r
+ else\r
+ {\r
+ if (result[i*4+3] == '0')\r
+ hex_result += '6';\r
+ else\r
+ hex_result += '7';\r
+ }\r
+ }\r
+ }\r
+ else\r
+ {\r
+ if (result[i*4+1] == '0')\r
+ {\r
+ if (result[i*4+2] == '0')\r
+ {\r
+ if (result[i*4+3] == '0')\r
+ hex_result += '8';\r
+ else\r
+ hex_result += '9';\r
+ }\r
+ else\r
+ {\r
+ if (result[i*4+3] == '0')\r
+ hex_result += 'a';\r
+ else\r
+ hex_result += 'b';\r
+ }\r
+ }\r
+ else\r
+ {\r
+ if (result[i*4+2] == '0')\r
+ {\r
+ if (result[i*4+3] == '0')\r
+ hex_result += 'c';\r
+ else\r
+ hex_result += 'd';\r
+ }\r
+ else\r
+ {\r
+ if (result[i*4+3] == '0')\r
+ hex_result += 'e';\r
+ else\r
+ hex_result += 'f';\r
+ }\r
+ }\r
+ }\r
+ }\r
+ result = hex_result;\r
+ // add the prefix\r
+ result.insert((std::string::size_type)0, "0x");\r
+ }\r
+ }\r
+ else\r
+ {\r
+ // convert to sign-magnitude\r
+ // the representation is:\r
+ // [sign]magnitude\r
+ bool negative = local_i.negative();\r
+ local_i.abs();\r
+ // create a representation of the magnitude by successive division\r
+ inf inf_radix(radix);\r
+ do\r
+ {\r
+ std::pair<inf,inf> divided = local_i.divide(inf_radix);\r
+ unsigned remainder = divided.second.to_unsigned();\r
+ char digit = to_char[remainder];\r
+ result.insert((std::string::size_type)0, 1, digit);\r
+ local_i = divided.first;\r
+ }\r
+ while(!local_i.zero());\r
+ // add the prefixes\r
+ // add a sign only for negative values\r
+ if (negative)\r
+ result.insert((std::string::size_type)0, 1, '-');\r
+ }\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // Conversions FROM string\r
+\r
+ void convert_from_string(const std::string& str, inf& result, unsigned radix = 10) throw(std::invalid_argument)\r
+ {\r
+ result = 0;\r
+ // only support the C-style radixes plus 0b for binary\r
+ // a radix of 0 means deduce the radix from the input - assume 10\r
+ if (radix != 0 && radix != 2 && radix != 8 && radix != 10 && radix != 16)\r
+ throw std::invalid_argument("invalid radix value");\r
+ unsigned i = 0;\r
+ // the radix passed as a parameter is just the default - it can be\r
+ // overridden by the C prefix\r
+ // Note: a leading zero is the C-style prefix for octal - I only make this\r
+ // override the default when the default radix is not specified\r
+ // first check for a C-style prefix\r
+ bool c_style = false;\r
+ if (i < str.size() && str[i] == '0')\r
+ {\r
+ // binary or hex\r
+ if (i+1 < str.size() && tolower(str[i+1]) == 'x')\r
+ {\r
+ c_style = true;\r
+ radix = 16;\r
+ i += 2;\r
+ }\r
+ else if (i+1 < str.size() && tolower(str[i+1]) == 'b')\r
+ {\r
+ c_style = true;\r
+ radix = 2;\r
+ i += 2;\r
+ }\r
+ else if (radix == 0)\r
+ {\r
+ c_style = true;\r
+ radix = 8;\r
+ i += 1;\r
+ }\r
+ }\r
+ if (radix == 0)\r
+ radix = 10;\r
+ if (c_style)\r
+ {\r
+ // the C style formats are bit patterns not integer values - these need\r
+ // to be sign-extended to get the right value\r
+ std::string binary;\r
+ if (radix == 2)\r
+ {\r
+ for (unsigned j = i; j < str.size(); j++)\r
+ {\r
+ switch(str[j])\r
+ {\r
+ case '0':\r
+ binary += '0';\r
+ break;\r
+ case '1':\r
+ binary += '1';\r
+ break;\r
+ default:\r
+ throw std::invalid_argument("invalid binary character in string " + str);\r
+ }\r
+ }\r
+ }\r
+ else if (radix == 8)\r
+ {\r
+ for (unsigned j = i; j < str.size(); j++)\r
+ {\r
+ switch(str[j])\r
+ {\r
+ case '0':\r
+ binary += "000";\r
+ break;\r
+ case '1':\r
+ binary += "001";\r
+ break;\r
+ case '2':\r
+ binary += "010";\r
+ break;\r
+ case '3':\r
+ binary += "011";\r
+ break;\r
+ case '4':\r
+ binary += "100";\r
+ break;\r
+ case '5':\r
+ binary += "101";\r
+ break;\r
+ case '6':\r
+ binary += "110";\r
+ break;\r
+ case '7':\r
+ binary += "111";\r
+ break;\r
+ default:\r
+ throw std::invalid_argument("invalid octal character in string " + str);\r
+ }\r
+ }\r
+ }\r
+ else\r
+ {\r
+ for (unsigned j = i; j < str.size(); j++)\r
+ {\r
+ switch(tolower(str[j]))\r
+ {\r
+ case '0':\r
+ binary += "0000";\r
+ break;\r
+ case '1':\r
+ binary += "0001";\r
+ break;\r
+ case '2':\r
+ binary += "0010";\r
+ break;\r
+ case '3':\r
+ binary += "0011";\r
+ break;\r
+ case '4':\r
+ binary += "0100";\r
+ break;\r
+ case '5':\r
+ binary += "0101";\r
+ break;\r
+ case '6':\r
+ binary += "0110";\r
+ break;\r
+ case '7':\r
+ binary += "0111";\r
+ break;\r
+ case '8':\r
+ binary += "1000";\r
+ break;\r
+ case '9':\r
+ binary += "1001";\r
+ break;\r
+ case 'a':\r
+ binary += "1010";\r
+ break;\r
+ case 'b':\r
+ binary += "1011";\r
+ break;\r
+ case 'c':\r
+ binary += "1100";\r
+ break;\r
+ case 'd':\r
+ binary += "1101";\r
+ break;\r
+ case 'e':\r
+ binary += "1110";\r
+ break;\r
+ case 'f':\r
+ binary += "1111";\r
+ break;\r
+ default:\r
+ throw std::invalid_argument("invalid hex character in string " + str);\r
+ }\r
+ }\r
+ }\r
+ // now convert the value\r
+ result.resize(binary.size());\r
+ for (unsigned j = 0; j < binary.size(); j++)\r
+ result.preset(binary.size() - j - 1, binary[j] == '1');\r
+ }\r
+ else\r
+ {\r
+ // sign-magnitude representation\r
+ // now scan for a sign and find whether this is a negative number\r
+ bool negative = false;\r
+ if (i < str.size())\r
+ {\r
+ switch (str[i])\r
+ {\r
+ case '-':\r
+ negative = true;\r
+ i++;\r
+ break;\r
+ case '+':\r
+ i++;\r
+ break;\r
+ }\r
+ }\r
+ for (; i < str.size(); i++)\r
+ {\r
+ result *= inf(radix);\r
+ unsigned char ascii = (unsigned char)str[i];\r
+ int ch = from_char[ascii] ;\r
+ if (ch == -1)\r
+ throw std::invalid_argument("invalid decimal character in string " + str);\r
+ result += inf(ch);\r
+ }\r
+ if (negative)\r
+ result.negate();\r
+ }\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // constructors - mostly implemented in terms of the assignment operators\r
+\r
+ inf::inf(void)\r
+ {\r
+ // void constructor initialises to zero - represented as a single-byte value containing zero\r
+ m_data.append(1,std::string::value_type(0));\r
+ }\r
+\r
+ inf::inf(short r)\r
+ {\r
+ operator=(r);\r
+ }\r
+\r
+ inf::inf(unsigned short r)\r
+ {\r
+ operator=(r);\r
+ }\r
+\r
+ inf::inf(int r)\r
+ {\r
+ operator=(r);\r
+ }\r
+\r
+ inf::inf(unsigned r)\r
+ {\r
+ operator=(r);\r
+ }\r
+\r
+ inf::inf(long r)\r
+ {\r
+ operator=(r);\r
+ }\r
+\r
+ inf::inf(unsigned long r)\r
+ {\r
+ operator=(r);\r
+ }\r
+\r
+ inf::inf (const std::string& r) throw(std::invalid_argument)\r
+ {\r
+ operator=(r);\r
+ }\r
+\r
+ inf::inf(const inf& r)\r
+ {\r
+#ifdef __BORLANDC__\r
+ // work round bug in Borland compiler - copy constructor fails if string\r
+ // contains null characters, so do my own copy\r
+ for (unsigned i = 0; i < r.m_data.size(); i++)\r
+ m_data += r.m_data[i];\r
+#else\r
+ m_data = r.m_data;\r
+#endif\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+\r
+ inf::~inf(void)\r
+ {\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // assignments convert from iteger types to internal representation\r
+\r
+ inf& inf::operator = (short r)\r
+ {\r
+ convert_from_signed(r, m_data);\r
+ return *this;\r
+ }\r
+\r
+ inf& inf::operator = (unsigned short r)\r
+ {\r
+ convert_from_unsigned(r, m_data);\r
+ return *this;\r
+ }\r
+\r
+ inf& inf::operator = (int r)\r
+ {\r
+ convert_from_signed(r, m_data);\r
+ return *this;\r
+ }\r
+\r
+ inf& inf::operator = (unsigned r)\r
+ {\r
+ convert_from_unsigned(r, m_data);\r
+ return *this;\r
+ }\r
+\r
+ inf& inf::operator = (long r)\r
+ {\r
+ convert_from_signed(r, m_data);\r
+ return *this;\r
+ }\r
+\r
+ inf& inf::operator = (unsigned long r)\r
+ {\r
+ convert_from_unsigned(r, m_data);\r
+ return *this;\r
+ }\r
+\r
+ inf& inf::operator = (const std::string& r) throw(std::invalid_argument)\r
+ {\r
+ convert_from_string(r, *this);\r
+ return *this;\r
+ }\r
+\r
+ inf& inf::operator = (const inf& r)\r
+ {\r
+ m_data = r.m_data;\r
+ return *this;\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+\r
+ short inf::to_short(bool truncate) const throw(std::overflow_error)\r
+ {\r
+ short result = 0;\r
+ if (!convert_to_signed(m_data, result))\r
+ if (!truncate)\r
+ throw std::overflow_error("stlplus::inf::to_short");\r
+ return result;\r
+ }\r
+\r
+ unsigned short inf::to_unsigned_short(bool truncate) const throw(std::overflow_error)\r
+ {\r
+ unsigned short result = 0;\r
+ if (!convert_to_unsigned(m_data, result))\r
+ if (!truncate)\r
+ throw std::overflow_error("stlplus::inf::to_unsigned_short");\r
+ return result;\r
+ }\r
+\r
+ int inf::to_int(bool truncate) const throw(std::overflow_error)\r
+ {\r
+ int result = 0;\r
+ if (!convert_to_signed(m_data, result))\r
+ if (!truncate)\r
+ throw std::overflow_error("stlplus::inf::to_int");\r
+ return result;\r
+ }\r
+\r
+ unsigned inf::to_unsigned(bool truncate) const throw(std::overflow_error)\r
+ {\r
+ unsigned result = 0;\r
+ if (!convert_to_unsigned(m_data, result))\r
+ if (!truncate)\r
+ throw std::overflow_error("stlplus::inf::to_unsigned");\r
+ return result;\r
+ }\r
+\r
+ long inf::to_long(bool truncate) const throw(std::overflow_error)\r
+ {\r
+ long result = 0;\r
+ if (!convert_to_signed(m_data, result))\r
+ if (!truncate)\r
+ throw std::overflow_error("stlplus::inf::to_long");\r
+ return result;\r
+ }\r
+\r
+ unsigned long inf::to_unsigned_long(bool truncate) const throw(std::overflow_error)\r
+ {\r
+ unsigned long result = 0;\r
+ if (!convert_to_unsigned(m_data, result))\r
+ if (!truncate)\r
+ throw std::overflow_error("stlplus::inf::to_unsigned_long");\r
+ return result;\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // resize the inf regardless of the data\r
+\r
+ void inf::resize(unsigned bits)\r
+ {\r
+ if (bits == 0) bits = 1;\r
+ unsigned bytes = (bits+7)/8;\r
+ byte extend = negative() ? byte(255) : byte (0);\r
+ while(bytes > m_data.size())\r
+ m_data.append(1,extend);\r
+ }\r
+\r
+ // reduce the bit count to the minimum needed to preserve the value\r
+\r
+ void inf::reduce(void)\r
+ {\r
+ reduce_string(m_data);\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // the number of significant bits in the number\r
+\r
+ unsigned inf::bits (void) const\r
+ {\r
+ // The number of significant bits in the integer value - this is the number\r
+ // of indexable bits less any redundant sign bits at the msb\r
+ // This does not assume that the inf has been reduced to its minimum form\r
+ unsigned result = indexable_bits();\r
+ bool sign = bit(result-1);\r
+ while (result > 1 && (sign == bit(result-2)))\r
+ result--;\r
+ return result;\r
+ }\r
+\r
+ unsigned inf::size(void) const\r
+ {\r
+ return bits();\r
+ }\r
+\r
+ unsigned inf::indexable_bits (void) const\r
+ {\r
+ return 8 * unsigned(m_data.size());\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // bitwise operations\r
+\r
+ bool inf::bit (unsigned index) const throw(std::out_of_range)\r
+ {\r
+ if (index >= indexable_bits())\r
+ throw std::out_of_range(std::string("stlplus::inf::bit"));\r
+ // first split the offset into byte offset and bit offset\r
+ unsigned byte_offset = index/8;\r
+ unsigned bit_offset = index%8;\r
+ return (byte(m_data[byte_offset]) & (byte(1) << bit_offset)) != 0;\r
+ }\r
+\r
+ bool inf::operator [] (unsigned index) const throw(std::out_of_range)\r
+ {\r
+ return bit(index);\r
+ }\r
+\r
+ void inf::set (unsigned index) throw(std::out_of_range)\r
+ {\r
+ if (index >= indexable_bits())\r
+ throw std::out_of_range(std::string("stlplus::inf::set"));\r
+ // first split the offset into byte offset and bit offset\r
+ unsigned byte_offset = index/8;\r
+ unsigned bit_offset = index%8;\r
+ m_data[byte_offset] |= (byte(1) << bit_offset);\r
+ }\r
+\r
+ void inf::clear (unsigned index) throw(std::out_of_range)\r
+ {\r
+ if (index >= indexable_bits())\r
+ throw std::out_of_range(std::string("stlplus::inf::clear"));\r
+ // first split the offset into byte offset and bit offset\r
+ unsigned byte_offset = index/8;\r
+ unsigned bit_offset = index%8;\r
+ m_data[byte_offset] &= (~(byte(1) << bit_offset));\r
+ }\r
+\r
+ void inf::preset (unsigned index, bool value) throw(std::out_of_range)\r
+ {\r
+ if (value)\r
+ set(index);\r
+ else\r
+ clear(index);\r
+ }\r
+\r
+ inf inf::slice(unsigned low, unsigned high) const throw(std::out_of_range)\r
+ {\r
+ if (low >= indexable_bits())\r
+ throw std::out_of_range(std::string("stlplus::inf::slice: low index"));\r
+ if (high >= indexable_bits())\r
+ throw std::out_of_range(std::string("stlplus::inf::slice: high index"));\r
+ inf result;\r
+ if (high >= low)\r
+ {\r
+ // create a result the right size and filled with sign bits\r
+ std::string::size_type result_size = (high-low+1+7)/8;\r
+ result.m_data.erase();\r
+ byte extend = bit(high) ? byte(255) : byte (0);\r
+ while (result.m_data.size() < result_size)\r
+ result.m_data.append(1,extend);\r
+ // now set the relevant bits\r
+ for (unsigned i = low; i <= high; i++)\r
+ result.preset(i-low, bit(i));\r
+ }\r
+ return result;\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // testing operations\r
+\r
+ bool inf::negative (void) const\r
+ {\r
+ return bit(indexable_bits()-1);\r
+ }\r
+\r
+ bool inf::natural (void) const\r
+ {\r
+ return !negative();\r
+ }\r
+\r
+ bool inf::positive (void) const\r
+ {\r
+ return natural() && !zero();\r
+ }\r
+\r
+ bool inf::zero (void) const\r
+ {\r
+ for (std::string::size_type i = 0; i < m_data.size(); i++)\r
+ if (m_data[i] != 0)\r
+ return false;\r
+ return true;\r
+ }\r
+\r
+ bool inf::non_zero (void) const\r
+ {\r
+ return !zero();\r
+ }\r
+\r
+ bool inf::operator ! (void) const\r
+ {\r
+ return zero();\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // comparison operators\r
+\r
+ bool inf::operator == (const inf& r) const\r
+ {\r
+ // Two infs are equal if they are numerically equal, even if they are\r
+ // different sizes (i.e. they could be non-reduced values).\r
+ // This makes life a little more complicated than if I could assume that values were reduced.\r
+ byte l_extend = negative() ? byte(255) : byte (0);\r
+ byte r_extend = r.negative() ? byte(255) : byte (0);\r
+ std::string::size_type bytes = maximum(m_data.size(),r.m_data.size());\r
+ for (std::string::size_type i = bytes; i--; )\r
+ {\r
+ byte l_byte = (i < m_data.size() ? byte(m_data[i]) : l_extend);\r
+ byte r_byte = (i < r.m_data.size() ? byte(r.m_data[i]) : r_extend);\r
+ if (l_byte != r_byte)\r
+ return false;\r
+ }\r
+ return true;\r
+ }\r
+\r
+ bool inf::operator != (const inf& r) const\r
+ {\r
+ return !operator==(r);\r
+ }\r
+\r
+ bool inf::operator < (const inf& r) const\r
+ {\r
+ // This could be implemented in terms of subtraction. However, it can be\r
+ // simplified since there is no need to calculate the accurate difference,\r
+ // just the direction of the difference. I compare from msB down and as\r
+ // soon as a byte difference is found, that defines the ordering. The\r
+ // problem is that in 2's-complement, all negative values are greater than\r
+ // all natural values if you just do a straight unsigned comparison. I\r
+ // handle this by doing a preliminary test for different signs.\r
+\r
+ // For example, a 3-bit signed type has the coding:\r
+ // 000 = 0\r
+ // ...\r
+ // 011 = 3\r
+ // 100 = -4\r
+ // ...\r
+ // 111 = -1\r
+\r
+ // So, for natural values, the ordering of the integer values is the\r
+ // ordering of the bit patterns. Similarly, for negative values, the\r
+ // ordering of the integer values is the ordering of the bit patterns\r
+ // However, the bit patterns for the negative values are *greater than*\r
+ // the natural values. This is a side-effect of the naffness of\r
+ // 2's-complement representation\r
+\r
+ // first handle the case of comparing two values with different signs\r
+ bool l_sign = negative();\r
+ bool r_sign = r.negative();\r
+ if (l_sign != r_sign)\r
+ {\r
+ // one argument must be negative and the other natural\r
+ // the left is less if it is the negative one\r
+ return l_sign;\r
+ }\r
+ // the arguments are the same sign\r
+ // so the ordering is a simple unsigned byte-by-byte comparison\r
+ // However, this is complicated by the possibility that the values could be different lengths\r
+ byte l_extend = l_sign ? byte(255) : byte (0);\r
+ byte r_extend = r_sign ? byte(255) : byte (0);\r
+ std::string::size_type bytes = maximum(m_data.size(),r.m_data.size());\r
+ for (std::string::size_type i = bytes; i--; )\r
+ {\r
+ byte l_byte = (i < m_data.size() ? byte(m_data[i]) : l_extend);\r
+ byte r_byte = (i < r.m_data.size() ? byte(r.m_data[i]) : r_extend);\r
+ if (l_byte != r_byte)\r
+ return l_byte < r_byte;\r
+ }\r
+ // if I get here, the two are equal, so that is not less-than\r
+ return false;\r
+ }\r
+\r
+ bool inf::operator <= (const inf& r) const\r
+ {\r
+ return !(r < *this);\r
+ }\r
+\r
+ bool inf::operator > (const inf& r) const\r
+ {\r
+ return r < *this;\r
+ }\r
+\r
+ bool inf::operator >= (const inf& r) const\r
+ {\r
+ return !(*this < r);\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // logical operators\r
+\r
+ inf& inf::invert (void)\r
+ {\r
+ for (std::string::size_type i = 0; i < m_data.size(); i++)\r
+ m_data[i] = ~m_data[i];\r
+ return *this;\r
+ }\r
+\r
+ inf inf::operator ~ (void) const\r
+ {\r
+ inf result(*this);\r
+ result.invert();\r
+ return result;\r
+ }\r
+\r
+ inf& inf::operator &= (const inf& r)\r
+ {\r
+ // bitwise AND is extended to the length of the largest argument\r
+ byte l_extend = negative() ? byte(255) : byte (0);\r
+ byte r_extend = r.negative() ? byte(255) : byte (0);\r
+ std::string::size_type bytes = maximum(m_data.size(),r.m_data.size());\r
+ for (std::string::size_type i = 0; i < bytes; i++)\r
+ {\r
+ byte l_byte = (i < m_data.size() ? byte(m_data[i]) : l_extend);\r
+ byte r_byte = (i < r.m_data.size() ? byte(r.m_data[i]) : r_extend);\r
+ byte result = l_byte & r_byte;\r
+ if (i < m_data.size())\r
+ m_data[i] = result;\r
+ else\r
+ m_data.append(1,result);\r
+ }\r
+ // now reduce the result\r
+ reduce();\r
+ return *this;\r
+ }\r
+\r
+ inf inf::operator & (const inf& r) const\r
+ {\r
+ inf result(*this);\r
+ result &= r;\r
+ return result;\r
+ }\r
+\r
+ inf& inf::operator |= (const inf& r)\r
+ {\r
+ // bitwise OR is extended to the length of the largest argument\r
+ byte l_extend = negative() ? byte(255) : byte (0);\r
+ byte r_extend = r.negative() ? byte(255) : byte (0);\r
+ std::string::size_type bytes = maximum(m_data.size(),r.m_data.size());\r
+ for (std::string::size_type i = 0; i < bytes; i++)\r
+ {\r
+ byte l_byte = (i < m_data.size() ? byte(m_data[i]) : l_extend);\r
+ byte r_byte = (i < r.m_data.size() ? byte(r.m_data[i]) : r_extend);\r
+ byte result = l_byte | r_byte;\r
+ if (i < m_data.size())\r
+ m_data[i] = result;\r
+ else\r
+ m_data.append(1,result);\r
+ }\r
+ // now reduce the result\r
+ reduce();\r
+ return *this;\r
+ }\r
+\r
+ inf inf::operator | (const inf& r) const\r
+ {\r
+ inf result(*this);\r
+ result |= r;\r
+ return result;\r
+ }\r
+\r
+ inf& inf::operator ^= (const inf& r)\r
+ {\r
+ // bitwise XOR is extended to the length of the largest argument\r
+ byte l_extend = negative() ? byte(255) : byte (0);\r
+ byte r_extend = r.negative() ? byte(255) : byte (0);\r
+ std::string::size_type bytes = maximum(m_data.size(),r.m_data.size());\r
+ for (std::string::size_type i = 0; i < bytes; i++)\r
+ {\r
+ byte l_byte = (i < m_data.size() ? byte(m_data[i]) : l_extend);\r
+ byte r_byte = (i < r.m_data.size() ? byte(r.m_data[i]) : r_extend);\r
+ byte result = l_byte ^ r_byte;\r
+ if (i < m_data.size())\r
+ m_data[i] = result;\r
+ else\r
+ m_data.append(1,result);\r
+ }\r
+ // now reduce the result\r
+ reduce();\r
+ return *this;\r
+ }\r
+\r
+ inf inf::operator ^ (const inf& r) const\r
+ {\r
+ inf result(*this);\r
+ result ^= r;\r
+ return result;\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // shift operators all preserve the value by increasing the word size\r
+\r
+ inf& inf::operator <<= (unsigned shift)\r
+ {\r
+ // left shift is a shift towards the msb, with 0s being shifted in at the lsb\r
+ // split this into a byte shift followed by a bit shift\r
+\r
+ // first expand the value to be big enough for the result\r
+ std::string::size_type new_size = (indexable_bits() + shift + 7) / 8;\r
+ byte extend = negative() ? byte(255) : byte (0);\r
+ while (m_data.size() < new_size)\r
+ m_data.append(1,extend);\r
+ // now do the byte shift\r
+ unsigned byte_shift = shift/8;\r
+ if (byte_shift > 0)\r
+ {\r
+ for (std::string::size_type b = new_size; b--; )\r
+ m_data[b] = (b >= byte_shift) ? m_data[b-byte_shift] : byte(0);\r
+ }\r
+ // and finally the bit shift\r
+ unsigned bit_shift = shift%8;\r
+ if (bit_shift > 0)\r
+ {\r
+ for (std::string::size_type b = new_size; b--; )\r
+ {\r
+ byte current = byte(m_data[b]);\r
+ byte previous = b > 0 ? m_data[b-1] : byte(0);\r
+ m_data[b] = (current << bit_shift) | (previous >> (8 - bit_shift));\r
+ }\r
+ }\r
+ // now reduce the result\r
+ reduce();\r
+ return *this;\r
+ }\r
+\r
+ inf inf::operator << (unsigned shift) const\r
+ {\r
+ inf result(*this);\r
+ result <<= shift;\r
+ return result;\r
+ }\r
+\r
+ inf& inf::operator >>= (unsigned shift)\r
+ {\r
+ // right shift is a shift towards the lsb, with sign bits being shifted in at the msb\r
+ // split this into a byte shift followed by a bit shift\r
+\r
+ // a byte of sign bits\r
+ byte extend = negative() ? byte(255) : byte (0);\r
+ // do the byte shift\r
+ unsigned byte_shift = shift/8;\r
+ if (byte_shift > 0)\r
+ {\r
+ for (std::string::size_type b = 0; b < m_data.size(); b++)\r
+ m_data[b] = (b + byte_shift < m_data.size()) ? m_data[b+byte_shift] : extend;\r
+ }\r
+ // and finally the bit shift\r
+ unsigned bit_shift = shift%8;\r
+ if (bit_shift > 0)\r
+ {\r
+ for (std::string::size_type b = 0; b < m_data.size(); b++)\r
+ {\r
+ byte current = byte(m_data[b]);\r
+ byte next = ((b+1) < m_data.size()) ? m_data[b+1] : extend;\r
+ byte shifted = (current >> bit_shift) | (next << (8 - bit_shift));\r
+ m_data[b] = shifted;\r
+ }\r
+ }\r
+ // now reduce the result\r
+ reduce();\r
+ return *this;\r
+ }\r
+\r
+ inf inf::operator >> (unsigned shift) const\r
+ {\r
+ inf result(*this);\r
+ result >>= shift;\r
+ return result;\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // negation operators\r
+\r
+ inf& inf::negate (void)\r
+ {\r
+ // do 2's-complement negation\r
+ // equivalent to inversion plus one\r
+ invert();\r
+ operator += (inf(1));\r
+ return *this;\r
+ }\r
+\r
+ inf inf::operator - (void) const\r
+ {\r
+ inf result(*this);\r
+ result.negate();\r
+ return result;\r
+ }\r
+\r
+ inf& inf::abs(void)\r
+ {\r
+ if (negative()) negate();\r
+ return *this;\r
+ }\r
+\r
+ inf abs(const inf& i)\r
+ {\r
+ inf result = i;\r
+ result.abs();\r
+ return result;\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // addition operators\r
+\r
+ inf& inf::operator += (const inf& r)\r
+ {\r
+ // do 2's-complement addition\r
+ // Note that the addition can give a result that is larger than either argument\r
+ byte carry = 0;\r
+ std::string::size_type max_size = maximum(m_data.size(),r.m_data.size());\r
+ byte l_extend = negative() ? byte(255) : byte (0);\r
+ byte r_extend = r.negative() ? byte(255) : byte (0);\r
+ for (std::string::size_type i = 0; i < max_size; i++)\r
+ {\r
+ byte l_byte = (i < m_data.size() ? byte(m_data[i]) : l_extend);\r
+ byte r_byte = (i < r.m_data.size() ? byte(r.m_data[i]) : r_extend);\r
+ // calculate the addition in a type that is bigger than a byte in order to catch the carry-out\r
+ unsigned short result = ((unsigned short)(l_byte)) + ((unsigned short)(r_byte)) + carry;\r
+ // now truncate the result to get the lsB\r
+ if (i < m_data.size())\r
+ m_data[i] = byte(result);\r
+ else\r
+ m_data.append(1,byte(result));\r
+ // and capture the carry out by grabbing the second byte of the result\r
+ carry = byte(result >> 8);\r
+ }\r
+ // if the result overflowed or underflowed, add an extra byte to catch it\r
+ unsigned short result = ((unsigned short)(l_extend)) + ((unsigned short)(r_extend)) + carry;\r
+ if (byte(result) != (negative() ? byte(255) : byte(0)))\r
+ m_data.append(1,byte(result));\r
+ // now reduce the result\r
+ reduce();\r
+ return *this;\r
+ }\r
+\r
+ inf inf::operator + (const inf& r) const\r
+ {\r
+ inf result(*this);\r
+ result += r;\r
+ return result;\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // subtraction operators\r
+\r
+ inf& inf::operator -= (const inf& r)\r
+ {\r
+ // subtraction is defined in terms of negation and addition\r
+ inf negated = -r;\r
+ operator += (negated);\r
+ return *this;\r
+ }\r
+\r
+ inf inf::operator - (const inf& r) const\r
+ {\r
+ inf result(*this);\r
+ result -= r;\r
+ return result;\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // multiplication operators\r
+\r
+ inf& inf::operator *= (const inf& r)\r
+ {\r
+ // 2's complement multiplication\r
+ // one day I'll do a more efficient version than this based on the underlying representation\r
+ inf left(*this);\r
+ inf right = r;\r
+ // make the right value natural but preserve its sign for later\r
+ bool right_negative = right.negative();\r
+ right.abs();\r
+ // implemented as a series of conditional additions\r
+ operator = (0);\r
+ // left.resize(right.bits() + left.bits() - 1);\r
+ left <<= right.bits()-1;\r
+ for (unsigned i = right.bits(); i--; )\r
+ {\r
+ if (right[i]) \r
+ operator += (left);\r
+ left >>= 1;\r
+ }\r
+ if (right_negative)\r
+ negate();\r
+ // now reduce the result\r
+ reduce();\r
+ return *this;\r
+ }\r
+\r
+ inf inf::operator * (const inf& r) const\r
+ {\r
+ inf result(*this);\r
+ result *= r;\r
+ return result;\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // division and remainder operators\r
+\r
+ std::pair<inf,inf> inf::divide(const inf& right) const throw(divide_by_zero)\r
+ {\r
+ if (right.zero())\r
+ throw divide_by_zero("stlplus::inf::divide");\r
+ inf numerator(*this);\r
+ inf denominator = right;\r
+ // make the numerator natural but preserve the sign for later\r
+ bool numerator_negative = numerator.negative();\r
+ numerator.abs();\r
+ // same with the denominator\r
+ bool denominator_negative = denominator.negative();\r
+ denominator.abs();\r
+ // the quotient and remainder will form the result\r
+ // start with the quotiont zero and the remainder equal to the whole of the\r
+ // numerator, then do trial subtraction from this\r
+ inf quotient;\r
+ inf remainder = numerator;\r
+ // there's nothing more to do if the numerator is smaller than the denominator\r
+ // but otherwise do the division\r
+ if (numerator.bits() >= denominator.bits())\r
+ {\r
+ // make the quotient big enough to take the result\r
+ quotient.resize(numerator.bits());\r
+ // start with the numerator shifted to the far left\r
+ unsigned shift = numerator.bits() - denominator.bits();\r
+ denominator <<= shift;\r
+ // do the division by repeated subtraction, \r
+ for (unsigned i = shift+1; i--; )\r
+ {\r
+ if (remainder >= denominator)\r
+ {\r
+ remainder -= denominator;\r
+ quotient.set(i);\r
+ }\r
+ denominator >>= 1;\r
+ }\r
+ }\r
+ // now adjust the signs \r
+ // x/(-y) == (-x)/y == -(x/y)\r
+ if (numerator_negative != denominator_negative)\r
+ quotient.negate();\r
+ quotient.reduce();\r
+ // x%(-y) == x%y and (-x)%y == -(x%y)\r
+ if (numerator_negative)\r
+ remainder.negate();\r
+ remainder.reduce();\r
+ return std::pair<inf,inf>(quotient,remainder);\r
+ }\r
+\r
+ inf& inf::operator /= (const inf& r) throw(divide_by_zero)\r
+ {\r
+ std::pair<inf,inf> result = divide(r);\r
+ operator=(result.first);\r
+ return *this;\r
+ }\r
+\r
+ inf inf::operator / (const inf& r) const throw(divide_by_zero)\r
+ {\r
+ std::pair<inf,inf> result = divide(r);\r
+ return result.first;\r
+ }\r
+\r
+ inf& inf::operator %= (const inf& r) throw(divide_by_zero)\r
+ {\r
+ std::pair<inf,inf> result = divide(r);\r
+ operator=(result.second);\r
+ return *this;\r
+ }\r
+\r
+ inf inf::operator % (const inf& r) const throw(divide_by_zero)\r
+ {\r
+ std::pair<inf,inf> result = divide(r);\r
+ return result.second;\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // prefix (void) and postfix (int) operators\r
+\r
+ inf& inf::operator ++ (void)\r
+ {\r
+ operator += (inf(1));\r
+ return *this;\r
+ }\r
+\r
+ inf inf::operator ++ (int)\r
+ {\r
+ inf old(*this);\r
+ operator += (inf(1));\r
+ return old;\r
+ }\r
+\r
+ inf& inf::operator -- (void)\r
+ {\r
+ operator -= (inf(1));\r
+ return *this;\r
+ }\r
+\r
+ inf inf::operator -- (int)\r
+ {\r
+ inf old(*this);\r
+ operator -= (inf(1));\r
+ return old;\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // string representation and I/O routines\r
+\r
+ std::string inf::to_string(unsigned radix) const\r
+ throw(std::invalid_argument)\r
+ {\r
+ std::string result;\r
+ convert_to_string(*this, result, radix);\r
+ return result;\r
+ }\r
+\r
+ inf& inf::from_string(const std::string& value, unsigned radix)\r
+ throw(std::invalid_argument)\r
+ {\r
+ convert_from_string(value, *this, radix);\r
+ return *this;\r
+ }\r
+\r
+ std::ostream& operator << (std::ostream& str, const inf& i)\r
+ {\r
+ try\r
+ {\r
+ // get radix\r
+ unsigned radix = 10;\r
+ if (str.flags() & std::ios_base::oct)\r
+ radix = 8;\r
+ if (str.flags() & std::ios_base::hex)\r
+ radix = 16;\r
+ // the field width is handled by iostream, so I don't need to handle it as well\r
+ // generate the string representation then print it\r
+ str << i.to_string(radix);\r
+ }\r
+ catch(const std::invalid_argument)\r
+ {\r
+ str.setstate(std::ios_base::badbit);\r
+ }\r
+ return str;\r
+ }\r
+\r
+ std::istream& operator >> (std::istream& str, inf& i)\r
+ {\r
+ try\r
+ {\r
+ // get radix\r
+ unsigned radix = 10;\r
+ if (str.flags() & std::ios_base::oct)\r
+ radix = 8;\r
+ if (str.flags() & std::ios_base::hex)\r
+ radix = 16;\r
+ // now get the string image of the value\r
+ std::string image;\r
+ str >> image;\r
+ // and convert to inf\r
+ i.from_string(image, radix);\r
+ }\r
+ catch(const std::invalid_argument)\r
+ {\r
+ str.setstate(std::ios_base::badbit);\r
+ }\r
+ return str;\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // diagnostic dump\r
+ // just convert to hex\r
+\r
+ std::string inf::image_debug(void) const\r
+ {\r
+ // create this dump in the human-readable form, i.e. msB to the left\r
+ std::string result = "0x";\r
+ for (std::string::size_type i = m_data.size(); i--; )\r
+ {\r
+ byte current = m_data[i];\r
+ byte msB = (current & byte(0xf0)) >> 4;\r
+ result += to_char[msB];\r
+ byte lsB = (current & byte(0x0f));\r
+ result += to_char[lsB];\r
+ }\r
+ return result;\r
+ }\r
+\r
+ const std::string& inf::get_bytes(void) const\r
+ {\r
+ return m_data;\r
+ }\r
+\r
+ void inf::set_bytes(const std::string& data)\r
+ {\r
+ m_data = data;\r
+ }\r
+\r
+} // end namespace stlplus\r
-#ifndef STLPLUS_INF
-#define STLPLUS_INF
-////////////////////////////////////////////////////////////////////////////////
-
-// Author: Andy Rushton
-// Copyright: (c) Southampton University 1999-2004
-// (c) Andy Rushton 2004-2009
-// License: BSD License, see ../docs/license.html
-
-// An infinite-precision integer class. This allows calculations on large
-// integers to be performed without overflow.
-
-// this class can throw the following exceptions:
-// std::out_of_range
-// std::overflow_error
-// std::invalid_argument
-// stlplus::divide_by_zero // why doesn't std have this?
-// all of these are derivations of the baseclass:
-// std::logic_error
-// So you can catch all of them by catching the baseclass
-
-// Warning: inf was never intended to be fast, it is just for programs which
-// need a bit of infinite-precision integer arithmetic. For high-performance
-// processing, use the Gnu Multi-Precision (GMP) library. The inf type is just
-// easier to integrate and is already ported to all platforms and compilers
-// that STLplus is ported to.
-
-////////////////////////////////////////////////////////////////////////////////
-#include "portability_fixes.hpp"
-#include "portability_exceptions.hpp"
-#include <string>
-#include <iostream>
-
-////////////////////////////////////////////////////////////////////////////////
-
-namespace stlplus
-{
-
-////////////////////////////////////////////////////////////////////////////////
-
- class inf
- {
- public:
-
- //////////////////////////////////////////////////////////////////////////////
- // constructors and assignments initialise the inf
-
- // the void constructor initialises to zero, the others initialise to the
- // value of the C integer type or the text value contained in the string
-
- inf(void);
- explicit inf(short);
- explicit inf(unsigned short);
- explicit inf(int);
- explicit inf(unsigned);
- explicit inf(long);
- explicit inf(unsigned long);
- explicit inf(const std::string&) throw(std::invalid_argument);
- inf(const inf&);
-
- ~inf(void);
-
- // assignments with equivalent behaviour to the constructors
-
- inf& operator = (short);
- inf& operator = (unsigned short);
- inf& operator = (int);
- inf& operator = (unsigned);
- inf& operator = (long);
- inf& operator = (unsigned long);
- inf& operator = (const std::string&) throw(std::invalid_argument);
- inf& operator = (const inf&);
-
- //////////////////////////////////////////////////////////////////////////////
- // conversions back to the C types
- // truncate: controls the behaviour when the value is too long for the result
- // true: truncate the value
- // false: throw an exception
-
- short to_short(bool truncate = true) const throw(std::overflow_error);
- unsigned short to_unsigned_short(bool truncate = true) const throw(std::overflow_error);
-
- int to_int(bool truncate = true) const throw(std::overflow_error);
- unsigned to_unsigned(bool truncate = true) const throw(std::overflow_error);
-
- long to_long(bool truncate = true) const throw(std::overflow_error);
- unsigned long to_unsigned_long(bool truncate = true) const throw(std::overflow_error);
-
- //////////////////////////////////////////////////////////////////////////////
- // bitwise manipulation
-
- void resize(unsigned bits);
- void reduce(void);
-
- // the number of significant bits in the value
- unsigned bits (void) const;
- unsigned size (void) const;
-
- // the number of bits that can be accessed by the bit() method (=bits() rounded up to the next byte)
- unsigned indexable_bits(void) const;
-
- bool bit (unsigned index) const throw(std::out_of_range);
- bool operator [] (unsigned index) const throw(std::out_of_range);
-
- void set (unsigned index) throw(std::out_of_range);
- void clear (unsigned index) throw(std::out_of_range);
- void preset (unsigned index, bool value) throw(std::out_of_range);
-
- inf slice(unsigned low, unsigned high) const throw(std::out_of_range);
-
- //////////////////////////////////////////////////////////////////////////////
- // tests for common values or ranges
-
- bool negative (void) const;
- bool natural (void) const;
- bool positive (void) const;
- bool zero (void) const;
- bool non_zero (void) const;
-
- // tests used in if(i) and if(!i)
-// operator bool (void) const;
- bool operator ! (void) const;
-
- //////////////////////////////////////////////////////////////////////////////
- // comparisons
-
- bool operator == (const inf&) const;
- bool operator != (const inf&) const;
- bool operator < (const inf&) const;
- bool operator <= (const inf&) const;
- bool operator > (const inf&) const;
- bool operator >= (const inf&) const;
-
- //////////////////////////////////////////////////////////////////////////////
- // bitwise logic operations
-
- inf& invert (void);
- inf operator ~ (void) const;
-
- inf& operator &= (const inf&);
- inf operator & (const inf&) const;
-
- inf& operator |= (const inf&);
- inf operator | (const inf&) const;
-
- inf& operator ^= (const inf&);
- inf operator ^ (const inf&) const;
-
- inf& operator <<= (unsigned shift);
- inf operator << (unsigned shift) const;
-
- inf& operator >>= (unsigned shift);
- inf operator >> (unsigned shift) const;
-
- //////////////////////////////////////////////////////////////////////////////
- // arithmetic operations
-
- inf& negate (void);
- inf operator - (void) const;
-
- inf& abs(void);
- friend inf abs(const inf&);
-
- inf& operator += (const inf&);
- inf operator + (const inf&) const;
-
- inf& operator -= (const inf&);
- inf operator - (const inf&) const;
-
- inf& operator *= (const inf&);
- inf operator * (const inf&) const;
-
- inf& operator /= (const inf&) throw(divide_by_zero);
- inf operator / (const inf&) const throw(divide_by_zero);
-
- inf& operator %= (const inf&) throw(divide_by_zero);
- inf operator % (const inf&) const throw(divide_by_zero);
-
- // combined division operator - returns the result pair(quotient,remainder) in one go
- std::pair<inf,inf> divide(const inf&) const throw(divide_by_zero);
-
- //////////////////////////////////////////////////////////////////////////////
- // pre- and post- increment and decrement
-
- inf& operator ++ (void);
- inf operator ++ (int);
- inf& operator -- (void);
- inf operator -- (int);
-
- //////////////////////////////////////////////////////////////////////////////
- // string representation and I/O
-
- std::string image_debug(void) const;
-
- // conversion to a string representation
- // radix must be 10, 2, 8 or 16
- std::string to_string(unsigned radix = 10) const
- throw(std::invalid_argument);
-
- // conversion from a string
- // radix == 0 - radix is deduced from the input - assumed 10 unless number is prefixed by 0b, 0 or 0x
- // however, you can specify the radix to be 10, 2, 8 or 16 to force that interpretation
- inf& from_string(const std::string&, unsigned radix = 0)
- throw(std::invalid_argument);
-
- //////////////////////////////////////////////////////////////////////////////
- private:
- std::string m_data;
- public:
- const std::string& get_bytes(void) const;
- void set_bytes(const std::string&);
- };
-
- ////////////////////////////////////////////////////////////////////////////////
- // redefine friends for gcc v4.1
-
- inf abs(const inf&);
-
- ////////////////////////////////////////////////////////////////////////////////
-
- std::ostream& operator << (std::ostream&, const inf&);
- std::istream& operator >> (std::istream&, inf&);
-
- ////////////////////////////////////////////////////////////////////////////////
-
-} // end namespace stlplus
-
-#endif
+#ifndef STLPLUS_INF\r
+#define STLPLUS_INF\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+// Author: Andy Rushton\r
+// Copyright: (c) Southampton University 1999-2004\r
+// (c) Andy Rushton 2004 onwards\r
+// License: BSD License, see ../docs/license.html\r
+\r
+// An infinite-precision integer class. This allows calculations on large\r
+// integers to be performed without overflow.\r
+\r
+// this class can throw the following exceptions:\r
+// std::out_of_range\r
+// std::overflow_error\r
+// std::invalid_argument\r
+// stlplus::divide_by_zero // why doesn't std have this?\r
+// all of these are derivations of the baseclass:\r
+// std::logic_error\r
+// So you can catch all of them by catching the baseclass\r
+\r
+// Warning: inf was never intended to be fast, it is just for programs which\r
+// need a bit of infinite-precision integer arithmetic. For high-performance\r
+// processing, use the Gnu Multi-Precision (GMP) library. The inf type is just\r
+// easier to integrate and is already ported to all platforms and compilers\r
+// that STLplus is ported to.\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "portability_fixes.hpp"\r
+#include "portability_exceptions.hpp"\r
+#include <string>\r
+#include <iostream>\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+ class inf\r
+ {\r
+ public:\r
+\r
+ //////////////////////////////////////////////////////////////////////////////\r
+ // constructors and assignments initialise the inf\r
+\r
+ // the void constructor initialises to zero, the others initialise to the\r
+ // value of the C integer type or the text value contained in the string\r
+\r
+ inf(void);\r
+ explicit inf(short);\r
+ explicit inf(unsigned short);\r
+ explicit inf(int);\r
+ explicit inf(unsigned);\r
+ explicit inf(long);\r
+ explicit inf(unsigned long);\r
+ explicit inf(const std::string&) throw(std::invalid_argument);\r
+ inf(const inf&);\r
+\r
+ ~inf(void);\r
+\r
+ // assignments with equivalent behaviour to the constructors\r
+\r
+ inf& operator = (short);\r
+ inf& operator = (unsigned short);\r
+ inf& operator = (int);\r
+ inf& operator = (unsigned);\r
+ inf& operator = (long);\r
+ inf& operator = (unsigned long);\r
+ inf& operator = (const std::string&) throw(std::invalid_argument);\r
+ inf& operator = (const inf&);\r
+\r
+ //////////////////////////////////////////////////////////////////////////////\r
+ // conversions back to the C types\r
+ // truncate: controls the behaviour when the value is too long for the result\r
+ // true: truncate the value\r
+ // false: throw an exception\r
+\r
+ short to_short(bool truncate = true) const throw(std::overflow_error);\r
+ unsigned short to_unsigned_short(bool truncate = true) const throw(std::overflow_error);\r
+\r
+ int to_int(bool truncate = true) const throw(std::overflow_error);\r
+ unsigned to_unsigned(bool truncate = true) const throw(std::overflow_error);\r
+\r
+ long to_long(bool truncate = true) const throw(std::overflow_error);\r
+ unsigned long to_unsigned_long(bool truncate = true) const throw(std::overflow_error);\r
+\r
+ //////////////////////////////////////////////////////////////////////////////\r
+ // bitwise manipulation\r
+\r
+ void resize(unsigned bits);\r
+ void reduce(void);\r
+\r
+ // the number of significant bits in the value\r
+ unsigned bits (void) const;\r
+ unsigned size (void) const;\r
+\r
+ // the number of bits that can be accessed by the bit() method (=bits() rounded up to the next byte)\r
+ unsigned indexable_bits(void) const;\r
+\r
+ bool bit (unsigned index) const throw(std::out_of_range);\r
+ bool operator [] (unsigned index) const throw(std::out_of_range);\r
+\r
+ void set (unsigned index) throw(std::out_of_range);\r
+ void clear (unsigned index) throw(std::out_of_range);\r
+ void preset (unsigned index, bool value) throw(std::out_of_range);\r
+\r
+ inf slice(unsigned low, unsigned high) const throw(std::out_of_range);\r
+\r
+ //////////////////////////////////////////////////////////////////////////////\r
+ // tests for common values or ranges\r
+\r
+ bool negative (void) const;\r
+ bool natural (void) const;\r
+ bool positive (void) const;\r
+ bool zero (void) const;\r
+ bool non_zero (void) const;\r
+\r
+ // tests used in if(i) and if(!i)\r
+// operator bool (void) const;\r
+ bool operator ! (void) const;\r
+\r
+ //////////////////////////////////////////////////////////////////////////////\r
+ // comparisons\r
+\r
+ bool operator == (const inf&) const;\r
+ bool operator != (const inf&) const;\r
+ bool operator < (const inf&) const;\r
+ bool operator <= (const inf&) const;\r
+ bool operator > (const inf&) const;\r
+ bool operator >= (const inf&) const;\r
+\r
+ //////////////////////////////////////////////////////////////////////////////\r
+ // bitwise logic operations\r
+\r
+ inf& invert (void);\r
+ inf operator ~ (void) const;\r
+\r
+ inf& operator &= (const inf&);\r
+ inf operator & (const inf&) const;\r
+\r
+ inf& operator |= (const inf&);\r
+ inf operator | (const inf&) const;\r
+\r
+ inf& operator ^= (const inf&);\r
+ inf operator ^ (const inf&) const;\r
+\r
+ inf& operator <<= (unsigned shift);\r
+ inf operator << (unsigned shift) const;\r
+\r
+ inf& operator >>= (unsigned shift);\r
+ inf operator >> (unsigned shift) const;\r
+\r
+ //////////////////////////////////////////////////////////////////////////////\r
+ // arithmetic operations\r
+\r
+ inf& negate (void);\r
+ inf operator - (void) const;\r
+\r
+ inf& abs(void);\r
+ friend inf abs(const inf&);\r
+\r
+ inf& operator += (const inf&);\r
+ inf operator + (const inf&) const;\r
+\r
+ inf& operator -= (const inf&);\r
+ inf operator - (const inf&) const;\r
+\r
+ inf& operator *= (const inf&);\r
+ inf operator * (const inf&) const;\r
+\r
+ inf& operator /= (const inf&) throw(divide_by_zero);\r
+ inf operator / (const inf&) const throw(divide_by_zero);\r
+\r
+ inf& operator %= (const inf&) throw(divide_by_zero);\r
+ inf operator % (const inf&) const throw(divide_by_zero);\r
+\r
+ // combined division operator - returns the result pair(quotient,remainder) in one go\r
+ std::pair<inf,inf> divide(const inf&) const throw(divide_by_zero);\r
+\r
+ //////////////////////////////////////////////////////////////////////////////\r
+ // pre- and post- increment and decrement\r
+\r
+ inf& operator ++ (void);\r
+ inf operator ++ (int);\r
+ inf& operator -- (void);\r
+ inf operator -- (int);\r
+\r
+ //////////////////////////////////////////////////////////////////////////////\r
+ // string representation and I/O\r
+\r
+ std::string image_debug(void) const;\r
+\r
+ // conversion to a string representation\r
+ // radix must be 10, 2, 8 or 16\r
+ std::string to_string(unsigned radix = 10) const\r
+ throw(std::invalid_argument);\r
+\r
+ // conversion from a string\r
+ // radix == 0 - radix is deduced from the input - assumed 10 unless number is prefixed by 0b, 0 or 0x\r
+ // however, you can specify the radix to be 10, 2, 8 or 16 to force that interpretation\r
+ inf& from_string(const std::string&, unsigned radix = 0)\r
+ throw(std::invalid_argument);\r
+\r
+ //////////////////////////////////////////////////////////////////////////////\r
+ private:\r
+ std::string m_data;\r
+ public:\r
+ const std::string& get_bytes(void) const;\r
+ void set_bytes(const std::string&);\r
+ };\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // redefine friends for gcc v4.1\r
+\r
+ inf abs(const inf&);\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+\r
+ std::ostream& operator << (std::ostream&, const inf&);\r
+ std::istream& operator >> (std::istream&, inf&);\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
+\r
+#endif\r
-////////////////////////////////////////////////////////////////////////////////
-
-// Author: Andy Rushton
-// Copyright: (c) Southampton University 1999-2004
-// (c) Andy Rushton 2004-2009
-// License: BSD License, see ../docs/license.html
-
-// Contains all the platform-specific socket handling used by the TCP and UDP classes
-
-// TODO - any conversion required to support IPv6
-
-////////////////////////////////////////////////////////////////////////////////
-
-#include "ip_sockets.hpp"
-#include "dprintf.hpp"
-#include <string.h>
-
-#ifdef MSWINDOWS
-// Windoze-specific includes
-#include <winsock2.h>
-#define ERRNO WSAGetLastError()
-#define HERRNO WSAGetLastError()
-#define IOCTL ioctlsocket
-#define CLOSE closesocket
-#define SHUT_RDWR SD_BOTH
-#define EINPROGRESS WSAEINPROGRESS
-#define EWOULDBLOCK WSAEWOULDBLOCK
-#define ECONNRESET WSAECONNRESET
-#define SOCKLEN_T int
-#else
-// Generic Unix includes
-// fix for older versions of Darwin?
-#define _BSD_SOCKLEN_T_ int
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/ioctl.h>
-#include <sys/time.h>
-#include <netinet/in.h>
-#include <errno.h>
-#include <netdb.h>
-#include <unistd.h>
-#define INVALID_SOCKET -1
-#define ERRNO errno
-#define HERRNO h_errno
-#define SOCKET int
-#define SOCKET_ERROR -1
-#define IOCTL ::ioctl
-#define CLOSE ::close
-#define SOCKLEN_T socklen_t
-#ifdef SOLARIS
-// Sun put some definitions in a different place
-#include <sys/filio.h>
-#endif
-#endif
-
-////////////////////////////////////////////////////////////////////////////////
-
-namespace stlplus
-{
-
- ////////////////////////////////////////////////////////////////////////////////
- // Utilities
-
- // get an operating-system error message given an error code
- static std::string error_string(int error)
- {
- std::string result = "error " + dformat("%d",error);
-#ifdef MSWINDOWS
- char* message = 0;
- FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
- 0,
- error,
- MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // "User default language"
- (LPTSTR)&message,
- 0,0);
- if (message)
- {
- result = message;
- LocalFree(message);
- }
- // the error message is for some perverse reason newline terminated - remove this
- if (result[result.size()-1] == '\n')
- result.erase(result.end()-1);
- if (result[result.size()-1] == '\r')
- result.erase(result.end()-1);
-#else
- char* message = strerror(error);
- if (message && message[0])
- result = message;
-#endif
- return result;
- }
-
- // convert address:port into a sockaddr
- static void convert_address(unsigned long address, unsigned short port, sockaddr& sa)
- {
- sa.sa_family = AF_INET;
- unsigned short network_port = htons(port);
- memcpy(&sa.sa_data[0], &network_port, sizeof(network_port));
- unsigned long network_address = htonl(address);
- memcpy(&sa.sa_data[2], &network_address, sizeof(network_address));
- }
-
-// // convert host:port into a sockaddr
-// static void convert_host(hostent& host, unsigned short port, sockaddr& sa)
-// {
-// sa.sa_family = host.h_addrtype;
-// unsigned short network_port = htons(port);
-// memcpy(&sa.sa_data[0], &network_port, sizeof(network_port));
-// memcpy(&sa.sa_data[2], host.h_addr, host.h_length);
-// }
-
- // convert sockaddr to address:port
- static void convert_sockaddr(const sockaddr& sa, unsigned long& address, unsigned short& port)
- {
- unsigned short network_port = 0;
- memcpy(&network_port, &sa.sa_data[0], sizeof(network_port));
- port = ntohs(network_port);
- unsigned long network_address = 0;
- memcpy(&network_address, &sa.sa_data[2], sizeof(network_address));
- address = ntohl(network_address);
- }
-
- ////////////////////////////////////////////////////////////////////////////////
- // Initialisation
- // Windows requires that Winsock is initialised before use and closed after
- // These routines initialise once on first use and close on the destruction of the last object using it
- // on non-windows platforms, I still increment/decrement the sockets count variable for diagnostic purposes
-
- static int sockets_count = 0;
-
- static int sockets_init(void)
- {
- int error = 0;
- if (sockets_count++ == 0)
- {
-#ifdef MSWINDOWS
- WSAData winsock_info;
- // request Winsock 2.0 or higher
- error = WSAStartup(MAKEWORD(2,0),&winsock_info);
-#endif
- }
- return error;
- }
-
- static int sockets_close(void)
- {
- int error = 0;
- if (--sockets_count == 0)
- {
-#ifdef MSWINDOWS
- if (WSACleanup() == SOCKET_ERROR)
- error = ERRNO;
-#endif
- }
- return error;
- }
-
- ////////////////////////////////////////////////////////////////////////////////
- // Socket Implementation - common code to manipulate a TCP socket
-
- class IP_socket_internals
- {
- private:
- IP_socket_type m_type;
- SOCKET m_socket;
- unsigned long m_remote_address;
- unsigned short m_remote_port;
- mutable int m_error;
- mutable std::string m_message;
- unsigned m_count;
-
- // disable copying of the internals
- IP_socket_internals(const IP_socket_internals&);
- IP_socket_internals& operator=(const IP_socket_internals&);
-
- public:
-
- ////////////////////////////////////////////////////////////////////////////
- // PIMPL alias counting
-
- void increment(void)
- {
- ++m_count;
- }
-
- bool decrement(void)
- {
- --m_count;
- return m_count == 0;
- }
-
- ////////////////////////////////////////////////////////////////////////////
- // constructors/destructors
-
- // construct an invalid socket
- IP_socket_internals(void) : m_type(undefined_socket_type), m_socket(INVALID_SOCKET), m_error(0), m_count(1)
- {
- set_error(sockets_init());
- }
-
- // close on destroy
- ~IP_socket_internals(void)
- {
- close();
- set_error(sockets_close());
- }
-
- ////////////////////////////////////////////////////////////////////////////
- // initialisation, connection
-
- bool initialised(void) const
- {
- return m_socket != INVALID_SOCKET;
- }
-
- // attach this object to a pre-opened socket
- bool set(SOCKET socket, unsigned long remote_address, unsigned short remote_port)
- {
- if (initialised()) close();
- clear_error();
- m_socket = socket;
- m_remote_address = remote_address;
- m_remote_port = remote_port;
- return true;
- }
-
- // create a raw socket attached to this object
- bool initialise(IP_socket_type type)
- {
- if (initialised()) close();
- clear_error();
- if ((type != TCP) && (type != UDP))
- {
- set_error(-1, "Illegal socket type");
- return false;
- }
- // create an anonymous socket
- m_socket = ::socket(AF_INET, ((type == TCP) ? SOCK_STREAM : SOCK_DGRAM), 0);
- if (m_socket == INVALID_SOCKET)
- {
- set_error(ERRNO);
- close();
- return false;
- }
- // record the type on success only
- m_type = type;
- // set the socket into non-blocking mode
- unsigned long nonblocking = 1;
- if (IOCTL(m_socket, FIONBIO, &nonblocking) == SOCKET_ERROR)
- {
- set_error(ERRNO);
- return false;
- }
- return true;
- }
-
- // function for performing IP lookup (i.e. gethostbyname)
- // could be standalone but making it a member means that it can use the socket's error handler
- // - remote_address: IP name or number
- // - returns the IP address as a number - zero if there's an error
- unsigned long ip_lookup(const std::string& remote_address)
- {
- unsigned long result = 0;
- // Lookup the IP address to convert it into a host record
- // this DOES lookup IP address names as well (not according to MS help !!)
- // TODO - convert this to use ::getaddrinfo - ::gethostbyname is deprecated
- hostent* host_info = ::gethostbyname(remote_address.c_str());
- if (!host_info)
- {
- set_error(HERRNO);
- return 0;
- }
- // extract the address from the host info
- unsigned long network_address = 0;
- memcpy(&network_address, host_info->h_addr, host_info->h_length);
- result = ntohl(network_address);
- return result;
- }
-
- // tests whether a socket is ready for communication
- bool select(bool readable, bool writeable, unsigned wait)
- {
- if (!initialised()) return false;
- // set up the readable set
- fd_set readable_set;
- fd_set* readable_set_ptr = 0;
- if (readable)
- {
- FD_ZERO(&readable_set);
- FD_SET(m_socket,&readable_set);
- readable_set_ptr = &readable_set;
- }
- // set up the writeable set
- fd_set writeable_set;
- fd_set* writeable_set_ptr = 0;
- if (writeable)
- {
- FD_ZERO(&writeable_set);
- FD_SET(m_socket,&writeable_set);
- writeable_set_ptr = &writeable_set;
- }
- // TODO - check the error set and lookup the error?
- fd_set* error_set_ptr = 0;
- // set up the timout value
- // Note: a null pointer implements a blocking select
- // a pointer to a zero value implements a zero-wait poll
- // a pointer to a positive value implements a poll with a timeout
- // I currently only implement polling with timeout which may be zero - no blocking
- timeval timeout;
- timeval* timeout_ptr = 0;
- timeout.tv_sec = wait/1000000;
- timeout.tv_usec = wait%1000000;
- timeout_ptr = &timeout;
- // now test the socket
- int select_result = ::select(m_socket+1, readable_set_ptr, writeable_set_ptr, error_set_ptr, timeout_ptr);
- switch(select_result)
- {
- case SOCKET_ERROR:
- // select failed with an error - trap the error
- set_error(ERRNO);
- return false;
- case 0:
- // timeout exceeded without a connection appearing
- return false;
- default:
- // at least one connection is pending
- // TODO - do we need to do the extra socket options checking on Posix?
- // TODO - does this connect in any way to the error_set above?
- return true;
- }
- }
-
- // bind the socket to a port so that it can receive from specific address
- bool bind(unsigned long remote_address, unsigned short local_port)
- {
- if (!initialised()) return false;
- // name the socket and bind it to a port - this is a requirement for a server
- sockaddr server;
- convert_address(INADDR_ANY, local_port, server);
- if (::bind(m_socket, &server, sizeof(server)) == SOCKET_ERROR)
- {
- set_error(ERRNO);
- close();
- return false;
- }
- return true;
- }
-
- // bind the socket to a port so that it can receive from any address
- bool bind_any(unsigned short local_port)
- {
- return bind(INADDR_ANY, local_port);
- }
-
- // set this socket up to be a listening port
- // must have been bound to a local port already
- // - length of backlog queue to manage - may be zero
- // - returns success status
- bool listen(unsigned short queue)
- {
- if (!initialised()) return false;
- // set the port to listen for incoming connections
- if (::listen(m_socket, (int)queue) == SOCKET_ERROR)
- {
- set_error(ERRNO);
- close();
- return false;
- }
- return true;
- }
-
- // test whether there's an incoming connection on the socket
- // only applicable if it has been set up as a listening port
- bool accept_ready(unsigned wait)
- {
- // the test for a connection being ready is the same as the test for whether the socket is readable
- // see documentation for select
- return select(true, false, wait);
- }
-
- // accept a connection on the socket
- // only applicable if it has been set up as a listening port
- // - returns socket filled in with the accepted connection's details - or with the error fields set
- IP_socket accept(void)
- {
- if (!initialised()) return IP_socket();
- IP_socket result;
- // accept the connection, at the same time getting the address of the connecting client
- sockaddr saddress;
- SOCKLEN_T saddress_length = sizeof(saddress);
- SOCKET socket = ::accept(m_socket, &saddress, &saddress_length);
- if (socket == INVALID_SOCKET)
- {
- // only set the result socket with an error
- result.m_impl->set_error(ERRNO);
- return result;
- }
- // extract the contents of the address
- unsigned long remote_address = 0;
- unsigned short remote_port = 0;
- convert_sockaddr(saddress, remote_address, remote_port);
- result.m_impl->set(socket, remote_address, remote_port);
- return result;
- }
-
- // client connect to a server
- // - remote_address: IP number of remote address to connect to
- // - remote_port: port to connect to
- bool connect(unsigned long remote_address, unsigned short remote_port)
- {
- if (!initialised()) return false;
- // fill in the connection data structure
- sockaddr connect_data;
- convert_address(remote_address, remote_port, connect_data);
- // connect binds the socket to a local address
- // if connectionless it simply sets the default remote address
- // if connectioned it makes the connection
- if (::connect(m_socket, &connect_data, sizeof(connect_data)) == SOCKET_ERROR)
- {
- // the socket is non-blocking, so connect will almost certainly fail with EINPROGRESS which is not an error
- // only catch real errors
- int error = ERRNO;
- if (error != EINPROGRESS && error != EWOULDBLOCK)
- {
- set_error(error);
- return false;
- }
- }
- // extract the remote connection details for local storage
- convert_sockaddr(connect_data, m_remote_address, m_remote_port);
- return true;
- }
-
- // test whether a socket is connected and ready to communicate
- bool connected(unsigned wait)
- {
- if (!initialised()) return false;
- // Linux and Windows docs say test with select for whether socket is
- // writable. However, a problem has been reported with Linux whereby
- // the OS will report a socket as writable when it isn't
- // first use the select method
- if (!select(false, true, wait))
- return false;
-#ifdef MSWINDOWS
- // Windows needs no further processing - select method works
- return true;
-#else
- // Posix version needs further checking using the socket options
- // DJDM: socket has returned EINPROGRESS on the first attempt at connection
- // it has also returned that it can be written to
- // we must now ask it if it has actually connected - using getsockopt
- int error = 0;
- socklen_t serror = sizeof(int);
- if (::getsockopt(m_socket, SOL_SOCKET, SO_ERROR, &error, &serror)==0)
- // handle the error value - one of them means that the socket has connected
- if (!error || error == EISCONN)
- return true;
- return false;
-#endif
- }
-
- bool close(void)
- {
- bool result = true;
- if (initialised())
- {
- if (shutdown(m_socket,SHUT_RDWR) == SOCKET_ERROR)
- {
- set_error(ERRNO);
- result = false;
- }
- if (CLOSE(m_socket) == SOCKET_ERROR)
- {
- set_error(ERRNO);
- result = false;
- }
- }
- m_socket = INVALID_SOCKET;
- m_remote_address = 0;
- m_remote_port = 0;
- return result;
- }
-
- ////////////////////////////////////////////////////////////////////////////
- // sending/receiving
-
- bool send_ready(unsigned wait)
- {
- // determines whether the socket is ready to send by testing whether it is writable
- return select(false, true, wait);
- }
-
- bool send (std::string& data)
- {
- if (!initialised()) return false;
- // send the data - this will never block but may not send all the data
- int bytes = ::send(m_socket, data.c_str(), data.size(), 0);
- if (bytes == SOCKET_ERROR)
- {
- set_error(ERRNO);
- return false;
- }
- // remove the sent bytes from the data buffer so that the buffer represents the data still to be sent
- data.erase(0,bytes);
- return true;
- }
-
- bool send_packet(std::string& data, unsigned long address = 0, unsigned short port = 0)
- {
- if (!initialised()) return false;
- // if no address specified, rely on the socket having been connected (can I test this?)
- // so use the standard send, otherwise use the sendto function
- int bytes = 0;
- if (!address)
- {
- bytes = ::send(m_socket, data.c_str(), data.size(), 0);
- }
- else
- {
- sockaddr saddress;
- convert_address(address, port, saddress);
- bytes = ::sendto(m_socket, data.c_str(), data.size(), 0, &saddress, sizeof(saddress));
- }
- if (bytes == SOCKET_ERROR)
- {
- set_error(ERRNO);
- return false;
- }
- // remove the sent bytes from the data buffer so that the buffer represents the data still to be sent
- data.erase(0,bytes);
- return true;
- }
-
- bool receive_ready(unsigned wait)
- {
- // determines whether the socket is ready to receive by testing whether it is readable
- return select(true, false, wait);
- }
-
- bool receive (std::string& data)
- {
- if (!initialised()) return false;
- // determine how much data is available to read
- unsigned long bytes = 0;
- if (IOCTL(m_socket, FIONREAD, &bytes) == SOCKET_ERROR)
- {
- set_error(ERRNO);
- return false;
- }
- // get the data up to the amount claimed to be present - this is non-blocking
- char* buffer = new char[bytes+1];
- int read = ::recv(m_socket, buffer, bytes, 0);
- if (read == SOCKET_ERROR)
- {
- delete[] buffer;
- set_error(ERRNO);
- close();
- return false;
- }
- if (read == 0)
- {
- // TODO - check whether this is an appropriate conditon to close the socket
- close();
- }
- else
- {
- // this is binary data so copy the bytes including nulls
- data.append(buffer,read);
- }
- delete[] buffer;
- return true;
- }
-
- bool receive_packet(std::string& data, unsigned long& address, unsigned short& port)
- {
- if (!initialised()) return false;
- // determine how much data is available to read
- unsigned long bytes = 0;
- if (IOCTL(m_socket, FIONREAD, &bytes) == SOCKET_ERROR)
- {
- set_error(ERRNO);
- return false;
- }
- // get the data up to the amount claimed to be present - this is non-blocking
- // also get the sender's details
- char* buffer = new char[bytes+1];
- sockaddr saddress;
- SOCKLEN_T saddress_length = sizeof(saddress);
- int read = ::recvfrom(m_socket, buffer, bytes, 0, &saddress, &saddress_length);
- if (read == SOCKET_ERROR)
- {
- // UDP connection reset means that a previous sent failed to deliver cos the address was unknown
- // this is NOT an error with the sending server socket, which IS still usable
- int error = ERRNO;
- if (error != ECONNRESET)
- {
- delete[] buffer;
- set_error(error);
- close();
- return false;
- }
- }
- // this is binary data so copy the bytes including nulls
- data.append(buffer,read);
- // also retrieve the sender's details
- convert_sockaddr(saddress, address, port);
- delete[] buffer;
- return true;
- }
-
- bool receive_packet(std::string& data)
- {
- // call the above and then discard the address details
- unsigned long address = 0;
- unsigned short port = 0;
- return receive_packet(data, address, port);
- }
-
- ////////////////////////////////////////////////////////////////////////////
- // informational
-
- IP_socket_type type(void) const
- {
- return m_type;
- }
-
- unsigned short local_port(void) const
- {
- if (!initialised()) return 0;
- sockaddr saddress;
- SOCKLEN_T saddress_length = sizeof(saddress);
- if (::getsockname(m_socket, &saddress, &saddress_length) != 0)
- {
- set_error(ERRNO);
- return 0;
- }
- unsigned long address = 0;
- unsigned short port = 0;
- convert_sockaddr(saddress, address, port);
- return port;
- }
-
- unsigned long remote_address(void) const
- {
- return m_remote_address;
- }
-
- unsigned short remote_port(void) const
- {
- return m_remote_port;
- }
-
- ////////////////////////////////////////////////////////////////////////////
- // error handling
-
- void set_error (int error, const char* message = 0) const
- {
- if (error != 0)
- {
- m_error = error;
- if (message && (message[0] != 0))
- m_message = message;
- else
- m_message = error_string(error);
- }
- }
-
- int error(void) const
- {
- return m_error;
- }
-
- void clear_error (void) const
- {
- m_error = 0;
- m_message.erase();
- }
-
- std::string message(void) const
- {
- return m_message;
- }
-
- };
-
- ////////////////////////////////////////////////////////////////////////////////
- // Socket - common code to manipulate a socket
-
- // create an uninitialised socket
- IP_socket::IP_socket(void) : m_impl(new IP_socket_internals)
- {
- }
-
- // create an initialised socket
- // - type: create either a TCP or UDP socket - if neither, creates an uninitialised socket
- IP_socket::IP_socket(IP_socket_type type) : m_impl(new IP_socket_internals)
- {
- initialise(type);
- }
-
- // destroy the socket, closing it if open
- IP_socket::~IP_socket(void)
- {
- if (m_impl->decrement())
- delete m_impl;
- }
-
- ////////////////////////////////////////////////////////////////////////////
- // copying is implemented as aliasing
-
- IP_socket::IP_socket(const IP_socket& right) : m_impl(0)
- {
- // make this an alias of right
- m_impl = right.m_impl;
- m_impl->increment();
- }
-
- IP_socket& IP_socket::operator=(const IP_socket& right)
- {
- // make self-copy safe
- if (m_impl == right.m_impl) return *this;
- // first dealias the existing implementation
- if (m_impl->decrement())
- delete m_impl;
- // now make this an alias of right
- m_impl = right.m_impl;
- m_impl->increment();
- return *this;
- }
-
- ////////////////////////////////////////////////////////////////////////////
- // initialisation, connection
-
- // initialise the socket
- // - type: create either a TCP or UDP socket
- // - returns success status
- bool IP_socket::initialise(IP_socket_type type)
- {
- return m_impl->initialise(type);
- }
-
- // test whether this is an initialised socket
- // - returns whether this is initialised
- bool IP_socket::initialised(void) const
- {
- return m_impl->initialised();
- }
-
- // close, i.e. disconnect the socket
- // - returns a success flag
- bool IP_socket::close(void)
- {
- return m_impl->close();
- }
-
- // function for performing IP lookup (i.e. gethostbyname)
- // could be standalone but making it a member means that it can use the socket's error handler
- // - remote_address: IP name (stlplus.sourceforge.net) or dotted number (216.34.181.96)
- // - returns the IP address as a long integer - zero if there's an error
- unsigned long IP_socket::ip_lookup(const std::string& remote_address)
- {
- return m_impl->ip_lookup(remote_address);
- }
-
- // test whether a socket is ready to communicate
- // - readable: test whether socket is ready to read
- // - writeable: test whether a socket is ready to write
- // - timeout: if socket is not ready, time to wait before giving up - in micro-seconds - 0 means don't wait
- // returns false if not ready or error - use error() method to tell - true if ready
- bool IP_socket::select(bool readable, bool writeable, unsigned timeout)
- {
- return m_impl->select(readable, writeable, timeout);
- }
-
- // bind the socket to a port so that it can receive from specific address - typically used by a client
- // - remote_address: IP number of remote server to send/receive to/from
- // - local_port: port on local machine to bind to the address
- // - returns success flag
- bool IP_socket::bind(unsigned long remote_address, unsigned short local_port)
- {
- return m_impl->bind(remote_address, local_port);
- }
-
- // bind the socket to a port so that it can receive from any address - typically used by a server
- // - local_port: port on local machine to bind to the address
- // - returns success flag
- bool IP_socket::bind_any(unsigned short local_port)
- {
- return m_impl->bind_any(local_port);
- }
-
- // initialise a socket and set this socket up to be a listening port
- // - queue: length of backlog queue to manage - may be zero
- // - returns success status
- bool IP_socket::listen(unsigned short queue)
- {
- return m_impl->listen(queue);
- }
-
- // test for a connection on the object's socket - only applicable if it has been set up as a listening port
- // - returns true if a connection is ready to be accepted
- bool IP_socket::accept_ready(unsigned timeout) const
- {
- return m_impl->accept_ready(timeout);
- }
-
- // accept a connection on the object's socket - only applicable if it has been set up as a listening port
- // - returns the connection as a new socket
- IP_socket IP_socket::accept(void)
- {
- return m_impl->accept();
- }
-
- // client connect to a server
- // - address: IP number already lookup up with ip_lookup
- // - port: port to connect to
- // - returns a success flag
- bool IP_socket::connect(unsigned long address, unsigned short port)
- {
- return m_impl->connect(address, port);
- }
-
- // test whether a socket is connected and ready to communicate, returns on successful connect or timeout
- // - timeout: how long to wait in microseconds if not connected yet
- // - returns success flag
- bool IP_socket::connected(unsigned timeout)
- {
- return m_impl->connected(timeout);
- }
-
- ////////////////////////////////////////////////////////////////////////////
- // sending/receiving
-
- // test whether a socket is connected and ready to send data, returns if ready or on timeout
- // - timeout: how long to wait in microseconds if not connected yet (blocking)
- // - returns status
- bool IP_socket::send_ready(unsigned timeout)
- {
- return m_impl->send_ready(timeout);
- }
-
- // send data through the socket - if the data is long only part of it may
- // be sent. The sent part is removed from the data, so the same string can
- // be sent again and again until it is empty.
- // - data: string containing data to be sent - any data successfully sent is removed
- // - returns success flag
- bool IP_socket::send (std::string& data)
- {
- return m_impl->send(data);
- }
-
- // send data through a connectionless (UDP) socket
- // the data will be sent as a single packet
- // - packet: string containing data to be sent - any data successfully sent is removed
- // - remote_address: address of the remote host to send to - optional if the socket has been connected to remote
- // - remote_port: port of the remote host to send to - optional if the socket has been connected to remote
- // - returns success flag
- bool IP_socket::send_packet(std::string& packet, unsigned long remote_address, unsigned short remote_port)
- {
- return m_impl->send_packet(packet, remote_address, remote_port);
- }
-
- // send data through a connectionless (UDP) socket
- // the data will be sent as a single packet
- // only works if the socket has been connected to remote
- // - packet: string containing data to be sent - any data successfully sent is removed
- // - returns success flag
- bool IP_socket::send_packet(std::string& packet)
- {
- return m_impl->send_packet(packet);
- }
-
- // test whether a socket is connected and ready to receive data, returns if ready or on timeout
- // - timeout: how long to wait in microseconds if not connected yet (blocking)
- // - returns status
- bool IP_socket::receive_ready(unsigned timeout)
- {
- return m_impl->receive_ready(timeout);
- }
-
- // receive data through a connection-based (TCP) socket
- // if the data is long only part of it may be received. The received data
- // is appended to the string, building it up in stages, so the same string
- // can be received again and again until all information has been
- // received.
- // - data: string receiving data from socket - any data successfully received is appended
- // - returns success flag
- bool IP_socket::receive (std::string& data)
- {
- return m_impl->receive(data);
- }
-
- // receive data through a connectionless (UDP) socket
- // - packet: string receiving data from socket - any data successfully received is appended
- // - remote_address: returns the address of the remote host received from
- // - remote_port: returns the port of the remote host received from
- // - returns success flag
- bool IP_socket::receive_packet(std::string& packet, unsigned long& remote_address, unsigned short& remote_port)
- {
- return m_impl->receive_packet(packet, remote_address, remote_port);
- }
-
- // variant of above which does not give back the address and port of the sender
- // receive data through a connectionless (UDP) socket
- // - packet: string receiving data from socket - any data successfully received is appended
- // - returns success flag
- bool IP_socket::receive_packet(std::string& packet)
- {
- return m_impl->receive_packet(packet);
- }
-
- ////////////////////////////////////////////////////////////////////////////
- // informational
-
- IP_socket_type IP_socket::type(void) const
- {
- return m_impl->type();
- }
-
- unsigned short IP_socket::local_port(void) const
- {
- return m_impl->local_port();
- }
-
- unsigned long IP_socket::remote_address(void) const
- {
- return m_impl->remote_address();
- }
-
- unsigned short IP_socket::remote_port(void) const
- {
- return m_impl->remote_port();
- }
-
- ////////////////////////////////////////////////////////////////////////////
- // error handling
-
- void IP_socket::set_error (int error, const std::string& message) const
- {
- m_impl->set_error(error, message.c_str());
- }
-
- void IP_socket::clear_error (void) const
- {
- m_impl->clear_error();
- }
-
- int IP_socket::error(void) const
- {
- return m_impl->error();
- }
-
- std::string IP_socket::message(void) const
- {
- return m_impl->message();
- }
-
- ////////////////////////////////////////////////////////////////////////////////
-
-} // end namespace stlplus
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+// Author: Andy Rushton\r
+// Copyright: (c) Southampton University 1999-2004\r
+// (c) Andy Rushton 2004 onwards\r
+// License: BSD License, see ../docs/license.html\r
+\r
+// Contains all the platform-specific socket handling used by the TCP and UDP classes\r
+\r
+// TODO - any conversion required to support IPv6\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+#include "ip_sockets.hpp"\r
+#include "dprintf.hpp"\r
+#include <string.h>\r
+\r
+#ifdef MSWINDOWS\r
+// Windoze-specific includes\r
+#include <winsock2.h>\r
+#define ERRNO WSAGetLastError()\r
+#define HERRNO WSAGetLastError()\r
+#define IOCTL ioctlsocket\r
+#define CLOSE closesocket\r
+#define SHUT_RDWR SD_BOTH\r
+#define SOCKLEN_T int\r
+#define SEND_FLAGS 0\r
+#if _MSC_VER < 1600 // not defined before Visual Studio 10\r
+#define EINPROGRESS WSAEINPROGRESS\r
+#define EWOULDBLOCK WSAEWOULDBLOCK\r
+#define ECONNRESET WSAECONNRESET\r
+#endif\r
+#else\r
+// Generic Unix includes\r
+// fix for older versions of Darwin?\r
+#define _BSD_SOCKLEN_T_ int\r
+#include <sys/types.h>\r
+#include <sys/socket.h>\r
+#include <sys/ioctl.h>\r
+#include <sys/time.h>\r
+#include <netinet/in.h>\r
+#include <errno.h>\r
+#include <netdb.h>\r
+#include <unistd.h>\r
+#define INVALID_SOCKET -1\r
+#define ERRNO errno\r
+#define HERRNO h_errno\r
+#define SOCKET int\r
+#define SOCKET_ERROR -1\r
+#define IOCTL ::ioctl\r
+#define CLOSE ::close\r
+#define SOCKLEN_T socklen_t\r
+#define SEND_FLAGS MSG_NOSIGNAL\r
+#ifdef SOLARIS\r
+// Sun put some definitions in a different place\r
+#include <sys/filio.h>\r
+#endif\r
+#endif\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // Utilities\r
+\r
+ // get an operating-system error message given an error code\r
+ static std::string error_string(int error)\r
+ {\r
+ std::string result = "error " + dformat("%d",error);\r
+#ifdef MSWINDOWS\r
+ char* message = 0;\r
+ FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,\r
+ 0,\r
+ error,\r
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // "User default language"\r
+ (LPTSTR)&message,\r
+ 0,0);\r
+ if (message) \r
+ {\r
+ result = message;\r
+ LocalFree(message);\r
+ }\r
+ // the error message is for some perverse reason newline terminated - remove this\r
+ if (result[result.size()-1] == '\n')\r
+ result.erase(result.end()-1);\r
+ if (result[result.size()-1] == '\r')\r
+ result.erase(result.end()-1);\r
+#else\r
+ char* message = strerror(error);\r
+ if (message && message[0])\r
+ result = message;\r
+#endif\r
+ return result;\r
+ }\r
+\r
+ // convert address:port into a sockaddr\r
+ static void convert_address(unsigned long address, unsigned short port, sockaddr& sa)\r
+ {\r
+ sa.sa_family = AF_INET;\r
+ unsigned short network_port = htons(port);\r
+ memcpy(&sa.sa_data[0], &network_port, sizeof(network_port));\r
+ unsigned long network_address = htonl(address);\r
+ memcpy(&sa.sa_data[2], &network_address, sizeof(network_address));\r
+ }\r
+\r
+// // convert host:port into a sockaddr\r
+// static void convert_host(hostent& host, unsigned short port, sockaddr& sa)\r
+// {\r
+// sa.sa_family = host.h_addrtype;\r
+// unsigned short network_port = htons(port);\r
+// memcpy(&sa.sa_data[0], &network_port, sizeof(network_port));\r
+// memcpy(&sa.sa_data[2], host.h_addr, host.h_length);\r
+// }\r
+\r
+ // convert sockaddr to address:port\r
+ static void convert_sockaddr(const sockaddr& sa, unsigned long& address, unsigned short& port)\r
+ {\r
+ unsigned short network_port = 0;\r
+ memcpy(&network_port, &sa.sa_data[0], sizeof(network_port));\r
+ port = ntohs(network_port);\r
+ unsigned long network_address = 0;\r
+ memcpy(&network_address, &sa.sa_data[2], sizeof(network_address));\r
+ address = ntohl(network_address);\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // Initialisation\r
+ // Windows requires that Winsock is initialised before use and closed after\r
+ // These routines initialise once on first use and close on the destruction of the last object using it\r
+ // on non-windows platforms, I still increment/decrement the sockets count variable for diagnostic purposes\r
+\r
+ static int sockets_count = 0;\r
+\r
+ static int sockets_init(void)\r
+ {\r
+ int error = 0;\r
+ if (sockets_count++ == 0)\r
+ {\r
+#ifdef MSWINDOWS\r
+ WSAData winsock_info;\r
+ // request Winsock 2.0 or higher\r
+ error = WSAStartup(MAKEWORD(2,0),&winsock_info);\r
+#endif\r
+ }\r
+ return error;\r
+ }\r
+\r
+ static int sockets_close(void)\r
+ {\r
+ int error = 0;\r
+ if (--sockets_count == 0)\r
+ {\r
+#ifdef MSWINDOWS\r
+ if (WSACleanup() == SOCKET_ERROR)\r
+ error = ERRNO;\r
+#endif\r
+ }\r
+ return error;\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // Socket Implementation - common code to manipulate a TCP socket\r
+\r
+ class IP_socket_internals\r
+ {\r
+ private:\r
+ IP_socket_type m_type;\r
+ SOCKET m_socket;\r
+ unsigned long m_remote_address;\r
+ unsigned short m_remote_port;\r
+ mutable int m_error;\r
+ mutable std::string m_message;\r
+ unsigned m_count;\r
+\r
+ // disable copying of the internals\r
+ IP_socket_internals(const IP_socket_internals&);\r
+ IP_socket_internals& operator=(const IP_socket_internals&);\r
+\r
+ public:\r
+\r
+ ////////////////////////////////////////////////////////////////////////////\r
+ // PIMPL alias counting \r
+\r
+ void increment(void)\r
+ {\r
+ ++m_count;\r
+ }\r
+\r
+ bool decrement(void)\r
+ {\r
+ --m_count;\r
+ return m_count == 0;\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////\r
+ // constructors/destructors\r
+\r
+ // construct an invalid socket\r
+ IP_socket_internals(void) : m_type(undefined_socket_type), m_socket(INVALID_SOCKET), m_error(0), m_count(1)\r
+ {\r
+ set_error(sockets_init());\r
+ }\r
+\r
+ // close on destroy\r
+ ~IP_socket_internals(void)\r
+ {\r
+ close();\r
+ set_error(sockets_close());\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////\r
+ // initialisation, connection\r
+\r
+ bool initialised(void) const\r
+ {\r
+ return m_socket != INVALID_SOCKET;\r
+ }\r
+\r
+ // attach this object to a pre-opened socket\r
+ bool set(SOCKET socket, unsigned long remote_address, unsigned short remote_port)\r
+ {\r
+ if (initialised()) close();\r
+ clear_error();\r
+ m_socket = socket;\r
+ m_remote_address = remote_address;\r
+ m_remote_port = remote_port;\r
+ return true;\r
+ }\r
+\r
+ // create a raw socket attached to this object\r
+ bool initialise(IP_socket_type type)\r
+ {\r
+ if (initialised()) close();\r
+ clear_error();\r
+ if ((type != TCP) && (type != UDP))\r
+ {\r
+ set_error(-1, "Illegal socket type");\r
+ return false;\r
+ }\r
+ // create an anonymous socket\r
+ m_socket = ::socket(AF_INET, ((type == TCP) ? SOCK_STREAM : SOCK_DGRAM), 0);\r
+ if (m_socket == INVALID_SOCKET)\r
+ {\r
+ set_error(ERRNO);\r
+ close();\r
+ return false;\r
+ }\r
+ // record the type on success only\r
+ m_type = type;\r
+ // set the socket into non-blocking mode\r
+ unsigned long nonblocking = 1;\r
+ if (IOCTL(m_socket, FIONBIO, &nonblocking) == SOCKET_ERROR)\r
+ {\r
+ set_error(ERRNO);\r
+ return false;\r
+ }\r
+ return true;\r
+ }\r
+ \r
+ // function for performing IP lookup (i.e. gethostbyname)\r
+ // could be standalone but making it a member means that it can use the socket's error handler\r
+ // - remote_address: IP name or number\r
+ // - returns the IP address as a number - zero if there's an error\r
+ unsigned long ip_lookup(const std::string& remote_address)\r
+ {\r
+ unsigned long result = 0;\r
+ // Lookup the IP address to convert it into a host record\r
+ // this DOES lookup IP address names as well (not according to MS help !!)\r
+ // TODO - convert this to use ::getaddrinfo - ::gethostbyname is deprecated\r
+ hostent* host_info = ::gethostbyname(remote_address.c_str());\r
+ if (!host_info)\r
+ {\r
+ set_error(HERRNO);\r
+ return 0;\r
+ }\r
+ // extract the address from the host info\r
+ unsigned long network_address = 0;\r
+ memcpy(&network_address, host_info->h_addr, host_info->h_length);\r
+ result = ntohl(network_address);\r
+ return result;\r
+ }\r
+\r
+ // tests whether a socket is ready for communication\r
+ bool select(bool readable, bool writeable, unsigned wait)\r
+ {\r
+ if (!initialised()) return false;\r
+ // set up the readable set\r
+ fd_set readable_set;\r
+ fd_set* readable_set_ptr = 0;\r
+ if (readable)\r
+ {\r
+ FD_ZERO(&readable_set);\r
+ FD_SET(m_socket,&readable_set);\r
+ readable_set_ptr = &readable_set;\r
+ }\r
+ // set up the writeable set\r
+ fd_set writeable_set;\r
+ fd_set* writeable_set_ptr = 0;\r
+ if (writeable)\r
+ {\r
+ FD_ZERO(&writeable_set);\r
+ FD_SET(m_socket,&writeable_set);\r
+ writeable_set_ptr = &writeable_set;\r
+ }\r
+ // TODO - check the error set and lookup the error?\r
+ fd_set* error_set_ptr = 0;\r
+ // set up the timout value\r
+ // Note: a null pointer implements a blocking select\r
+ // a pointer to a zero value implements a zero-wait poll\r
+ // a pointer to a positive value implements a poll with a timeout\r
+ // I currently only implement polling with timeout which may be zero - no blocking\r
+ timeval timeout;\r
+ timeval* timeout_ptr = 0;\r
+ timeout.tv_sec = wait/1000000;\r
+ timeout.tv_usec = wait%1000000;\r
+ timeout_ptr = &timeout;\r
+ // now test the socket\r
+ int select_result = ::select(m_socket+1, readable_set_ptr, writeable_set_ptr, error_set_ptr, timeout_ptr);\r
+ switch(select_result)\r
+ {\r
+ case SOCKET_ERROR:\r
+ // select failed with an error - trap the error\r
+ set_error(ERRNO);\r
+ return false;\r
+ case 0:\r
+ // timeout exceeded without a connection appearing\r
+ return false;\r
+ default:\r
+ // at least one connection is pending\r
+ // TODO - do we need to do the extra socket options checking on Posix?\r
+ // TODO - does this connect in any way to the error_set above?\r
+ return true;\r
+ }\r
+ }\r
+\r
+ // bind the socket to a port so that it can receive from specific address\r
+ bool bind(unsigned long remote_address, unsigned short local_port)\r
+ {\r
+ if (!initialised()) return false;\r
+ // name the socket and bind it to a port - this is a requirement for a server\r
+ sockaddr server;\r
+ convert_address(INADDR_ANY, local_port, server);\r
+ if (::bind(m_socket, &server, sizeof(server)) == SOCKET_ERROR)\r
+ {\r
+ set_error(ERRNO);\r
+ close();\r
+ return false;\r
+ }\r
+ return true;\r
+ }\r
+ \r
+ // bind the socket to a port so that it can receive from any address\r
+ bool bind_any(unsigned short local_port)\r
+ {\r
+ return bind(INADDR_ANY, local_port);\r
+ }\r
+\r
+ // set this socket up to be a listening port\r
+ // must have been bound to a local port already\r
+ // - length of backlog queue to manage - may be zero\r
+ // - returns success status\r
+ bool listen(unsigned short queue)\r
+ {\r
+ if (!initialised()) return false;\r
+ // set the port to listen for incoming connections\r
+ if (::listen(m_socket, (int)queue) == SOCKET_ERROR)\r
+ {\r
+ set_error(ERRNO);\r
+ close();\r
+ return false;\r
+ }\r
+ return true;\r
+ }\r
+\r
+ // test whether there's an incoming connection on the socket\r
+ // only applicable if it has been set up as a listening port\r
+ bool accept_ready(unsigned wait)\r
+ {\r
+ // the test for a connection being ready is the same as the test for whether the socket is readable\r
+ // see documentation for select\r
+ return select(true, false, wait);\r
+ }\r
+\r
+ // accept a connection on the socket\r
+ // only applicable if it has been set up as a listening port\r
+ // - returns socket filled in with the accepted connection's details - or with the error fields set\r
+ IP_socket accept(void)\r
+ {\r
+ if (!initialised()) return IP_socket();\r
+ IP_socket result;\r
+ // accept the connection, at the same time getting the address of the connecting client\r
+ sockaddr saddress;\r
+ SOCKLEN_T saddress_length = sizeof(saddress);\r
+ SOCKET socket = ::accept(m_socket, &saddress, &saddress_length);\r
+ if (socket == INVALID_SOCKET)\r
+ {\r
+ // only set the result socket with an error\r
+ result.m_impl->set_error(ERRNO);\r
+ return result;\r
+ }\r
+ // extract the contents of the address\r
+ unsigned long remote_address = 0;\r
+ unsigned short remote_port = 0;\r
+ convert_sockaddr(saddress, remote_address, remote_port);\r
+ result.m_impl->set(socket, remote_address, remote_port);\r
+ return result;\r
+ }\r
+\r
+ // client connect to a server\r
+ // - remote_address: IP number of remote address to connect to\r
+ // - remote_port: port to connect to\r
+ bool connect(unsigned long remote_address, unsigned short remote_port)\r
+ {\r
+ if (!initialised()) return false;\r
+ // fill in the connection data structure\r
+ sockaddr connect_data;\r
+ convert_address(remote_address, remote_port, connect_data);\r
+ // connect binds the socket to a local address\r
+ // if connectionless it simply sets the default remote address\r
+ // if connectioned it makes the connection\r
+ if (::connect(m_socket, &connect_data, sizeof(connect_data)) == SOCKET_ERROR)\r
+ {\r
+ // the socket is non-blocking, so connect will almost certainly fail with EINPROGRESS which is not an error\r
+ // only catch real errors\r
+ int error = ERRNO;\r
+ if (error != EINPROGRESS && error != EWOULDBLOCK)\r
+ {\r
+ set_error(error);\r
+ return false;\r
+ }\r
+ }\r
+ // extract the remote connection details for local storage\r
+ convert_sockaddr(connect_data, m_remote_address, m_remote_port);\r
+ return true;\r
+ }\r
+\r
+ // test whether a socket is connected and ready to communicate\r
+ bool connected(unsigned wait)\r
+ {\r
+ if (!initialised()) return false;\r
+ // Linux and Windows docs say test with select for whether socket is\r
+ // writable. However, a problem has been reported with Linux whereby\r
+ // the OS will report a socket as writable when it isn't\r
+ // first use the select method\r
+ if (!select(false, true, wait))\r
+ return false;\r
+#ifdef MSWINDOWS\r
+ // Windows needs no further processing - select method works\r
+ return true;\r
+#else\r
+ // Posix version needs further checking using the socket options\r
+ // DJDM: socket has returned EINPROGRESS on the first attempt at connection\r
+ // it has also returned that it can be written to\r
+ // we must now ask it if it has actually connected - using getsockopt\r
+ int error = 0;\r
+ socklen_t serror = sizeof(int);\r
+ if (::getsockopt(m_socket, SOL_SOCKET, SO_ERROR, &error, &serror)==0)\r
+ // handle the error value - one of them means that the socket has connected\r
+ if (!error || error == EISCONN)\r
+ return true;\r
+ return false;\r
+#endif\r
+ }\r
+\r
+ bool close(void)\r
+ {\r
+ bool result = true;\r
+ if (initialised())\r
+ {\r
+ if (shutdown(m_socket,SHUT_RDWR) == SOCKET_ERROR)\r
+ {\r
+ set_error(ERRNO);\r
+ result = false;\r
+ }\r
+ if (CLOSE(m_socket) == SOCKET_ERROR)\r
+ {\r
+ set_error(ERRNO);\r
+ result = false;\r
+ }\r
+ }\r
+ m_socket = INVALID_SOCKET;\r
+ m_remote_address = 0;\r
+ m_remote_port = 0;\r
+ return result;\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////\r
+ // sending/receiving\r
+\r
+ bool send_ready(unsigned wait)\r
+ {\r
+ // determines whether the socket is ready to send by testing whether it is writable\r
+ return select(false, true, wait);\r
+ }\r
+\r
+ bool send (std::string& data)\r
+ {\r
+ if (!initialised()) return false;\r
+ // send the data - this will never block but may not send all the data\r
+ int bytes = ::send(m_socket, data.c_str(), data.size(), SEND_FLAGS);\r
+ if (bytes == SOCKET_ERROR)\r
+ {\r
+ set_error(ERRNO);\r
+ return false;\r
+ }\r
+ // remove the sent bytes from the data buffer so that the buffer represents the data still to be sent\r
+ data.erase(0,bytes);\r
+ return true;\r
+ }\r
+\r
+ bool send_packet(std::string& data, unsigned long address = 0, unsigned short port = 0)\r
+ {\r
+ if (!initialised()) return false;\r
+ // if no address specified, rely on the socket having been connected (can I test this?)\r
+ // so use the standard send, otherwise use the sendto function\r
+ int bytes = 0;\r
+ if (!address)\r
+ {\r
+ bytes = ::send(m_socket, data.c_str(), data.size(), SEND_FLAGS);\r
+ }\r
+ else\r
+ {\r
+ sockaddr saddress;\r
+ convert_address(address, port, saddress);\r
+ bytes = ::sendto(m_socket, data.c_str(), data.size(), SEND_FLAGS, &saddress, sizeof(saddress));\r
+ }\r
+ if (bytes == SOCKET_ERROR)\r
+ {\r
+ set_error(ERRNO);\r
+ return false;\r
+ }\r
+ // remove the sent bytes from the data buffer so that the buffer represents the data still to be sent\r
+ data.erase(0,bytes);\r
+ return true;\r
+ }\r
+\r
+ bool receive_ready(unsigned wait)\r
+ {\r
+ // determines whether the socket is ready to receive by testing whether it is readable\r
+ return select(true, false, wait);\r
+ }\r
+\r
+ bool receive (std::string& data)\r
+ {\r
+ if (!initialised()) return false;\r
+ // determine how much data is available to read\r
+ unsigned long bytes = 0;\r
+ if (IOCTL(m_socket, FIONREAD, &bytes) == SOCKET_ERROR)\r
+ {\r
+ set_error(ERRNO);\r
+ return false;\r
+ }\r
+ // get the data up to the amount claimed to be present - this is non-blocking\r
+ char* buffer = new char[bytes+1];\r
+ int read = ::recv(m_socket, buffer, bytes, 0);\r
+ if (read == SOCKET_ERROR)\r
+ {\r
+ delete[] buffer;\r
+ set_error(ERRNO);\r
+ close();\r
+ return false;\r
+ }\r
+ if (read == 0)\r
+ {\r
+ // TODO - check whether this is an appropriate conditon to close the socket\r
+ close();\r
+ }\r
+ else\r
+ {\r
+ // this is binary data so copy the bytes including nulls\r
+ data.append(buffer,read);\r
+ }\r
+ delete[] buffer;\r
+ return true;\r
+ }\r
+\r
+ bool receive_packet(std::string& data, unsigned long& address, unsigned short& port)\r
+ {\r
+ if (!initialised()) return false;\r
+ // determine how much data is available to read\r
+ unsigned long bytes = 0;\r
+ if (IOCTL(m_socket, FIONREAD, &bytes) == SOCKET_ERROR)\r
+ {\r
+ set_error(ERRNO);\r
+ return false;\r
+ }\r
+ // get the data up to the amount claimed to be present - this is non-blocking\r
+ // also get the sender's details\r
+ char* buffer = new char[bytes+1];\r
+ sockaddr saddress;\r
+ SOCKLEN_T saddress_length = sizeof(saddress);\r
+ int read = ::recvfrom(m_socket, buffer, bytes, 0, &saddress, &saddress_length);\r
+ if (read == SOCKET_ERROR)\r
+ {\r
+ // UDP connection reset means that a previous sent failed to deliver cos the address was unknown\r
+ // this is NOT an error with the sending server socket, which IS still usable\r
+ int error = ERRNO;\r
+ if (error != ECONNRESET)\r
+ {\r
+ delete[] buffer;\r
+ set_error(error);\r
+ close();\r
+ return false;\r
+ }\r
+ }\r
+ // this is binary data so copy the bytes including nulls\r
+ data.append(buffer,read);\r
+ // also retrieve the sender's details\r
+ convert_sockaddr(saddress, address, port);\r
+ delete[] buffer;\r
+ return true;\r
+ }\r
+\r
+ bool receive_packet(std::string& data)\r
+ {\r
+ // call the above and then discard the address details\r
+ unsigned long address = 0;\r
+ unsigned short port = 0;\r
+ return receive_packet(data, address, port);\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////\r
+ // informational\r
+\r
+ IP_socket_type type(void) const\r
+ {\r
+ return m_type;\r
+ }\r
+\r
+ unsigned short local_port(void) const\r
+ {\r
+ if (!initialised()) return 0;\r
+ sockaddr saddress;\r
+ SOCKLEN_T saddress_length = sizeof(saddress);\r
+ if (::getsockname(m_socket, &saddress, &saddress_length) != 0)\r
+ {\r
+ set_error(ERRNO);\r
+ return 0;\r
+ }\r
+ unsigned long address = 0;\r
+ unsigned short port = 0;\r
+ convert_sockaddr(saddress, address, port);\r
+ return port;\r
+ }\r
+\r
+ unsigned long remote_address(void) const\r
+ {\r
+ return m_remote_address;\r
+ }\r
+\r
+ unsigned short remote_port(void) const\r
+ {\r
+ return m_remote_port;\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////\r
+ // error handling\r
+\r
+ void set_error (int error, const char* message = 0) const\r
+ {\r
+ if (error != 0)\r
+ {\r
+ m_error = error;\r
+ if (message && (message[0] != 0))\r
+ m_message = message;\r
+ else\r
+ m_message = error_string(error);\r
+ }\r
+ }\r
+\r
+ int error(void) const\r
+ {\r
+ return m_error;\r
+ }\r
+\r
+ void clear_error (void) const\r
+ {\r
+ m_error = 0;\r
+ m_message.erase();\r
+ }\r
+\r
+ std::string message(void) const\r
+ {\r
+ return m_message;\r
+ }\r
+\r
+ };\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // Socket - common code to manipulate a socket\r
+\r
+ // create an uninitialised socket\r
+ IP_socket::IP_socket(void) : m_impl(new IP_socket_internals)\r
+ {\r
+ }\r
+\r
+ // create an initialised socket\r
+ // - type: create either a TCP or UDP socket - if neither, creates an uninitialised socket\r
+ IP_socket::IP_socket(IP_socket_type type) : m_impl(new IP_socket_internals)\r
+ {\r
+ initialise(type);\r
+ }\r
+\r
+ // destroy the socket, closing it if open\r
+ IP_socket::~IP_socket(void)\r
+ {\r
+ if (m_impl->decrement())\r
+ delete m_impl;\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////\r
+ // copying is implemented as aliasing\r
+\r
+ IP_socket::IP_socket(const IP_socket& right) : m_impl(0)\r
+ {\r
+ // make this an alias of right\r
+ m_impl = right.m_impl;\r
+ m_impl->increment();\r
+ }\r
+\r
+ IP_socket& IP_socket::operator=(const IP_socket& right)\r
+ {\r
+ // make self-copy safe\r
+ if (m_impl == right.m_impl) return *this;\r
+ // first dealias the existing implementation\r
+ if (m_impl->decrement())\r
+ delete m_impl;\r
+ // now make this an alias of right\r
+ m_impl = right.m_impl;\r
+ m_impl->increment();\r
+ return *this;\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////\r
+ // initialisation, connection\r
+\r
+ // initialise the socket\r
+ // - type: create either a TCP or UDP socket\r
+ // - returns success status\r
+ bool IP_socket::initialise(IP_socket_type type)\r
+ {\r
+ return m_impl->initialise(type);\r
+ }\r
+\r
+ // test whether this is an initialised socket\r
+ // - returns whether this is initialised\r
+ bool IP_socket::initialised(void) const\r
+ {\r
+ return m_impl->initialised();\r
+ }\r
+\r
+ // close, i.e. disconnect the socket\r
+ // - returns a success flag\r
+ bool IP_socket::close(void)\r
+ {\r
+ return m_impl->close();\r
+ }\r
+\r
+ // function for performing IP lookup (i.e. gethostbyname)\r
+ // could be standalone but making it a member means that it can use the socket's error handler\r
+ // - remote_address: IP name (stlplus.sourceforge.net) or dotted number (216.34.181.96)\r
+ // - returns the IP address as a long integer - zero if there's an error\r
+ unsigned long IP_socket::ip_lookup(const std::string& remote_address)\r
+ {\r
+ return m_impl->ip_lookup(remote_address);\r
+ }\r
+\r
+ // test whether a socket is ready to communicate\r
+ // - readable: test whether socket is ready to read\r
+ // - writeable: test whether a socket is ready to write\r
+ // - timeout: if socket is not ready, time to wait before giving up - in micro-seconds - 0 means don't wait\r
+ // returns false if not ready or error - use error() method to tell - true if ready\r
+ bool IP_socket::select(bool readable, bool writeable, unsigned timeout)\r
+ {\r
+ return m_impl->select(readable, writeable, timeout);\r
+ }\r
+\r
+ // bind the socket to a port so that it can receive from specific address - typically used by a client\r
+ // - remote_address: IP number of remote server to send/receive to/from\r
+ // - local_port: port on local machine to bind to the address\r
+ // - returns success flag\r
+ bool IP_socket::bind(unsigned long remote_address, unsigned short local_port)\r
+ {\r
+ return m_impl->bind(remote_address, local_port);\r
+ }\r
+\r
+ // bind the socket to a port so that it can receive from any address - typically used by a server\r
+ // - local_port: port on local machine to bind to the address\r
+ // - returns success flag\r
+ bool IP_socket::bind_any(unsigned short local_port)\r
+ {\r
+ return m_impl->bind_any(local_port);\r
+ }\r
+\r
+ // initialise a socket and set this socket up to be a listening port\r
+ // - queue: length of backlog queue to manage - may be zero\r
+ // - returns success status\r
+ bool IP_socket::listen(unsigned short queue)\r
+ {\r
+ return m_impl->listen(queue);\r
+ }\r
+\r
+ // test for a connection on the object's socket - only applicable if it has been set up as a listening port\r
+ // - returns true if a connection is ready to be accepted\r
+ bool IP_socket::accept_ready(unsigned timeout) const\r
+ {\r
+ return m_impl->accept_ready(timeout);\r
+ }\r
+\r
+ // accept a connection on the object's socket - only applicable if it has been set up as a listening port\r
+ // - returns the connection as a new socket\r
+ IP_socket IP_socket::accept(void)\r
+ {\r
+ return m_impl->accept();\r
+ }\r
+\r
+ // client connect to a server\r
+ // - address: IP number already lookup up with ip_lookup\r
+ // - port: port to connect to\r
+ // - returns a success flag\r
+ bool IP_socket::connect(unsigned long address, unsigned short port)\r
+ {\r
+ return m_impl->connect(address, port);\r
+ }\r
+\r
+ // test whether a socket is connected and ready to communicate, returns on successful connect or timeout\r
+ // - timeout: how long to wait in microseconds if not connected yet\r
+ // - returns success flag\r
+ bool IP_socket::connected(unsigned timeout)\r
+ {\r
+ return m_impl->connected(timeout);\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////\r
+ // sending/receiving\r
+\r
+ // test whether a socket is connected and ready to send data, returns if ready or on timeout\r
+ // - timeout: how long to wait in microseconds if not connected yet (blocking)\r
+ // - returns status\r
+ bool IP_socket::send_ready(unsigned timeout)\r
+ {\r
+ return m_impl->send_ready(timeout);\r
+ }\r
+\r
+ // send data through the socket - if the data is long only part of it may\r
+ // be sent. The sent part is removed from the data, so the same string can\r
+ // be sent again and again until it is empty.\r
+ // - data: string containing data to be sent - any data successfully sent is removed\r
+ // - returns success flag\r
+ bool IP_socket::send (std::string& data)\r
+ {\r
+ return m_impl->send(data);\r
+ }\r
+\r
+ // send data through a connectionless (UDP) socket\r
+ // the data will be sent as a single packet\r
+ // - packet: string containing data to be sent - any data successfully sent is removed\r
+ // - remote_address: address of the remote host to send to - optional if the socket has been connected to remote\r
+ // - remote_port: port of the remote host to send to - optional if the socket has been connected to remote\r
+ // - returns success flag\r
+ bool IP_socket::send_packet(std::string& packet, unsigned long remote_address, unsigned short remote_port)\r
+ {\r
+ return m_impl->send_packet(packet, remote_address, remote_port);\r
+ }\r
+\r
+ // send data through a connectionless (UDP) socket\r
+ // the data will be sent as a single packet\r
+ // only works if the socket has been connected to remote\r
+ // - packet: string containing data to be sent - any data successfully sent is removed\r
+ // - returns success flag\r
+ bool IP_socket::send_packet(std::string& packet)\r
+ {\r
+ return m_impl->send_packet(packet);\r
+ }\r
+\r
+ // test whether a socket is connected and ready to receive data, returns if ready or on timeout\r
+ // - timeout: how long to wait in microseconds if not connected yet (blocking)\r
+ // - returns status\r
+ bool IP_socket::receive_ready(unsigned timeout)\r
+ {\r
+ return m_impl->receive_ready(timeout);\r
+ }\r
+\r
+ // receive data through a connection-based (TCP) socket\r
+ // if the data is long only part of it may be received. The received data\r
+ // is appended to the string, building it up in stages, so the same string\r
+ // can be received again and again until all information has been\r
+ // received.\r
+ // - data: string receiving data from socket - any data successfully received is appended\r
+ // - returns success flag\r
+ bool IP_socket::receive (std::string& data)\r
+ {\r
+ return m_impl->receive(data);\r
+ }\r
+\r
+ // receive data through a connectionless (UDP) socket\r
+ // - packet: string receiving data from socket - any data successfully received is appended\r
+ // - remote_address: returns the address of the remote host received from\r
+ // - remote_port: returns the port of the remote host received from\r
+ // - returns success flag\r
+ bool IP_socket::receive_packet(std::string& packet, unsigned long& remote_address, unsigned short& remote_port)\r
+ {\r
+ return m_impl->receive_packet(packet, remote_address, remote_port);\r
+ }\r
+\r
+ // variant of above which does not give back the address and port of the sender\r
+ // receive data through a connectionless (UDP) socket\r
+ // - packet: string receiving data from socket - any data successfully received is appended\r
+ // - returns success flag\r
+ bool IP_socket::receive_packet(std::string& packet)\r
+ {\r
+ return m_impl->receive_packet(packet);\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////\r
+ // informational\r
+\r
+ IP_socket_type IP_socket::type(void) const\r
+ {\r
+ return m_impl->type();\r
+ }\r
+\r
+ unsigned short IP_socket::local_port(void) const\r
+ {\r
+ return m_impl->local_port();\r
+ }\r
+\r
+ unsigned long IP_socket::remote_address(void) const\r
+ {\r
+ return m_impl->remote_address();\r
+ }\r
+\r
+ unsigned short IP_socket::remote_port(void) const\r
+ {\r
+ return m_impl->remote_port();\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////\r
+ // error handling\r
+\r
+ void IP_socket::set_error (int error, const std::string& message) const\r
+ {\r
+ m_impl->set_error(error, message.c_str());\r
+ }\r
+\r
+ void IP_socket::clear_error (void) const\r
+ {\r
+ m_impl->clear_error();\r
+ }\r
+\r
+ int IP_socket::error(void) const\r
+ {\r
+ return m_impl->error();\r
+ }\r
+\r
+ std::string IP_socket::message(void) const\r
+ {\r
+ return m_impl->message();\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
-#ifndef STLPLUS_IP_SOCKET
-#define STLPLUS_IP_SOCKET
-////////////////////////////////////////////////////////////////////////////////
-
-// Author: Andy Rushton
-// Copyright: (c) Southampton University 1999-2004
-// (c) Andy Rushton 2004-2009
-// License: BSD License, see ../docs/license.html
-
-// A platform-independent (Unix and Windows anyway) interface to Internet-Protocol sockets
-
-////////////////////////////////////////////////////////////////////////////////
-#include "portability_fixes.hpp"
-#include <string>
-
-namespace stlplus
-{
-
- //////////////////////////////////////////////////////////////////////////////
- // internals
- // use a PIMPL interface to hide the platform-specifics in the implementation
-
- class IP_socket_internals;
-
- ////////////////////////////////////////////////////////////////////////////
- // Types of socket supported
-
- enum IP_socket_type {undefined_socket_type = -1, TCP = 0, UDP = 1};
-
- //////////////////////////////////////////////////////////////////////////////
- // Socket class
-
- class IP_socket
- {
- public:
-
- ////////////////////////////////////////////////////////////////////////////
- // constructors/destructors
-
- // create an uninitialised socket
- IP_socket(void);
-
- // create an initialised socket
- // - type: create either a TCP or UDP socket
- IP_socket(IP_socket_type type);
-
- // destroy the socket, closing it if open
- ~IP_socket(void);
-
- // copying is implemented as aliasing
- IP_socket(const IP_socket&);
- IP_socket& operator=(const IP_socket&);
-
- ////////////////////////////////////////////////////////////////////////////
- // initialisation
-
- // initialise the socket
- // - type: create either a TCP or UDP socket
- // - returns success status
- bool initialise(IP_socket_type type);
-
- // test whether this is an initialised socket
- // - returns whether this is initialised
- bool initialised(void) const;
-
- // close, i.e. disconnect the socket
- // - returns a success flag
- bool close(void);
-
- //////////////////////////////////////////////////////////////////////////////
- // Socket configuration
-
- // function for performing IP lookup (i.e. gethostbyname)
- // could be standalone but making it a member means that it can use the socket's error handler
- // i.e. if this fails, the sockets error code will be set - clear it to use the socket again
- // - remote_address: IP name (stlplus.sourceforge.net) or dotted number (216.34.181.96)
- // - returns the IP address as a long integer - zero if there's an error
- unsigned long ip_lookup(const std::string& remote_address);
-
- // test whether a socket is ready to communicate
- // - readable: test whether socket is ready to read
- // - writeable: test whether a socket is ready to write
- // - timeout: if socket is not ready, time to wait before giving up - in micro-seconds - 0 means don't wait
- // returns false if not ready or error - use error() method to tell - true if ready
- bool select(bool readable, bool writeable, unsigned timeout = 0);
-
- // bind the socket to a port so that it can receive from specific address - typically used by a client
- // - remote_address: IP number of remote server to send/receive to/from
- // - local_port: port on local machine to bind to the address
- // - returns success flag
- bool bind(unsigned long remote_address, unsigned short local_port);
-
- // bind the socket to a port so that it can receive from any address - typically used by a server
- // - local_port: port on local machine to bind to the address
- // - returns success flag
- bool bind_any(unsigned short local_port);
-
- // set this socket up to be a listening port
- // socket must be bound to a port already
- // - queue: length of backlog queue to manage - may be zero meaning no queue
- // - returns success status
- bool listen(unsigned short queue = 0);
-
- // test for a connection on the socket
- // only applicable if it has been set up as a listening port
- // - timeout: how long to wait in microseconds if not connected yet
- // - returns true if a connection is ready to be accepted
- bool accept_ready(unsigned timeout = 0) const;
-
- // accept a connection on the socket
- // only applicable if it has been set up as a listening port
- // - returns the connection as a new socket
- IP_socket accept(void);
-
- // create a connection - usually used by a client
- // TCP: client connect to a remote server
- // UDP: setup remote address and port for sends
- // - remote_address: IP number already looked up using ip_lookup
- // - remote_port: port to connect to
- // - returns a success flag
- bool connect(unsigned long remote_address, unsigned short remote_port);
-
- // test whether a socket is connected and ready to communicate, returns on successful connect or timeout
- // - timeout: how long to wait in microseconds if not connected yet
- // - returns true if connected and ready to communicate, false if not ready or error
- bool connected(unsigned timeout = 0);
-
- ////////////////////////////////////////////////////////////////////////////
- // sending/receiving
-
- // test whether a socket is connected and ready to send data, returns if ready or on timeout
- // - timeout: how long to wait in microseconds if not connected yet (blocking)
- // - returns status
- bool send_ready(unsigned timeout = 0);
-
- // send data through a connection-based (TCP) socket
- // if the data is long only part of it may be sent. The sent part is
- // removed from the data, so the same string can be sent again and again
- // until it is empty.
- // - data: string containing data to be sent - any data successfully sent is removed
- // - returns success flag
- bool send(std::string& data);
-
- // send data through a connectionless (UDP) socket
- // the data will be sent as a single packet
- // - packet: string containing data to be sent - any data successfully sent is removed
- // - remote_address: address of the remote host to send to - optional if the socket has been connected to remote
- // - remote_port: port of the remote host to send to - optional if the socket has been connected to remote
- // - returns success flag
- bool send_packet(std::string& packet, unsigned long remote_address, unsigned short remote_port);
-
- // send data through a connectionless (UDP) socket
- // the data will be sent as a single packet
- // only works if the socket has been connected to remote
- // - packet: string containing data to be sent - any data successfully sent is removed
- // - returns success flag
- bool send_packet(std::string& packet);
-
- // test whether a socket is connected and ready to receive data, returns if ready or on timeout
- // - timeout: how long to wait in microseconds if not connected yet (blocking)
- // - returns status
- bool receive_ready(unsigned wait = 0);
-
- // receive data through a connection-based (TCP) socket
- // if the data is long only part of it may be received. The received data
- // is appended to the string, building it up in stages, so the same string
- // can be received again and again until all information has been
- // received.
- // - data: string receiving data from socket - any data successfully received is appended
- // - returns success flag
- bool receive(std::string& data);
-
- // receive data through a connectionless (UDP) socket
- // - packet: string receiving data from socket - any data successfully received is appended
- // - remote_address: returns the address of the remote host received from
- // - remote_port: returns the port of the remote host received from
- // - returns success flag
- bool receive_packet(std::string& packet, unsigned long& remote_address, unsigned short& remote_port);
-
- // variant of above which does not give back the address and port of the sender
- // receive data through a connectionless (UDP) socket
- // - packet: string receiving data from socket - any data successfully received is appended
- // - returns success flag
- bool receive_packet(std::string& packet);
-
- ////////////////////////////////////////////////////////////////////////////
- // informational
-
- // gets the type of socket
- // - returns undefined_socket_type, TCP or UDP
- IP_socket_type type(void) const;
-
- // the local port number of the connection
- // returns the port number, 0 if not bound to a port
- unsigned short local_port(void) const;
-
- // the remote address of the connection
- // returns the address, 0 if not connected
- unsigned long remote_address(void) const;
-
- // the remote port number of the connection
- // returns the port number, 0 if not connected to a port
- unsigned short remote_port(void) const;
-
- ////////////////////////////////////////////////////////////////////////////
- // error handling
- // errors are set internally
- // an error code of 0 is the test for no error, don't rely on the message being an empty string
- // an error code != 0 means an error, then there will be a message explaining the error
-
- // indicate an error - mostly used internally, you can set your own errors - use a negative code
- void set_error (int error, const std::string& message) const;
-
- // if an error is set it stays set - so you must clear it before further operations
- void clear_error (void) const;
-
- // get the error code of the last error
- int error(void) const;
-
- // get the explanatory message of the last error
- std::string message(void) const;
-
- ////////////////////////////////////////////////////////////////////////////
-
- private:
- friend class IP_socket_internals;
- IP_socket_internals* m_impl;
- };
-
-
-} // end namespace stlplus
-
-#endif
+#ifndef STLPLUS_IP_SOCKET\r
+#define STLPLUS_IP_SOCKET\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+// Author: Andy Rushton\r
+// Copyright: (c) Southampton University 1999-2004\r
+// (c) Andy Rushton 2004 onwards\r
+// License: BSD License, see ../docs/license.html\r
+\r
+// A platform-independent (Unix and Windows anyway) interface to Internet-Protocol sockets\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "portability_fixes.hpp"\r
+#include <string>\r
+\r
+namespace stlplus\r
+{\r
+\r
+ //////////////////////////////////////////////////////////////////////////////\r
+ // internals\r
+ // use a PIMPL interface to hide the platform-specifics in the implementation\r
+\r
+ class IP_socket_internals;\r
+\r
+ ////////////////////////////////////////////////////////////////////////////\r
+ // Types of socket supported\r
+\r
+ enum IP_socket_type {undefined_socket_type = -1, TCP = 0, UDP = 1};\r
+\r
+ //////////////////////////////////////////////////////////////////////////////\r
+ // Socket class\r
+\r
+ class IP_socket\r
+ {\r
+ public:\r
+\r
+ ////////////////////////////////////////////////////////////////////////////\r
+ // constructors/destructors\r
+\r
+ // create an uninitialised socket\r
+ IP_socket(void);\r
+\r
+ // create an initialised socket\r
+ // - type: create either a TCP or UDP socket\r
+ IP_socket(IP_socket_type type);\r
+\r
+ // destroy the socket, closing it if open\r
+ ~IP_socket(void);\r
+\r
+ // copying is implemented as aliasing\r
+ IP_socket(const IP_socket&);\r
+ IP_socket& operator=(const IP_socket&);\r
+\r
+ ////////////////////////////////////////////////////////////////////////////\r
+ // initialisation\r
+\r
+ // initialise the socket\r
+ // - type: create either a TCP or UDP socket\r
+ // - returns success status\r
+ bool initialise(IP_socket_type type);\r
+\r
+ // test whether this is an initialised socket\r
+ // - returns whether this is initialised\r
+ bool initialised(void) const;\r
+\r
+ // close, i.e. disconnect the socket\r
+ // - returns a success flag\r
+ bool close(void);\r
+\r
+ //////////////////////////////////////////////////////////////////////////////\r
+ // Socket configuration\r
+\r
+ // function for performing IP lookup (i.e. gethostbyname)\r
+ // could be standalone but making it a member means that it can use the socket's error handler\r
+ // i.e. if this fails, the sockets error code will be set - clear it to use the socket again\r
+ // - remote_address: IP name (stlplus.sourceforge.net) or dotted number (216.34.181.96)\r
+ // - returns the IP address as a long integer - zero if there's an error\r
+ unsigned long ip_lookup(const std::string& remote_address);\r
+\r
+ // test whether a socket is ready to communicate\r
+ // - readable: test whether socket is ready to read\r
+ // - writeable: test whether a socket is ready to write\r
+ // - timeout: if socket is not ready, time to wait before giving up - in micro-seconds - 0 means don't wait\r
+ // returns false if not ready or error - use error() method to tell - true if ready\r
+ bool select(bool readable, bool writeable, unsigned timeout = 0);\r
+\r
+ // bind the socket to a port so that it can receive from specific address - typically used by a client\r
+ // - remote_address: IP number of remote server to send/receive to/from\r
+ // - local_port: port on local machine to bind to the address\r
+ // - returns success flag\r
+ bool bind(unsigned long remote_address, unsigned short local_port);\r
+\r
+ // bind the socket to a port so that it can receive from any address - typically used by a server\r
+ // - local_port: port on local machine to bind to the address\r
+ // - returns success flag\r
+ bool bind_any(unsigned short local_port);\r
+\r
+ // set this socket up to be a listening port\r
+ // socket must be bound to a port already\r
+ // - queue: length of backlog queue to manage - may be zero meaning no queue\r
+ // - returns success status\r
+ bool listen(unsigned short queue = 0);\r
+\r
+ // test for a connection on the socket\r
+ // only applicable if it has been set up as a listening port\r
+ // - timeout: how long to wait in microseconds if not connected yet\r
+ // - returns true if a connection is ready to be accepted\r
+ bool accept_ready(unsigned timeout = 0) const;\r
+\r
+ // accept a connection on the socket\r
+ // only applicable if it has been set up as a listening port\r
+ // - returns the connection as a new socket\r
+ IP_socket accept(void);\r
+\r
+ // create a connection - usually used by a client\r
+ // TCP: client connect to a remote server\r
+ // UDP: setup remote address and port for sends\r
+ // - remote_address: IP number already looked up using ip_lookup\r
+ // - remote_port: port to connect to\r
+ // - returns a success flag\r
+ bool connect(unsigned long remote_address, unsigned short remote_port);\r
+\r
+ // test whether a socket is connected and ready to communicate, returns on successful connect or timeout\r
+ // - timeout: how long to wait in microseconds if not connected yet\r
+ // - returns true if connected and ready to communicate, false if not ready or error\r
+ bool connected(unsigned timeout = 0);\r
+\r
+ ////////////////////////////////////////////////////////////////////////////\r
+ // sending/receiving\r
+\r
+ // test whether a socket is connected and ready to send data, returns if ready or on timeout\r
+ // - timeout: how long to wait in microseconds if not connected yet (blocking)\r
+ // - returns status\r
+ bool send_ready(unsigned timeout = 0);\r
+\r
+ // send data through a connection-based (TCP) socket\r
+ // if the data is long only part of it may be sent. The sent part is\r
+ // removed from the data, so the same string can be sent again and again\r
+ // until it is empty.\r
+ // - data: string containing data to be sent - any data successfully sent is removed\r
+ // - returns success flag\r
+ bool send(std::string& data);\r
+\r
+ // send data through a connectionless (UDP) socket\r
+ // the data will be sent as a single packet\r
+ // - packet: string containing data to be sent - any data successfully sent is removed\r
+ // - remote_address: address of the remote host to send to - optional if the socket has been connected to remote\r
+ // - remote_port: port of the remote host to send to - optional if the socket has been connected to remote\r
+ // - returns success flag\r
+ bool send_packet(std::string& packet, unsigned long remote_address, unsigned short remote_port);\r
+\r
+ // send data through a connectionless (UDP) socket\r
+ // the data will be sent as a single packet\r
+ // only works if the socket has been connected to remote\r
+ // - packet: string containing data to be sent - any data successfully sent is removed\r
+ // - returns success flag\r
+ bool send_packet(std::string& packet);\r
+\r
+ // test whether a socket is connected and ready to receive data, returns if ready or on timeout\r
+ // - timeout: how long to wait in microseconds if not connected yet (blocking)\r
+ // - returns status\r
+ bool receive_ready(unsigned wait = 0);\r
+\r
+ // receive data through a connection-based (TCP) socket\r
+ // if the data is long only part of it may be received. The received data\r
+ // is appended to the string, building it up in stages, so the same string\r
+ // can be received again and again until all information has been\r
+ // received.\r
+ // - data: string receiving data from socket - any data successfully received is appended\r
+ // - returns success flag\r
+ bool receive(std::string& data);\r
+\r
+ // receive data through a connectionless (UDP) socket\r
+ // - packet: string receiving data from socket - any data successfully received is appended\r
+ // - remote_address: returns the address of the remote host received from\r
+ // - remote_port: returns the port of the remote host received from\r
+ // - returns success flag\r
+ bool receive_packet(std::string& packet, unsigned long& remote_address, unsigned short& remote_port);\r
+\r
+ // variant of above which does not give back the address and port of the sender\r
+ // receive data through a connectionless (UDP) socket\r
+ // - packet: string receiving data from socket - any data successfully received is appended\r
+ // - returns success flag\r
+ bool receive_packet(std::string& packet);\r
+\r
+ ////////////////////////////////////////////////////////////////////////////\r
+ // informational\r
+\r
+ // gets the type of socket\r
+ // - returns undefined_socket_type, TCP or UDP\r
+ IP_socket_type type(void) const;\r
+\r
+ // the local port number of the connection\r
+ // returns the port number, 0 if not bound to a port\r
+ unsigned short local_port(void) const;\r
+\r
+ // the remote address of the connection\r
+ // returns the address, 0 if not connected\r
+ unsigned long remote_address(void) const;\r
+\r
+ // the remote port number of the connection\r
+ // returns the port number, 0 if not connected to a port\r
+ unsigned short remote_port(void) const;\r
+\r
+ ////////////////////////////////////////////////////////////////////////////\r
+ // error handling\r
+ // errors are set internally\r
+ // an error code of 0 is the test for no error, don't rely on the message being an empty string\r
+ // an error code != 0 means an error, then there will be a message explaining the error\r
+\r
+ // indicate an error - mostly used internally, you can set your own errors - use a negative code\r
+ void set_error (int error, const std::string& message) const;\r
+\r
+ // if an error is set it stays set - so you must clear it before further operations\r
+ void clear_error (void) const;\r
+\r
+ // get the error code of the last error\r
+ int error(void) const;\r
+\r
+ // get the explanatory message of the last error\r
+ std::string message(void) const;\r
+\r
+ ////////////////////////////////////////////////////////////////////////////\r
+\r
+ private:\r
+ friend class IP_socket_internals;\r
+ IP_socket_internals* m_impl;\r
+ };\r
+\r
+\r
+} // end namespace stlplus\r
+\r
+#endif\r
-#ifndef STLPLUS_PORTABILITY
-#define STLPLUS_PORTABILITY
-////////////////////////////////////////////////////////////////////////////////
-
-// Author: Andy Rushton
-// Copyright: (c) Southampton University 1999-2004
-// (c) Andy Rushton 2004-2009
-// License: BSD License, see ../docs/license.html
-
-// Allows all the STLplus portability packages to be included in one go
-
-////////////////////////////////////////////////////////////////////////////////
-
-#include "build.hpp"
-#include "debug.hpp"
-#include "dprintf.hpp"
-#include "dynaload.hpp"
-#include "file_system.hpp"
-#include "inf.hpp"
-#include "subprocesses.hpp"
-#include "tcp_sockets.hpp"
-#include "udp_sockets.hpp"
-#include "time.hpp"
-#include "version.hpp"
-#include "wildcard.hpp"
-
-////////////////////////////////////////////////////////////////////////////////
-#endif
+#ifndef STLPLUS_PORTABILITY\r
+#define STLPLUS_PORTABILITY\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+// Author: Andy Rushton\r
+// Copyright: (c) Southampton University 1999-2004\r
+// (c) Andy Rushton 2004 onwards\r
+// License: BSD License, see ../docs/license.html\r
+\r
+// Allows all the STLplus portability packages to be included in one go\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+#include "build.hpp"\r
+#include "debug.hpp"\r
+#include "dprintf.hpp"\r
+#include "dynaload.hpp"\r
+#include "file_system.hpp"\r
+#include "inf.hpp"\r
+#include "subprocesses.hpp"\r
+#include "tcp_sockets.hpp"\r
+#include "udp_sockets.hpp"\r
+#include "time.hpp"\r
+#include "version.hpp"\r
+#include "wildcard.hpp"\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#endif\r
-#ifndef STLPLUS_PORTABILITY_EXCEPTIONS
-#define STLPLUS_PORTABILITY_EXCEPTIONS
-////////////////////////////////////////////////////////////////////////////////
-
-// Author: Andy Rushton
-// Copyright: (c) Southampton University 1999-2004
-// (c) Andy Rushton 2004-2009
-// License: BSD License, see ../docs/license.html
-
-// Adds missing arithmetic exceptions used in this library but missing from std
-
-////////////////////////////////////////////////////////////////////////////////
-#include "portability_fixes.hpp"
-#include <string>
-#include <stdexcept>
-
-namespace stlplus
-{
-
- ////////////////////////////////////////////////////////////////////////////////
- // thrown by division when the divisor is zero
- // This is a subclass of std::logic_error so can be caught by a generic catch clause for the superclass
-
- class divide_by_zero : public std::logic_error {
- public:
- divide_by_zero (const std::string& what_arg): std::logic_error (what_arg) { }
- };
-
-////////////////////////////////////////////////////////////////////////////////
-
-} // end namespace stlplus
-
-#endif
+#ifndef STLPLUS_PORTABILITY_EXCEPTIONS\r
+#define STLPLUS_PORTABILITY_EXCEPTIONS\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+// Author: Andy Rushton\r
+// Copyright: (c) Southampton University 1999-2004\r
+// (c) Andy Rushton 2004 onwards\r
+// License: BSD License, see ../docs/license.html\r
+\r
+// Adds missing arithmetic exceptions used in this library but missing from std\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "portability_fixes.hpp"\r
+#include <string>\r
+#include <stdexcept>\r
+\r
+namespace stlplus\r
+{\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // thrown by division when the divisor is zero\r
+ // This is a subclass of std::logic_error so can be caught by a generic catch clause for the superclass\r
+\r
+ class divide_by_zero : public std::logic_error {\r
+ public:\r
+ divide_by_zero (const std::string& what_arg): std::logic_error (what_arg) { }\r
+ };\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
+\r
+#endif\r
-////////////////////////////////////////////////////////////////////////////////
-
-// Author: Andy Rushton
-// Copyright: (c) Southampton University 1999-2004
-// (c) Andy Rushton 2004-2009
-// License: BSD License, see ../docs/license.html
-
-////////////////////////////////////////////////////////////////////////////////
-#include "portability_fixes.hpp"
-
-#ifdef MSWINDOWS
-#include "windows.h"
-#endif
-
-////////////////////////////////////////////////////////////////////////////////
-// problems with missing functions
-////////////////////////////////////////////////////////////////////////////////
-
-#ifdef MSWINDOWS
-unsigned sleep(unsigned seconds)
-{
- Sleep(1000*seconds);
- // should return remaining time if interrupted - however Windoze Sleep cannot be interrupted
- return 0;
-}
-#endif
-
-////////////////////////////////////////////////////////////////////////////////
-// Function for establishing endian-ness
-////////////////////////////////////////////////////////////////////////////////
-
-bool stlplus::little_endian(void)
-{
- int sample = 1;
- char* sample_bytes = (char*)&sample;
- return sample_bytes[0] != 0;
-}
-
-////////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+// Author: Andy Rushton\r
+// Copyright: (c) Southampton University 1999-2004\r
+// (c) Andy Rushton 2004 onwards\r
+// License: BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "portability_fixes.hpp"\r
+\r
+#ifdef MSWINDOWS\r
+#include "windows.h"\r
+#endif\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// problems with missing functions\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+#ifdef MSWINDOWS\r
+unsigned sleep(unsigned seconds)\r
+{\r
+ Sleep(1000*seconds);\r
+ // should return remaining time if interrupted - however Windoze Sleep cannot be interrupted\r
+ return 0;\r
+}\r
+#endif\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// Function for establishing endian-ness\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+bool stlplus::little_endian(void)\r
+{\r
+ int sample = 1;\r
+ char* sample_bytes = (char*)&sample;\r
+ return sample_bytes[0] != 0;\r
+}\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
-#ifndef STLPLUS_PORTABILITY_FIXES
-#define STLPLUS_PORTABILITY_FIXES
-////////////////////////////////////////////////////////////////////////////////
-
-// Author: Andy Rushton
-// Copyright: (c) Southampton University 1999-2004
-// (c) Andy Rushton 2004-2009
-// License: BSD License, see ../docs/license.html
-
-// Contains work arounds for OS or Compiler specific problems to try to make
-// them look more alike
-
-// It is strongly recommended that this header be included as the first
-// #include in every source file
-
-////////////////////////////////////////////////////////////////////////////////
-
-////////////////////////////////////////////////////////////////////////////////
-// Problem with MicroSoft defining two different macros to identify Windows
-////////////////////////////////////////////////////////////////////////////////
-
-#if defined(_WIN32) || defined(_WIN32_WCE)
-#define MSWINDOWS
-#endif
-
-////////////////////////////////////////////////////////////////////////////////
-// Problems with unnecessary or unfixable compiler warnings
-////////////////////////////////////////////////////////////////////////////////
-
-#ifdef _MSC_VER
-// Microsoft Visual Studio
-// shut up the following irritating warnings
-// 4786 - VC6, identifier string exceeded maximum allowable length and was truncated (only affects debugger)
-// 4305 - VC6, identifier type was converted to a smaller type
-// 4503 - VC6, decorated name was longer than the maximum the compiler allows (only affects debugger)
-// 4309 - VC6, type conversion operation caused a constant to exceeded the space allocated for it
-// 4290 - VC6, C++ exception specification ignored
-// 4800 - VC6, forcing value to bool 'true' or 'false' (performance warning)
-// 4675 - VC7.1, "change" in function overload resolution _might_ have altered program
-// 4996 - VC8, 'xxxx' was declared deprecated
-#pragma warning(disable: 4786 4305 4503 4309 4290 4800 4675 4996)
-#endif
-
-#ifdef __BORLANDC__
-// Borland
-// Shut up the following irritating warnings
-// 8026 - Functions with exception specifications are not expanded inline
-// 8027 - Functions with xxx are not expanded inline
-#pragma warn -8026
-#pragma warn -8027
-#endif
-
-////////////////////////////////////////////////////////////////////////////////
-// Problems with redefinition of min/max in various different versions of library headers
-////////////////////////////////////////////////////////////////////////////////
-
-// The Windows headers define macros called max/min which conflict with the templates std::max and std::min.
-// So, to avoid conflicts, MS removed the std::max/min rather than fixing the problem!
-// From Visual Studio .NET (SV7, compiler version 13.00) the STL templates have been added correctly.
-// For MFC compatibility, only undef min and max in non-MFC programs - some bits of MFC
-// use macro min/max in headers.
-
-// I've created extra template function definitions minimum/maximum that avoid all the problems above
-
-namespace stlplus
-{
- template<typename T> const T& maximum(const T& l, const T& r) {return l > r ? l : r;}
- template<typename T> const T& minimum(const T& l, const T& r) {return l < r ? l : r;}
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Problems with differences between namespaces
-////////////////////////////////////////////////////////////////////////////////
-
-// Note: not sure of the relevance of this - maybe deprecated?
-// problem in gcc pre-v3 where the sub-namespaces in std aren't present
-// this mean that the statement "using namespace std::rel_ops" created an error because the namespace didn't exist
-
-// I've done a fix here that creates an empty namespace for this case, but I
-// do *not* try to move the contents of std::rel_ops into namespace std
-// This fix only works if you use "using namespace std::rel_ops" to bring in the template relational operators (e.g. != defined i.t.o. ==)
-
-#ifdef __GNUC__
-namespace std
-{
- namespace rel_ops
- {
- }
-}
-#endif
-
-////////////////////////////////////////////////////////////////////////////////
-// problems with missing functions
-////////////////////////////////////////////////////////////////////////////////
-
-#ifdef MSWINDOWS
-unsigned sleep(unsigned seconds);
-#else
-#include <unistd.h>
-#endif
-
-////////////////////////////////////////////////////////////////////////////////
-// Function for establishing endian-ness
-////////////////////////////////////////////////////////////////////////////////
-// Different machine architectures store data using different byte orders.
-// This is referred to as Big- and Little-Endian Byte Ordering.
-//
-// The issue is: where does a pointer to an integer type actually point?
-//
-// In both conventions, the address points to the left of the word but:
-// Big-Endian - The most significant byte is on the left end of a word
-// Little-Endian - The least significant byte is on the left end of a word
-//
-// Bytes are addressed left to right, so in big-endian order byte 0 is the
-// msB, whereas in little-endian order byte 0 is the lsB. For example,
-// Intel-based machines store data in little-endian byte order so byte 0 is
-// the lsB.
-//
-// This function establishes byte order at run-time
-
-namespace stlplus
-{
- bool little_endian(void);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-#endif
+#ifndef STLPLUS_PORTABILITY_FIXES\r
+#define STLPLUS_PORTABILITY_FIXES\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+// Author: Andy Rushton\r
+// Copyright: (c) Southampton University 1999-2004\r
+// (c) Andy Rushton 2004 onwards\r
+// License: BSD License, see ../docs/license.html\r
+\r
+// Contains work arounds for OS or Compiler specific problems to try to make\r
+// them look more alike\r
+\r
+// It is strongly recommended that this header be included as the first\r
+// #include in every source file\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// Problem with MicroSoft defining two different macros to identify Windows\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+#if defined(_WIN32) || defined(_WIN32_WCE)\r
+#define MSWINDOWS\r
+#endif\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// Problems with unnecessary or unfixable compiler warnings\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+#ifdef _MSC_VER\r
+// Microsoft Visual Studio\r
+// shut up the following irritating warnings\r
+// 4786 - VC6, identifier string exceeded maximum allowable length and was truncated (only affects debugger)\r
+// 4305 - VC6, identifier type was converted to a smaller type\r
+// 4503 - VC6, decorated name was longer than the maximum the compiler allows (only affects debugger)\r
+// 4309 - VC6, type conversion operation caused a constant to exceeded the space allocated for it\r
+// 4290 - VC6, C++ exception specification ignored\r
+// 4800 - VC6, forcing value to bool 'true' or 'false' (performance warning)\r
+// 4675 - VC7.1, "change" in function overload resolution _might_ have altered program\r
+// 4996 - VC8, 'xxxx' was declared deprecated\r
+#pragma warning(disable: 4786 4305 4503 4309 4290 4800 4675 4996)\r
+#endif\r
+\r
+#ifdef __BORLANDC__\r
+// Borland\r
+// Shut up the following irritating warnings\r
+// 8026 - Functions with exception specifications are not expanded inline\r
+// 8027 - Functions with xxx are not expanded inline\r
+#pragma warn -8026\r
+#pragma warn -8027\r
+#endif\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// Problems with redefinition of min/max in various different versions of library headers\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+// The Windows headers define macros called max/min which conflict with the templates std::max and std::min.\r
+// So, to avoid conflicts, MS removed the std::max/min rather than fixing the problem!\r
+// From Visual Studio .NET (SV7, compiler version 13.00) the STL templates have been added correctly.\r
+// For MFC compatibility, only undef min and max in non-MFC programs - some bits of MFC\r
+// use macro min/max in headers. \r
+\r
+// I've created extra template function definitions minimum/maximum that avoid all the problems above\r
+\r
+namespace stlplus\r
+{\r
+ template<typename T> const T& maximum(const T& l, const T& r) {return l > r ? l : r;}\r
+ template<typename T> const T& minimum(const T& l, const T& r) {return l < r ? l : r;}\r
+}\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// Problems with differences between namespaces\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+// Note: not sure of the relevance of this - maybe deprecated?\r
+// problem in gcc pre-v3 where the sub-namespaces in std aren't present\r
+// this mean that the statement "using namespace std::rel_ops" created an error because the namespace didn't exist\r
+\r
+// I've done a fix here that creates an empty namespace for this case, but I\r
+// do *not* try to move the contents of std::rel_ops into namespace std\r
+// This fix only works if you use "using namespace std::rel_ops" to bring in the template relational operators (e.g. != defined i.t.o. ==)\r
+\r
+#ifdef __GNUC__\r
+namespace std\r
+{\r
+ namespace rel_ops\r
+ {\r
+ }\r
+}\r
+#endif\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// problems with missing functions\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+#ifdef MSWINDOWS\r
+unsigned sleep(unsigned seconds);\r
+#else\r
+#include <unistd.h>\r
+#endif\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// Function for establishing endian-ness\r
+////////////////////////////////////////////////////////////////////////////////\r
+// Different machine architectures store data using different byte orders.\r
+// This is referred to as Big- and Little-Endian Byte Ordering. \r
+//\r
+// The issue is: where does a pointer to an integer type actually point?\r
+//\r
+// In both conventions, the address points to the left of the word but:\r
+// Big-Endian - The most significant byte is on the left end of a word\r
+// Little-Endian - The least significant byte is on the left end of a word\r
+//\r
+// Bytes are addressed left to right, so in big-endian order byte 0 is the\r
+// msB, whereas in little-endian order byte 0 is the lsB. For example,\r
+// Intel-based machines store data in little-endian byte order so byte 0 is\r
+// the lsB.\r
+//\r
+// This function establishes byte order at run-time\r
+\r
+namespace stlplus\r
+{\r
+ bool little_endian(void);\r
+}\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#endif\r
-////////////////////////////////////////////////////////////////////////////////
-
-// Author: Andy Rushton
-// Copyright: (c) Southampton University 1999-2004
-// (c) Andy Rushton 2004-2009
-// License: BSD License, see ../docs/license.html
-
-////////////////////////////////////////////////////////////////////////////////
-
-// Bug fix by Alistair Low: kill on Windows now kills grandchild processes as
-// well as the child process. This is done using jobs - which has to be
-// enabled by stating that the version of Windows is at least 5.0
-#if defined(_WIN32) || defined(_WIN32_WCE)
-#define _WIN32_WINNT 0x0500
-#endif
-
-#include "subprocesses.hpp"
-#include "file_system.hpp"
-#include "dprintf.hpp"
-#include <ctype.h>
-#include <string.h>
-#include <stdlib.h>
-
-#ifdef MSWINDOWS
-#else
-#include <signal.h>
-#include <errno.h>
-#include <sys/wait.h>
-#include <unistd.h>
-#include <fcntl.h>
-extern char** environ;
-#endif
-
-////////////////////////////////////////////////////////////////////////////////
-
-namespace stlplus
-{
-
- ////////////////////////////////////////////////////////////////////////////////
- // argument-vector related stuff
-
- static void skip_white (const std::string& command, unsigned& i)
- {
- while(i < command.size() && isspace(command[i]))
- i++;
- }
-
- // get_argument is the main function for breaking a string down into separate command arguments
- // it mimics the way shells break down a command into an argv[] and unescapes the escaped characters on the way
-
- static std::string get_argument (const std::string& command, unsigned& i)
- {
- std::string result;
-#ifdef MSWINDOWS
-
- // as far as I know, there is only double-quoting and no escape character in DOS
- // so, how do you include a double-quote in an argument???
-
- bool dquote = false;
- for ( ; i < command.size(); i++)
- {
- char ch = command[i];
- if (!dquote && isspace(ch)) break;
- if (dquote)
- {
- if (ch == '\"')
- dquote = false;
- else
- result += ch;
- }
- else if (ch == '\"')
- dquote = true;
- else
- result += ch;
- }
-#else
- bool squote = false;
- bool dquote = false;
- bool escaped = false;
- for ( ; i < command.size(); i++)
- {
- char ch = command[i];
- if (!squote && !dquote && !escaped && isspace(ch)) break;
- if (escaped)
- {
- result += ch;
- escaped = false;
- }
- else if (squote)
- {
- if (ch == '\'')
- squote = false;
- else
- result += ch;
- }
- else if (ch == '\\')
- escaped = true;
- else if (dquote)
- {
- if (ch == '\"')
- dquote = false;
- else
- result += ch;
- }
- else if (ch == '\'')
- squote = true;
- else if (ch == '\"')
- dquote = true;
- else
- result += ch;
- }
-#endif
-
- return result;
- }
-
-
- // this function performs the reverse of the above on a single argument
- // it escapes special characters and quotes the argument if necessary ready for shell interpretation
-
- static std::string make_argument (const std::string& arg)
- {
- std::string result;
- bool needs_quotes = false;
-
- for (unsigned i = 0; i < arg.size(); i++)
- {
- switch (arg[i])
- {
- // set of characters requiring escapes
-#ifdef MSWINDOWS
-#else
- case '\\': case '\'': case '\"': case '`': case '(': case ')':
- case '&': case '|': case '<': case '>': case '*': case '?': case '!':
- result += '\\';
- result += arg[i];
- break;
-#endif
- // set of whitespace characters that force quoting
- case ' ':
- result += arg[i];
- needs_quotes = true;
- break;
- default:
- result += arg[i];
- break;
- }
- }
-
- if (needs_quotes)
- {
- result.insert(result.begin(), '"');
- result += '"';
- }
- return result;
- }
-
- static char* copy_string (const char* str)
- {
- char* result = new char[strlen(str)+1];
- strcpy(result,str);
- return result;
- }
-
- ////////////////////////////////////////////////////////////////////////////////
-
- arg_vector::arg_vector (void)
- {
- m_argv = 0;
- }
-
- arg_vector::arg_vector (const arg_vector& a)
- {
- m_argv = 0;
- *this = a;
- }
-
- arg_vector::arg_vector (char** a)
- {
- m_argv = 0;
- *this = a;
- }
-
- arg_vector::arg_vector (const std::string& command)
- {
- m_argv = 0;
- *this = command;
- }
-
- arg_vector::arg_vector (const char* command)
- {
- m_argv = 0;
- *this = command;
- }
-
- arg_vector::~arg_vector (void)
- {
- clear();
- }
-
- arg_vector& arg_vector::operator = (const arg_vector& a)
- {
- return *this = a.m_argv;
- }
-
- arg_vector& arg_vector::operator = (char** argv)
- {
- clear();
- for (unsigned i = 0; argv[i]; i++)
- operator += (argv[i]);
- return *this;
- }
-
- arg_vector& arg_vector::operator = (const std::string& command)
- {
- clear();
- for (unsigned i = 0; i < command.size(); )
- {
- std::string argument = get_argument(command, i);
- operator += (argument);
- skip_white(command, i);
- }
- return *this;
- }
-
- arg_vector& arg_vector::operator = (const char* command)
- {
- return operator = (std::string(command));
- }
-
- arg_vector& arg_vector::operator += (const std::string& str)
- {
- insert(size(), str);
- return *this;
- }
-
- arg_vector& arg_vector::operator -= (const std::string& str)
- {
- insert(0, str);
- return *this;
- }
-
- void arg_vector::insert (unsigned index, const std::string& str) throw(std::out_of_range)
- {
- if (index > size()) throw std::out_of_range("arg_vector::insert");
- // copy up to but not including index, then add the new argument, then copy the rest
- char** new_argv = new char*[size()+2];
- unsigned i = 0;
- for ( ; i < index; i++)
- new_argv[i] = copy_string(m_argv[i]);
- new_argv[index] = copy_string(str.c_str());
- for ( ; i < size(); i++)
- new_argv[i+1] = copy_string(m_argv[i]);
- new_argv[i+1] = 0;
- clear();
- m_argv = new_argv;
- }
-
- void arg_vector::clear (unsigned index) throw(std::out_of_range)
- {
- if (index >= size()) throw std::out_of_range("arg_vector::clear");
- // copy up to index, skip it, then copy the rest
- char** new_argv = new char*[size()];
- unsigned i = 0;
- for ( ; i < index; i++)
- new_argv[i] = copy_string(m_argv[i]);
- i++;
- for ( ; i < size(); i++)
- new_argv[i-1] = copy_string(m_argv[i]);
- new_argv[i-1] = 0;
- clear();
- m_argv = new_argv;
- }
-
- void arg_vector::clear(void)
- {
- if (m_argv)
- {
- for (unsigned i = 0; m_argv[i]; i++)
- delete[] m_argv[i];
- delete[] m_argv;
- m_argv = 0;
- }
- }
-
- unsigned arg_vector::size (void) const
- {
- unsigned i = 0;
- if (m_argv)
- while (m_argv[i])
- i++;
- return i;
- }
-
- arg_vector::operator char** (void) const
- {
- return m_argv;
- }
-
- char** arg_vector::argv (void) const
- {
- return m_argv;
- }
-
- char* arg_vector::operator [] (unsigned index) const throw(std::out_of_range)
- {
- if (index >= size()) throw std::out_of_range("arg_vector::operator[]");
- return m_argv[index];
- }
-
- char* arg_vector::argv0 (void) const throw(std::out_of_range)
- {
- return operator [] (0);
- }
-
- std::string arg_vector::image (void) const
- {
- std::string result;
- for (unsigned i = 0; i < size(); i++)
- {
- if (i) result += ' ';
- result += make_argument(m_argv[i]);
- }
- return result;
- }
-
- ////////////////////////////////////////////////////////////////////////////////
- // environment-vector
-
- // Windoze environment is a single string containing null-terminated
- // name=value strings and the whole terminated by a null
-
- // Unix environment is a null-terminated vector of pointers to null-terminated strings
-
- ////////////////////////////////////////////////////////////////////////////////
- // platform specifics
-
-#ifdef MSWINDOWS
- // Windows utilities
-
- // Windows environment variables are case-insensitive and I do comparisons by converting to lowercase
- static std::string lowercase(const std::string& val)
- {
- std::string text = val;
- for (unsigned i = 0; i < text.size(); i++)
- text[i] = tolower(text[i]);
- return text;
- }
-
- static unsigned envp_size(const char* envp)
- {
- unsigned size = 0;
- while (envp[size] || (size > 0 && envp[size-1])) size++;
- size++;
- return size;
- }
-
- static void envp_extract(std::string& name, std::string& value, const char* envp, unsigned& envi)
- {
- name.erase();
- value.erase();
- if (!envp[envi]) return;
- // some special variables start with '=' so ensure at least one character in the name
- name += envp[envi++];
- while(envp[envi] != '=')
- name += envp[envi++];
- envi++;
- while(envp[envi])
- value += envp[envi++];
- envi++;
- }
-
- static void envp_append(const std::string& name, const std::string& value, char* envp, unsigned& envi)
- {
- for (unsigned i = 0; i < name.size(); i++)
- envp[envi++] = name[i];
- envp[envi++] = '=';
- for (unsigned j = 0; j < value.size(); j++)
- envp[envi++] = value[j];
- envp[envi++] = '\0';
- envp[envi] = '\0';
- }
-
- static char* envp_copy(const char* envp)
- {
- unsigned size = envp_size(envp);
- char* result = new char[size];
- result[0] = '\0';
- unsigned i = 0;
- unsigned j = 0;
- while(envp[i])
- {
- std::string name;
- std::string value;
- envp_extract(name, value, envp, i);
- envp_append(name, value, result, j);
- }
- return result;
- }
-
- static void envp_clear(char*& envp)
- {
- if (envp)
- {
- delete[] envp;
- envp = 0;
- }
- }
-
- static bool envp_equal(const std::string& left, const std::string& right)
- {
- return lowercase(left) == lowercase(right);
- }
-
- static bool envp_less(const std::string& left, const std::string& right)
- {
- return lowercase(left) < lowercase(right);
- }
-
-#else
- // Unix utilities
-
- static unsigned envp_size(char* const* envp)
- {
- unsigned size = 0;
- while(envp[size]) size++;
- size++;
- return size;
- }
-
- static void envp_extract(std::string& name, std::string& value, char* const* envp, unsigned& envi)
- {
- name = "";
- value = "";
- if (!envp[envi]) return;
- unsigned i = 0;
- while(envp[envi][i] != '=')
- name += envp[envi][i++];
- i++;
- while(envp[envi][i])
- value += envp[envi][i++];
- envi++;
- }
-
- static void envp_append(const std::string& name, const std::string& value, char** envp, unsigned& envi)
- {
- std::string entry = name + "=" + value;
- envp[envi] = copy_string(entry.c_str());
- envi++;
- envp[envi] = 0;
- }
-
- static char** envp_copy(char* const* envp)
- {
- unsigned size = envp_size(envp);
- char** result = new char*[size];
- unsigned i = 0;
- unsigned j = 0;
- while(envp[i])
- {
- std::string name;
- std::string value;
- envp_extract(name, value, envp, i);
- envp_append(name, value, result, j);
- }
- return result;
- }
-
- static void envp_clear(char**& envp)
- {
- if (envp)
- {
- for (unsigned i = 0; envp[i]; i++)
- delete[] envp[i];
- delete[] envp;
- envp = 0;
- }
- }
-
- static bool envp_equal(const std::string& left, const std::string& right)
- {
- return left == right;
- }
-
- static bool envp_less(const std::string& left, const std::string& right)
- {
- return left < right;
- }
-
-#endif
- ////////////////////////////////////////////////////////////////////////////////
-
- env_vector::env_vector(void)
- {
-#ifdef MSWINDOWS
- char* env = (char*)GetEnvironmentStringsA();
- m_env = envp_copy(env);
- FreeEnvironmentStringsA(env);
-#else
- m_env = envp_copy(::environ);
-#endif
- }
-
- env_vector::env_vector (const env_vector& a)
- {
- m_env = 0;
- *this = a;
- }
-
- env_vector::~env_vector (void)
- {
- clear();
- }
-
- env_vector& env_vector::operator = (const env_vector& a)
- {
- clear();
- m_env = envp_copy(a.m_env);
- return *this;
- }
-
- void env_vector::clear(void)
- {
- envp_clear(m_env);
- }
-
- void env_vector::add(const std::string& name, const std::string& value)
- {
- // the trick is to add the value in alphabetic order
- // this is done by copying the existing environment string to a new
- // string, inserting the new value when a name greater than it is found
- unsigned size = envp_size(m_env);
-#ifdef MSWINDOWS
- unsigned new_size = size + name.size() + value.size() + 2;
- char* new_v = new char[new_size];
- new_v[0] = '\0';
-#else
- unsigned new_size = size + 1;
- char** new_v = new char*[new_size];
- new_v[0] = 0;
-#endif
- // now extract each name=value pair and check the ordering
- bool added = false;
- unsigned i = 0;
- unsigned j = 0;
- while(m_env[i])
- {
- std::string current_name;
- std::string current_value;
- envp_extract(current_name, current_value, m_env, i);
- if (envp_equal(name,current_name))
- {
- // replace an existing value
- envp_append(name, value, new_v, j);
- }
- else if (!added && envp_less(name,current_name))
- {
- // add the new value first, then the existing one
- envp_append(name, value, new_v, j);
- envp_append(current_name, current_value, new_v, j);
- added = true;
- }
- else
- {
- // just add the existing value
- envp_append(current_name, current_value, new_v, j);
- }
- }
- if (!added)
- envp_append(name, value, new_v, j);
- envp_clear(m_env);
- m_env = new_v;
- }
-
-
- bool env_vector::remove (const std::string& name)
- {
- bool result = false;
- // this is done by copying the existing environment string to a new string, but excluding the specified name
- unsigned size = envp_size(m_env);
-#ifdef MSWINDOWS
- char* new_v = new char[size];
- new_v[0] = '\0';
-#else
- char** new_v = new char*[size];
- new_v[0] = 0;
-#endif
- unsigned i = 0;
- unsigned j = 0;
- while(m_env[i])
- {
- std::string current_name;
- std::string current_value;
- envp_extract(current_name, current_value, m_env, i);
- if (envp_equal(name,current_name))
- result = true;
- else
- envp_append(current_name, current_value, new_v, j);
- }
- envp_clear(m_env);
- m_env = new_v;
- return result;
- }
-
- std::string env_vector::operator [] (const std::string& name) const
- {
- return get(name);
- }
-
- std::string env_vector::get (const std::string& name) const
- {
- unsigned i = 0;
- while(m_env[i])
- {
- std::string current_name;
- std::string current_value;
- envp_extract(current_name, current_value, m_env, i);
- if (envp_equal(name,current_name))
- return current_value;
- }
- return std::string();
- }
-
- unsigned env_vector::size (void) const
- {
- unsigned i = 0;
-#ifdef MSWINDOWS
- unsigned offset = 0;
- while(m_env[offset])
- {
- std::string current_name;
- std::string current_value;
- envp_extract(current_name, current_value, m_env, offset);
- i++;
- }
-#else
- while(m_env[i])
- i++;
-#endif
-
- return i;
- }
-
- std::pair<std::string,std::string> env_vector::operator [] (unsigned index) const throw(std::out_of_range)
- {
- return get(index);
- }
-
- std::pair<std::string,std::string> env_vector::get (unsigned index) const throw(std::out_of_range)
- {
- if (index >= size()) throw std::out_of_range("arg_vector::get");
- unsigned j = 0;
- for (unsigned i = 0; i < index; i++)
- {
- std::string current_name;
- std::string current_value;
- envp_extract(current_name, current_value, m_env, j);
- }
- std::string name;
- std::string value;
- envp_extract(name, value, m_env, j);
- return std::make_pair(name,value);
- }
-
- ENVIRON_TYPE env_vector::envp (void) const
- {
- return m_env;
- }
-
- ////////////////////////////////////////////////////////////////////////////////
- // Synchronous subprocess
- // Win32 implementation mostly cribbed from MSDN examples and then made (much) more readable
- // Unix implementation mostly from man pages and bitter experience
- ////////////////////////////////////////////////////////////////////////////////
-
-#ifdef MSWINDOWS
-
- subprocess::subprocess(void)
- {
- m_pid.hProcess = 0;
- m_job = 0;
- m_child_in = 0;
- m_child_out = 0;
- m_child_err = 0;
- m_err = 0;
- m_status = 0;
- }
-
-#else
-
- subprocess::subprocess(void)
- {
- m_pid = -1;
- m_child_in = -1;
- m_child_out = -1;
- m_child_err = -1;
- m_err = 0;
- m_status = 0;
- }
-
-#endif
-
-#ifdef MSWINDOWS
-
- subprocess::~subprocess(void)
- {
- if (m_pid.hProcess != 0)
- {
- close_stdin();
- close_stdout();
- close_stderr();
- kill();
- WaitForSingleObject(m_pid.hProcess, INFINITE);
- CloseHandle(m_pid.hThread);
- CloseHandle(m_pid.hProcess);
- CloseHandle(m_job);
- }
- }
-
-#else
-
- subprocess::~subprocess(void)
- {
- if (m_pid != -1)
- {
- close_stdin();
- close_stdout();
- close_stderr();
- kill();
- for (;;)
- {
- int wait_status = 0;
- int wait_ret_val = waitpid(m_pid, &wait_status, 0);
- if (wait_ret_val != -1 || errno != EINTR) break;
- }
- }
- }
-
-#endif
-
- void subprocess::add_variable(const std::string& name, const std::string& value)
- {
- m_env.add(name, value);
- }
-
- bool subprocess::remove_variable(const std::string& name)
- {
- return m_env.remove(name);
- }
-
-#ifdef MSWINDOWS
-
- bool subprocess::spawn(const std::string& path, const arg_vector& argv,
- bool connect_stdin, bool connect_stdout, bool connect_stderr)
- {
- bool result = true;
- // first create the pipes to be used to connect to the child stdin/out/err
- // If no pipes requested, then connect to the parent stdin/out/err
- // for some reason you have to create a pipe handle, then duplicate it
- // This is not well explained in MSDN but seems to work
- PIPE_TYPE parent_stdin = 0;
- if (!connect_stdin)
- parent_stdin = GetStdHandle(STD_INPUT_HANDLE);
- else
- {
- PIPE_TYPE tmp = 0;
- SECURITY_ATTRIBUTES inherit_handles = {sizeof(SECURITY_ATTRIBUTES), 0, TRUE};
- CreatePipe(&parent_stdin, &tmp, &inherit_handles, 0);
- DuplicateHandle(GetCurrentProcess(), tmp, GetCurrentProcess(), &m_child_in, 0, FALSE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS);
- }
-
- PIPE_TYPE parent_stdout = 0;
- if (!connect_stdout)
- parent_stdout = GetStdHandle(STD_OUTPUT_HANDLE);
- else
- {
- PIPE_TYPE tmp = 0;
- SECURITY_ATTRIBUTES inherit_handles = {sizeof(SECURITY_ATTRIBUTES), 0, TRUE};
- CreatePipe(&tmp, &parent_stdout, &inherit_handles, 0);
- DuplicateHandle(GetCurrentProcess(), tmp, GetCurrentProcess(), &m_child_out, 0, FALSE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS);
- }
-
- PIPE_TYPE parent_stderr = 0;
- if (!connect_stderr)
- parent_stderr = GetStdHandle(STD_ERROR_HANDLE);
- else
- {
- PIPE_TYPE tmp = 0;
- SECURITY_ATTRIBUTES inherit_handles = {sizeof(SECURITY_ATTRIBUTES), 0, TRUE};
- CreatePipe(&tmp, &parent_stderr, &inherit_handles, 0);
- DuplicateHandle(GetCurrentProcess(), tmp, GetCurrentProcess(), &m_child_err, 0, FALSE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS);
- }
-
- // Now create the subprocess
- // The horrible trick of creating a console window and hiding it seems to be required for the pipes to work
- // Note that the child will inherit a copy of the pipe handles
- STARTUPINFOA startup = {sizeof(STARTUPINFO),0,0,0,0,0,0,0,0,0,0,
- STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW,SW_HIDE,0,0,
- parent_stdin,parent_stdout,parent_stderr};
- bool created = CreateProcessA(path.c_str(),(char*)argv.image().c_str(),0,0,TRUE,CREATE_SUSPENDED,m_env.envp(),0,&startup,&m_pid) != 0;
- // close the parent copy of the pipe handles so that the pipes will be closed when the child releases them
- if (connect_stdin) CloseHandle(parent_stdin);
- if (connect_stdout) CloseHandle(parent_stdout);
- if (connect_stderr) CloseHandle(parent_stderr);
- if (!created)
- {
- m_err = GetLastError();
- close_stdin();
- close_stdout();
- close_stderr();
- result = false;
- }
- else
- {
- m_job = CreateJobObject(NULL, NULL);
- AssignProcessToJobObject(m_job, m_pid.hProcess);
- ResumeThread(m_pid.hThread);
-
- // The child process is now running so call the user's callback
- // The convention is that the callback can return false, in which case this will kill the child (if its still running)
- if (!callback())
- {
- result = false;
- kill();
- }
- close_stdin();
- close_stdout();
- close_stderr();
- // wait for the child to finish
- // TODO - kill the child if a timeout happens
- WaitForSingleObject(m_pid.hProcess, INFINITE);
- DWORD exit_status = 0;
- if (!GetExitCodeProcess(m_pid.hProcess, &exit_status))
- {
- m_err = GetLastError();
- result = false;
- }
- else if (exit_status != 0)
- result = false;
- m_status = (int)exit_status;
- CloseHandle(m_pid.hThread);
- CloseHandle(m_pid.hProcess);
- CloseHandle(m_job);
- }
- m_pid.hProcess = 0;
- return result;
- }
-
-#else
-
- bool subprocess::spawn(const std::string& path, const arg_vector& argv,
- bool connect_stdin, bool connect_stdout, bool connect_stderr)
- {
- bool result = true;
- // first create the pipes to be used to connect to the child stdin/out/err
-
- int stdin_pipe [2] = {-1, -1};
- if (connect_stdin)
- pipe(stdin_pipe);
-
- int stdout_pipe [2] = {-1, -1};
- if (connect_stdout)
- pipe(stdout_pipe);
-
- int stderr_pipe [2] = {-1, -1};
- if (connect_stderr)
- pipe(stderr_pipe);
-
- // now create the subprocess
- // In Unix, this is done by forking (creating two copies of the parent), then overwriting the child copy using exec
- m_pid = ::fork();
- switch(m_pid)
- {
- case -1: // failed to fork
- m_err = errno;
- if (connect_stdin)
- {
- ::close(stdin_pipe[0]);
- ::close(stdin_pipe[1]);
- }
- if (connect_stdout)
- {
- ::close(stdout_pipe[0]);
- ::close(stdout_pipe[1]);
- }
- if (connect_stderr)
- {
- ::close(stderr_pipe[0]);
- ::close(stderr_pipe[1]);
- }
- result = false;
- break;
- case 0: // in child;
- {
- // for each pipe, close the end of the duplicated pipe that is being used by the parent
- // and connect the child's end of the pipe to the appropriate standard I/O device
- if (connect_stdin)
- {
- ::close(stdin_pipe[1]);
- dup2(stdin_pipe[0],STDIN_FILENO);
- }
- if (connect_stdout)
- {
- ::close(stdout_pipe[0]);
- dup2(stdout_pipe[1],STDOUT_FILENO);
- }
- if (connect_stderr)
- {
- ::close(stderr_pipe[0]);
- dup2(stderr_pipe[1],STDERR_FILENO);
- }
- execve(path.c_str(), argv.argv(), m_env.envp());
- // will only ever get here if the exec() failed completely - *must* now exit the child process
- // by using errno, the parent has some chance of diagnosing the cause of the problem
- exit(errno);
- }
- break;
- default: // in parent
- {
- // for each pipe, close the end of the duplicated pipe that is being used by the child
- // and connect the parent's end of the pipe to the class members so that they are visible to the parent() callback
- if (connect_stdin)
- {
- ::close(stdin_pipe[0]);
- m_child_in = stdin_pipe[1];
- }
- if (connect_stdout)
- {
- ::close(stdout_pipe[1]);
- m_child_out = stdout_pipe[0];
- }
- if (connect_stderr)
- {
- ::close(stderr_pipe[1]);
- m_child_err = stderr_pipe[0];
- }
- // call the user's callback
- if (!callback())
- {
- result = false;
- kill();
- }
- // close the pipes and wait for the child to finish
- // wait exits on a signal which may be the child signalling its exit or may be an interrupt
- close_stdin();
- close_stdout();
- close_stderr();
- int wait_status = 0;
- for (;;)
- {
- int wait_ret_val = waitpid(m_pid, &wait_status, 0);
- if (wait_ret_val != -1 || errno != EINTR) break;
- }
- // establish whether an error occurred
- if (WIFSIGNALED(wait_status))
- {
- // set_error(errno);
- m_status = WTERMSIG(wait_status);
- result = false;
- }
- else if (WIFEXITED(wait_status))
- {
- m_status = WEXITSTATUS(wait_status);
- if (m_status != 0)
- result = false;
- }
- m_pid = -1;
- }
- break;
- }
- return result;
- }
-
-#endif
-
- bool subprocess::spawn(const std::string& command_line,
- bool connect_stdin, bool connect_stdout, bool connect_stderr)
- {
- arg_vector arguments = command_line;
- if (arguments.size() == 0) return false;
- std::string path = path_lookup(arguments.argv0());
- if (path.empty()) return false;
- return spawn(path, arguments, connect_stdin, connect_stdout, connect_stderr);
- }
-
- bool subprocess::callback(void)
- {
- return true;
- }
-
-#ifdef MSWINDOWS
-
- bool subprocess::kill (void)
- {
- if (!m_pid.hProcess) return false;
- close_stdin();
- close_stdout();
- close_stderr();
- if (!TerminateJobObject(m_job, (UINT)-1))
- {
- m_err = GetLastError();
- return false;
- }
- return true;
- }
-
-#else
-
- bool subprocess::kill (void)
- {
- if (m_pid == -1) return false;
- close_stdin();
- close_stdout();
- close_stderr();
- if (::kill(m_pid, SIGINT) == -1)
- {
- m_err = errno;
- return false;
- }
- return true;
- }
-
-#endif
-
-#ifdef MSWINDOWS
-
- int subprocess::write_stdin (std::string& buffer)
- {
- if (m_child_in == 0) return -1;
- // do a blocking write of the whole buffer
- DWORD bytes = 0;
- if (!WriteFile(m_child_in, buffer.c_str(), (DWORD)buffer.size(), &bytes, 0))
- {
- m_err = GetLastError();
- close_stdin();
- return -1;
- }
- // now discard that part of the buffer that was written
- if (bytes > 0)
- buffer.erase(0, bytes);
- return bytes;
- }
-
-#else
-
- int subprocess::write_stdin (std::string& buffer)
- {
- if (m_child_in == -1) return -1;
- // do a blocking write of the whole buffer
- int bytes = write(m_child_in, buffer.c_str(), buffer.size());
- if (bytes == -1)
- {
- m_err = errno;
- close_stdin();
- return -1;
- }
- // now discard that part of the buffer that was written
- if (bytes > 0)
- buffer.erase(0, bytes);
- return bytes;
- }
-
-#endif
-
-#ifdef MSWINDOWS
-
- int subprocess::read_stdout (std::string& buffer)
- {
- if (m_child_out == 0) return -1;
- DWORD bytes = 0;
- DWORD buffer_size = 256;
- char* tmp = new char[buffer_size];
- if (!ReadFile(m_child_out, tmp, buffer_size, &bytes, 0))
- {
- if (GetLastError() != ERROR_BROKEN_PIPE)
- m_err = GetLastError();
- close_stdout();
- delete[] tmp;
- return -1;
- }
- if (bytes == 0)
- {
- // EOF
- close_stdout();
- delete[] tmp;
- return -1;
- }
- buffer.append(tmp, bytes);
- delete[] tmp;
- return (int)bytes;
- }
-
-#else
-
- int subprocess::read_stdout (std::string& buffer)
- {
- if (m_child_out == -1) return -1;
- int buffer_size = 256;
- char* tmp = new char[buffer_size];
- int bytes = read(m_child_out, tmp, buffer_size);
- if (bytes == -1)
- {
- m_err = errno;
- close_stdout();
- delete[] tmp;
- return -1;
- }
- if (bytes == 0)
- {
- // EOF
- close_stdout();
- delete[] tmp;
- return -1;
- }
- buffer.append(tmp, bytes);
- delete[] tmp;
- return bytes;
- }
-
-#endif
-
-#ifdef MSWINDOWS
-
- int subprocess::read_stderr(std::string& buffer)
- {
- if (m_child_err == 0) return -1;
- DWORD bytes = 0;
- DWORD buffer_size = 256;
- char* tmp = new char[buffer_size];
- if (!ReadFile(m_child_err, tmp, buffer_size, &bytes, 0))
- {
- if (GetLastError() != ERROR_BROKEN_PIPE)
- m_err = GetLastError();
- close_stderr();
- delete[] tmp;
- return -1;
- }
- if (bytes == 0)
- {
- // EOF
- close_stderr();
- delete[] tmp;
- return -1;
- }
- buffer.append(tmp, bytes);
- delete[] tmp;
- return (int)bytes;
- }
-
-#else
-
- int subprocess::read_stderr (std::string& buffer)
- {
- if (m_child_err == -1) return -1;
- int buffer_size = 256;
- char* tmp = new char[buffer_size];
- int bytes = read(m_child_err, tmp, buffer_size);
- if (bytes == -1)
- {
- m_err = errno;
- close_stderr();
- delete[] tmp;
- return -1;
- }
- if (bytes == 0)
- {
- // EOF
- close_stderr();
- delete[] tmp;
- return -1;
- }
- buffer.append(tmp, bytes);
- delete[] tmp;
- return bytes;
- }
-
-#endif
-
-#ifdef MSWINDOWS
-
- void subprocess::close_stdin (void)
- {
- if (m_child_in)
- {
- CloseHandle(m_child_in);
- m_child_in = 0;
- }
- }
-
-#else
-
- void subprocess::close_stdin (void)
- {
- if (m_child_in != -1)
- {
- ::close(m_child_in);
- m_child_in = -1;
- }
- }
-
-#endif
-
-#ifdef MSWINDOWS
-
- void subprocess::close_stdout (void)
- {
- if (m_child_out)
- {
- CloseHandle(m_child_out);
- m_child_out = 0;
- }
- }
-
-#else
-
- void subprocess::close_stdout (void)
- {
- if (m_child_out != -1)
- {
- ::close(m_child_out);
- m_child_out = -1;
- }
- }
-
-#endif
-
-#ifdef MSWINDOWS
-
- void subprocess::close_stderr (void)
- {
- if (m_child_err)
- {
- CloseHandle(m_child_err);
- m_child_err = 0;
- }
- }
-
-#else
-
- void subprocess::close_stderr (void)
- {
- if (m_child_err != -1)
- {
- ::close(m_child_err);
- m_child_err = -1;
- }
- }
-
-#endif
-
- bool subprocess::error(void) const
- {
- return m_err != 0;
- }
-
- int subprocess::error_number(void) const
- {
- return m_err;
- }
-
-#ifdef MSWINDOWS
-
- std::string subprocess::error_text(void) const
- {
- if (m_err == 0) return std::string();
- char* message;
- FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
- 0,
- m_err,
- MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
- (LPTSTR)&message,
- 0,0);
- std::string result = message;
- LocalFree(message);
- // the error message is for some perverse reason newline terminated - remove this
- if (result[result.size()-1] == '\n')
- result.erase(result.end()-1);
- if (result[result.size()-1] == '\r')
- result.erase(result.end()-1);
- return result;
- }
-
-#else
-
- std::string subprocess::error_text(void) const
- {
- if (m_err == 0) return std::string();
- char* text = strerror(m_err);
- if (text) return std::string(text);
- return "error number " + dformat("%d",m_err);
- }
-
-#endif
-
- int subprocess::exit_status(void) const
- {
- return m_status;
- }
-
- ////////////////////////////////////////////////////////////////////////////////
- // backtick subprocess and operations
-
- backtick_subprocess::backtick_subprocess(void) : subprocess()
- {
- }
-
- bool backtick_subprocess::callback(void)
- {
- for (;;)
- {
- std::string buffer;
- int read_size = read_stdout(buffer);
- if (read_size < 0) break;
- m_text += buffer;
- }
- return !error();
- }
-
- bool backtick_subprocess::spawn(const std::string& path, const arg_vector& argv)
- {
- return subprocess::spawn(path, argv, false, true, false);
- }
-
- bool backtick_subprocess::spawn(const std::string& command_line)
- {
- return subprocess::spawn(command_line, false, true, false);
- }
-
- std::vector<std::string> backtick_subprocess::text(void) const
- {
- std::vector<std::string> result;
- // convert the raw text into a vector of strings, each corresponding to a line
- // in the process, strip out platform-specific line-endings
- for (unsigned i = 0; i < m_text.size(); i++)
- {
- // handle any kind of line-ending - Dos, Unix or MacOS
- switch(m_text[i])
- {
- case '\xd': // carriage-return - optionally followed by linefeed
- {
- // discard optional following linefeed
- if ((i+1 < m_text.size()) && (m_text[i+1] == '\xa'))
- i++;
- // add a new line to the end of the vector
- result.push_back(std::string());
- break;
- }
- case '\xa': // linefeed
- {
- // add a new line to the end of the vector
- result.push_back(std::string());
- break;
- }
- default:
- {
- result.back() += m_text[i];
- break;
- }
- }
- }
- // tidy up - if the last line ended with a newline, the vector will end with an empty string - discard this
- if ((result.size()) > 0 && result.back().empty())
- result.erase(result.end()-1);
- return result;
- }
-
- std::vector<std::string> backtick(const std::string& path, const arg_vector& argv)
- {
- backtick_subprocess sub;
- sub.spawn(path, argv);
- return sub.text();
- }
-
- std::vector<std::string> backtick(const std::string& command_line)
- {
- backtick_subprocess sub;
- sub.spawn(command_line);
- return sub.text();
- }
-
- ////////////////////////////////////////////////////////////////////////////////
- // Asynchronous subprocess
-
-#ifdef MSWINDOWS
-
- async_subprocess::async_subprocess(void)
- {
- m_pid.hProcess = 0;
- m_job = 0;
- m_child_in = 0;
- m_child_out = 0;
- m_child_err = 0;
- m_err = 0;
- m_status = 0;
- }
-
-#else
-
- async_subprocess::async_subprocess(void)
- {
- m_pid = -1;
- m_child_in = -1;
- m_child_out = -1;
- m_child_err = -1;
- m_err = 0;
- m_status = 0;
- }
-
-#endif
-
-#ifdef MSWINDOWS
-
- async_subprocess::~async_subprocess(void)
- {
- if (m_pid.hProcess != 0)
- {
- close_stdin();
- close_stdout();
- close_stderr();
- kill();
- WaitForSingleObject(m_pid.hProcess, INFINITE);
- CloseHandle(m_pid.hThread);
- CloseHandle(m_pid.hProcess);
- CloseHandle(m_job);
- }
- }
-
-#else
-
- async_subprocess::~async_subprocess(void)
- {
- if (m_pid != -1)
- {
- close_stdin();
- close_stdout();
- close_stderr();
- kill();
- for (;;)
- {
- int wait_status = 0;
- int wait_ret_val = waitpid(m_pid, &wait_status, 0);
- if (wait_ret_val != -1 || errno != EINTR) break;
- }
- }
- }
-
-#endif
-
- void async_subprocess::set_error(int e)
- {
- m_err = e;
- }
-
- void async_subprocess::add_variable(const std::string& name, const std::string& value)
- {
- m_env.add(name, value);
- }
-
- bool async_subprocess::remove_variable(const std::string& name)
- {
- return m_env.remove(name);
- }
-
-#ifdef MSWINDOWS
-
- bool async_subprocess::spawn(const std::string& path, const arg_vector& argv,
- bool connect_stdin, bool connect_stdout, bool connect_stderr)
- {
- bool result = true;
- // first create the pipes to be used to connect to the child stdin/out/err
- // If no pipes requested, then connect to the parent stdin/out/err
- // for some reason you have to create a pipe handle, then duplicate it
- // This is not well explained in MSDN but seems to work
- PIPE_TYPE parent_stdin = 0;
- if (!connect_stdin)
- parent_stdin = GetStdHandle(STD_INPUT_HANDLE);
- else
- {
- PIPE_TYPE tmp = 0;
- SECURITY_ATTRIBUTES inherit_handles = {sizeof(SECURITY_ATTRIBUTES), 0, TRUE};
- CreatePipe(&parent_stdin, &tmp, &inherit_handles, 0);
- DuplicateHandle(GetCurrentProcess(), tmp, GetCurrentProcess(), &m_child_in, 0, FALSE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS);
- }
-
- PIPE_TYPE parent_stdout = 0;
- if (!connect_stdout)
- parent_stdout = GetStdHandle(STD_OUTPUT_HANDLE);
- else
- {
- PIPE_TYPE tmp = 0;
- SECURITY_ATTRIBUTES inherit_handles = {sizeof(SECURITY_ATTRIBUTES), 0, TRUE};
- CreatePipe(&tmp, &parent_stdout, &inherit_handles, 0);
- DuplicateHandle(GetCurrentProcess(), tmp, GetCurrentProcess(), &m_child_out, 0, FALSE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS);
- }
-
- PIPE_TYPE parent_stderr = 0;
- if (!connect_stderr)
- parent_stderr = GetStdHandle(STD_ERROR_HANDLE);
- else
- {
- PIPE_TYPE tmp = 0;
- SECURITY_ATTRIBUTES inherit_handles = {sizeof(SECURITY_ATTRIBUTES), 0, TRUE};
- CreatePipe(&tmp, &parent_stderr, &inherit_handles, 0);
- DuplicateHandle(GetCurrentProcess(), tmp, GetCurrentProcess(), &m_child_err, 0, FALSE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS);
- }
-
- // Now create the subprocess
- // The horrible trick of creating a console window and hiding it seems to be required for the pipes to work
- // Note that the child will inherit a copy of the pipe handles
- STARTUPINFOA startup = {sizeof(STARTUPINFO),0,0,0,0,0,0,0,0,0,0,
- STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW,SW_HIDE,0,0,
- parent_stdin,parent_stdout,parent_stderr};
- bool created = CreateProcessA(path.c_str(),(char*)argv.image().c_str(),0,0,TRUE,CREATE_SUSPENDED,m_env.envp(),0,&startup,&m_pid) != 0;
- // close the parent copy of the pipe handles so that the pipes will be closed when the child releases them
- if (connect_stdin) CloseHandle(parent_stdin);
- if (connect_stdout) CloseHandle(parent_stdout);
- if (connect_stderr) CloseHandle(parent_stderr);
- if (!created)
- {
- set_error(GetLastError());
- close_stdin();
- close_stdout();
- close_stderr();
- result = false;
- }
- else
- {
- m_job = CreateJobObject(NULL, NULL);
- AssignProcessToJobObject(m_job, m_pid.hProcess);
- ResumeThread(m_pid.hThread);
- }
- return result;
- }
-
-#else
-
- bool async_subprocess::spawn(const std::string& path, const arg_vector& argv,
- bool connect_stdin, bool connect_stdout, bool connect_stderr)
- {
- bool result = true;
- // first create the pipes to be used to connect to the child stdin/out/err
-
- int stdin_pipe [2] = {-1, -1};
- if (connect_stdin)
- pipe(stdin_pipe);
-
- int stdout_pipe [2] = {-1, -1};
- if (connect_stdout)
- pipe(stdout_pipe);
-
- int stderr_pipe [2] = {-1, -1};
- if (connect_stderr)
- pipe(stderr_pipe);
-
- // now create the subprocess
- // In Unix, this is done by forking (creating two copies of the parent), then overwriting the child copy using exec
- m_pid = ::fork();
- switch(m_pid)
- {
- case -1: // failed to fork
- set_error(errno);
- if (connect_stdin)
- {
- ::close(stdin_pipe[0]);
- ::close(stdin_pipe[1]);
- }
- if (connect_stdout)
- {
- ::close(stdout_pipe[0]);
- ::close(stdout_pipe[1]);
- }
- if (connect_stderr)
- {
- ::close(stderr_pipe[0]);
- ::close(stderr_pipe[1]);
- }
- result = false;
- break;
- case 0: // in child;
- {
- // for each pipe, close the end of the duplicated pipe that is being used by the parent
- // and connect the child's end of the pipe to the appropriate standard I/O device
- if (connect_stdin)
- {
- ::close(stdin_pipe[1]);
- dup2(stdin_pipe[0],STDIN_FILENO);
- }
- if (connect_stdout)
- {
- ::close(stdout_pipe[0]);
- dup2(stdout_pipe[1],STDOUT_FILENO);
- }
- if (connect_stderr)
- {
- ::close(stderr_pipe[0]);
- dup2(stderr_pipe[1],STDERR_FILENO);
- }
- execve(path.c_str(), argv.argv(), m_env.envp());
- // will only ever get here if the exec() failed completely - *must* now exit the child process
- // by using errno, the parent has some chance of diagnosing the cause of the problem
- exit(errno);
- }
- break;
- default: // in parent
- {
- // for each pipe, close the end of the duplicated pipe that is being used by the child
- // and connect the parent's end of the pipe to the class members so that they are visible to the parent() callback
- if (connect_stdin)
- {
- ::close(stdin_pipe[0]);
- m_child_in = stdin_pipe[1];
- if (fcntl(m_child_in, F_SETFL, O_NONBLOCK) == -1)
- {
- set_error(errno);
- result = false;
- }
- }
- if (connect_stdout)
- {
- ::close(stdout_pipe[1]);
- m_child_out = stdout_pipe[0];
- if (fcntl(m_child_out, F_SETFL, O_NONBLOCK) == -1)
- {
- set_error(errno);
- result = false;
- }
- }
- if (connect_stderr)
- {
- ::close(stderr_pipe[1]);
- m_child_err = stderr_pipe[0];
- if (fcntl(m_child_err, F_SETFL, O_NONBLOCK) == -1)
- {
- set_error(errno);
- result = false;
- }
- }
- }
- break;
- }
- return result;
- }
-
-#endif
-
- bool async_subprocess::spawn(const std::string& command_line,
- bool connect_stdin, bool connect_stdout, bool connect_stderr)
- {
- arg_vector arguments = command_line;
- if (arguments.size() == 0) return false;
- std::string path = path_lookup(arguments.argv0());
- if (path.empty()) return false;
- return spawn(path, arguments, connect_stdin, connect_stdout, connect_stderr);
- }
-
- bool async_subprocess::callback(void)
- {
- return true;
- }
-
-#ifdef MSWINDOWS
-
- bool async_subprocess::tick(void)
- {
- bool result = true;
- if (!callback())
- kill();
- DWORD exit_status = 0;
- if (!GetExitCodeProcess(m_pid.hProcess, &exit_status))
- {
- set_error(GetLastError());
- result = false;
- }
- else if (exit_status != STILL_ACTIVE)
- {
- CloseHandle(m_pid.hThread);
- CloseHandle(m_pid.hProcess);
- CloseHandle(m_job);
- m_pid.hProcess = 0;
- result = false;
- }
- m_status = (int)exit_status;
- return result;
- }
-
-#else
-
- bool async_subprocess::tick(void)
- {
- bool result = true;
- if (!callback())
- kill();
- int wait_status = 0;
- int wait_ret_val = waitpid(m_pid, &wait_status, WNOHANG);
- if (wait_ret_val == -1 && errno != EINTR)
- {
- set_error(errno);
- result = false;
- }
- else if (wait_ret_val != 0)
- {
- // the only states that indicate a terminated child are WIFSIGNALLED and WIFEXITED
- if (WIFSIGNALED(wait_status))
- {
- // set_error(errno);
- m_status = WTERMSIG(wait_status);
- result = false;
- }
- else if (WIFEXITED(wait_status))
- {
- // child has exited
- m_status = WEXITSTATUS(wait_status);
- result = false;
- }
- }
- if (!result)
- m_pid = -1;
- return result;
- }
-
-#endif
-
-#ifdef MSWINDOWS
-
- bool async_subprocess::kill(void)
- {
- if (!m_pid.hProcess) return false;
- close_stdin();
- close_stdout();
- close_stderr();
- if (!TerminateJobObject(m_job, (UINT)-1))
- {
- set_error(GetLastError());
- return false;
- }
- return true;
- }
-
-#else
-
- bool async_subprocess::kill(void)
- {
- if (m_pid == -1) return false;
- close_stdin();
- close_stdout();
- close_stderr();
- if (::kill(m_pid, SIGINT) == -1)
- {
- set_error(errno);
- return false;
- }
- return true;
- }
-
-#endif
-
-#ifdef MSWINDOWS
-
- int async_subprocess::write_stdin (std::string& buffer)
- {
- if (m_child_in == 0) return -1;
- // there doesn't seem to be a way of doing non-blocking writes under Windoze
- DWORD bytes = 0;
- if (!WriteFile(m_child_in, buffer.c_str(), (DWORD)buffer.size(), &bytes, 0))
- {
- set_error(GetLastError());
- close_stdin();
- return -1;
- }
- // now discard that part of the buffer that was written
- if (bytes > 0)
- buffer.erase(0, bytes);
- return (int)bytes;
- }
-
-#else
-
- int async_subprocess::write_stdin (std::string& buffer)
- {
- if (m_child_in == -1) return -1;
- // relies on the pipe being non-blocking
- // This does block under Windoze
- int bytes = write(m_child_in, buffer.c_str(), buffer.size());
- if (bytes == -1 && errno == EAGAIN)
- {
- // not ready
- return 0;
- }
- if (bytes == -1)
- {
- // error on write - close the pipe and give up
- set_error(errno);
- close_stdin();
- return -1;
- }
- // successful write
- // now discard that part of the buffer that was written
- if (bytes > 0)
- buffer.erase(0, bytes);
- return bytes;
- }
-
-#endif
-
-#ifdef MSWINDOWS
-
- int async_subprocess::read_stdout (std::string& buffer)
- {
- if (m_child_out == 0) return -1;
- // peek at the buffer to see how much data there is in the first place
- DWORD buffer_size = 0;
- if (!PeekNamedPipe(m_child_out, 0, 0, 0, &buffer_size, 0))
- {
- if (GetLastError() != ERROR_BROKEN_PIPE)
- set_error(GetLastError());
- close_stdout();
- return -1;
- }
- if (buffer_size == 0) return 0;
- DWORD bytes = 0;
- char* tmp = new char[buffer_size];
- if (!ReadFile(m_child_out, tmp, buffer_size, &bytes, 0))
- {
- set_error(GetLastError());
- close_stdout();
- delete[] tmp;
- return -1;
- }
- if (bytes == 0)
- {
- // EOF
- close_stdout();
- delete[] tmp;
- return -1;
- }
- buffer.append(tmp, bytes);
- delete[] tmp;
- return (int)bytes;
- }
-
-#else
-
- int async_subprocess::read_stdout (std::string& buffer)
- {
- if (m_child_out == -1) return -1;
- // rely on the pipe being non-blocking
- int buffer_size = 256;
- char* tmp = new char[buffer_size];
- int bytes = read(m_child_out, tmp, buffer_size);
- if (bytes == -1 && errno == EAGAIN)
- {
- // not ready
- delete[] tmp;
- return 0;
- }
- if (bytes == -1)
- {
- // error
- set_error(errno);
- close_stdout();
- delete[] tmp;
- return -1;
- }
- if (bytes == 0)
- {
- // EOF
- close_stdout();
- delete[] tmp;
- return -1;
- }
- // successful read
- buffer.append(tmp, bytes);
- delete[] tmp;
- return bytes;
- }
-
-#endif
-
-#ifdef MSWINDOWS
-
- int async_subprocess::read_stderr (std::string& buffer)
- {
- if (m_child_err == 0) return -1;
- // peek at the buffer to see how much data there is in the first place
- DWORD buffer_size = 0;
- if (!PeekNamedPipe(m_child_err, 0, 0, 0, &buffer_size, 0))
- {
- if (GetLastError() != ERROR_BROKEN_PIPE)
- set_error(GetLastError());
- close_stderr();
- return -1;
- }
- if (buffer_size == 0) return 0;
- DWORD bytes = 0;
- char* tmp = new char[buffer_size];
- if (!ReadFile(m_child_err, tmp, buffer_size, &bytes, 0))
- {
- set_error(GetLastError());
- close_stderr();
- delete[] tmp;
- return -1;
- }
- if (bytes == 0)
- {
- // EOF
- close_stderr();
- delete[] tmp;
- return -1;
- }
- buffer.append(tmp, bytes);
- delete[] tmp;
- return (int)bytes;
- }
-
-#else
-
- int async_subprocess::read_stderr (std::string& buffer)
- {
- if (m_child_err == -1) return -1;
- // rely on the pipe being non-blocking
- int buffer_size = 256;
- char* tmp = new char[buffer_size];
- int bytes = read(m_child_err, tmp, buffer_size);
- if (bytes == -1 && errno == EAGAIN)
- {
- // not ready
- delete[] tmp;
- return 0;
- }
- if (bytes == -1)
- {
- // error
- set_error(errno);
- close_stderr();
- delete[] tmp;
- return -1;
- }
- if (bytes == 0)
- {
- // EOF
- close_stderr();
- delete[] tmp;
- return -1;
- }
- // successful read
- buffer.append(tmp, bytes);
- delete[] tmp;
- return bytes;
- }
-
-#endif
-
-#ifdef MSWINDOWS
-
- void async_subprocess::close_stdin (void)
- {
- if (m_child_in)
- {
- CloseHandle(m_child_in);
- m_child_in = 0;
- }
- }
-
-#else
-
- void async_subprocess::close_stdin (void)
- {
- if (m_child_in != -1)
- {
- ::close(m_child_in);
- m_child_in = -1;
- }
- }
-
-#endif
-
-#ifdef MSWINDOWS
-
- void async_subprocess::close_stdout (void)
- {
- if (m_child_out)
- {
- CloseHandle(m_child_out);
- m_child_out = 0;
- }
- }
-
-#else
-
- void async_subprocess::close_stdout (void)
- {
- if (m_child_out != -1)
- {
- ::close(m_child_out);
- m_child_out = -1;
- }
- }
-
-#endif
-
-#ifdef MSWINDOWS
-
- void async_subprocess::close_stderr (void)
- {
- if (m_child_err)
- {
- CloseHandle(m_child_err);
- m_child_err = 0;
- }
- }
-
-#else
-
- void async_subprocess::close_stderr (void)
- {
- if (m_child_err != -1)
- {
- ::close(m_child_err);
- m_child_err = -1;
- }
- }
-
-#endif
-
- bool async_subprocess::error(void) const
- {
- return m_err != 0;
- }
-
- int async_subprocess::error_number(void) const
- {
- return m_err;
- }
-
-#ifdef MSWINDOWS
-
- std::string async_subprocess::error_text(void) const
- {
- if (m_err == 0) return std::string();
- char* message;
- FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
- 0,
- m_err,
- MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
- (LPTSTR)&message,
- 0,0);
- std::string result = message;
- LocalFree(message);
- // the error message is for some perverse reason newline terminated - remove this
- if (result[result.size()-1] == '\n')
- result.erase(result.end()-1);
- if (result[result.size()-1] == '\r')
- result.erase(result.end()-1);
- return result;
- }
-
-#else
-
- std::string async_subprocess::error_text(void) const
- {
- if (m_err == 0) return std::string();
- char* text = strerror(m_err);
- if (text) return std::string(text);
- return "error number " + dformat("%d",m_err);
- }
-
-#endif
-
- int async_subprocess::exit_status(void) const
- {
- return m_status;
- }
-
- ////////////////////////////////////////////////////////////////////////////////
-
-} // end namespace stlplus
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+// Author: Andy Rushton\r
+// Copyright: (c) Southampton University 1999-2004\r
+// (c) Andy Rushton 2004 onwards\r
+// License: BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+// Bug fix by Alistair Low: kill on Windows now kills grandchild processes as\r
+// well as the child process. This is done using jobs - which has to be\r
+// enabled by stating that the version of Windows is at least 5.0\r
+#if defined(_WIN32) || defined(_WIN32_WCE)\r
+#define _WIN32_WINNT 0x0500\r
+#endif\r
+\r
+#include "subprocesses.hpp"\r
+#include "file_system.hpp"\r
+#include "dprintf.hpp"\r
+#include <ctype.h>\r
+#include <string.h>\r
+#include <stdlib.h>\r
+\r
+#ifdef MSWINDOWS\r
+#ifdef __BORLANDC__\r
+// missing declaration in Borland headers\r
+LPTCH WINAPI GetEnvironmentStringsA(void);\r
+#endif\r
+#else\r
+extern char** environ;\r
+#include <signal.h>\r
+#include <errno.h>\r
+#include <sys/wait.h>\r
+#include <unistd.h>\r
+#include <fcntl.h>\r
+#endif\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // argument-vector related stuff\r
+\r
+ static void skip_white (const std::string& command, unsigned& i)\r
+ {\r
+ while(i < command.size() && isspace(command[i]))\r
+ i++;\r
+ }\r
+\r
+ // get_argument is the main function for breaking a string down into separate command arguments\r
+ // it mimics the way shells break down a command into an argv[] and unescapes the escaped characters on the way\r
+\r
+ static std::string get_argument (const std::string& command, unsigned& i)\r
+ {\r
+ std::string result;\r
+#ifdef MSWINDOWS\r
+\r
+ // as far as I know, there is only double-quoting and no escape character in DOS\r
+ // so, how do you include a double-quote in an argument???\r
+\r
+ bool dquote = false;\r
+ for ( ; i < command.size(); i++)\r
+ {\r
+ char ch = command[i];\r
+ if (!dquote && isspace(ch)) break;\r
+ if (dquote)\r
+ {\r
+ if (ch == '\"')\r
+ dquote = false;\r
+ else\r
+ result += ch;\r
+ }\r
+ else if (ch == '\"')\r
+ dquote = true;\r
+ else\r
+ result += ch;\r
+ }\r
+#else\r
+ bool squote = false;\r
+ bool dquote = false;\r
+ bool escaped = false;\r
+ for ( ; i < command.size(); i++)\r
+ {\r
+ char ch = command[i];\r
+ if (!squote && !dquote && !escaped && isspace(ch)) break;\r
+ if (escaped)\r
+ {\r
+ result += ch;\r
+ escaped = false;\r
+ }\r
+ else if (squote)\r
+ {\r
+ if (ch == '\'')\r
+ squote = false;\r
+ else\r
+ result += ch;\r
+ }\r
+ else if (ch == '\\')\r
+ escaped = true;\r
+ else if (dquote)\r
+ {\r
+ if (ch == '\"')\r
+ dquote = false;\r
+ else\r
+ result += ch;\r
+ }\r
+ else if (ch == '\'')\r
+ squote = true;\r
+ else if (ch == '\"')\r
+ dquote = true;\r
+ else\r
+ result += ch;\r
+ }\r
+#endif\r
+\r
+ return result;\r
+ }\r
+\r
+\r
+ // this function performs the reverse of the above on a single argument\r
+ // it escapes special characters and quotes the argument if necessary ready for shell interpretation\r
+\r
+ static std::string make_argument (const std::string& arg)\r
+ {\r
+ std::string result;\r
+ bool needs_quotes = false;\r
+\r
+ for (unsigned i = 0; i < arg.size(); i++)\r
+ {\r
+ switch (arg[i])\r
+ {\r
+ // set of characters requiring escapes\r
+#ifdef MSWINDOWS\r
+#else\r
+ case '\\': case '\'': case '\"': case '`': case '(': case ')':\r
+ case '&': case '|': case '<': case '>': case '*': case '?': case '!':\r
+ result += '\\';\r
+ result += arg[i];\r
+ break;\r
+#endif\r
+ // set of whitespace characters that force quoting\r
+ case ' ':\r
+ result += arg[i];\r
+ needs_quotes = true;\r
+ break;\r
+ default:\r
+ result += arg[i];\r
+ break;\r
+ }\r
+ }\r
+\r
+ if (needs_quotes)\r
+ {\r
+ result.insert(result.begin(), '"');\r
+ result += '"';\r
+ }\r
+ return result;\r
+ }\r
+\r
+ static char* copy_string (const char* str)\r
+ {\r
+ char* result = new char[strlen(str)+1];\r
+ strcpy(result,str);\r
+ return result;\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+\r
+ arg_vector::arg_vector (void)\r
+ {\r
+ m_argv = 0;\r
+ }\r
+\r
+ arg_vector::arg_vector (const arg_vector& a)\r
+ {\r
+ m_argv = 0;\r
+ *this = a;\r
+ }\r
+\r
+ arg_vector::arg_vector (char** a)\r
+ {\r
+ m_argv = 0;\r
+ *this = a;\r
+ }\r
+\r
+ arg_vector::arg_vector (const std::string& command)\r
+ {\r
+ m_argv = 0;\r
+ *this = command;\r
+ }\r
+\r
+ arg_vector::arg_vector (const char* command)\r
+ {\r
+ m_argv = 0;\r
+ *this = command;\r
+ }\r
+\r
+ arg_vector::~arg_vector (void)\r
+ {\r
+ clear();\r
+ }\r
+\r
+ arg_vector& arg_vector::operator = (const arg_vector& a)\r
+ {\r
+ return *this = a.m_argv;\r
+ }\r
+\r
+ arg_vector& arg_vector::operator = (char** argv)\r
+ {\r
+ clear();\r
+ for (unsigned i = 0; argv[i]; i++)\r
+ operator += (argv[i]);\r
+ return *this;\r
+ }\r
+\r
+ arg_vector& arg_vector::operator = (const std::string& command)\r
+ {\r
+ clear();\r
+ for (unsigned i = 0; i < command.size(); )\r
+ {\r
+ std::string argument = get_argument(command, i);\r
+ operator += (argument);\r
+ skip_white(command, i);\r
+ }\r
+ return *this;\r
+ }\r
+\r
+ arg_vector& arg_vector::operator = (const char* command)\r
+ {\r
+ return operator = (std::string(command));\r
+ }\r
+\r
+ arg_vector& arg_vector::operator += (const std::string& str)\r
+ {\r
+ insert(size(), str);\r
+ return *this;\r
+ }\r
+\r
+ arg_vector& arg_vector::operator -= (const std::string& str)\r
+ {\r
+ insert(0, str);\r
+ return *this;\r
+ }\r
+\r
+ void arg_vector::insert (unsigned index, const std::string& str) throw(std::out_of_range)\r
+ {\r
+ if (index > size()) throw std::out_of_range("arg_vector::insert");\r
+ // copy up to but not including index, then add the new argument, then copy the rest\r
+ char** new_argv = new char*[size()+2];\r
+ unsigned i = 0;\r
+ for ( ; i < index; i++)\r
+ new_argv[i] = copy_string(m_argv[i]);\r
+ new_argv[index] = copy_string(str.c_str());\r
+ for ( ; i < size(); i++)\r
+ new_argv[i+1] = copy_string(m_argv[i]);\r
+ new_argv[i+1] = 0;\r
+ clear();\r
+ m_argv = new_argv;\r
+ }\r
+\r
+ void arg_vector::clear (unsigned index) throw(std::out_of_range)\r
+ {\r
+ if (index >= size()) throw std::out_of_range("arg_vector::clear");\r
+ // copy up to index, skip it, then copy the rest\r
+ char** new_argv = new char*[size()];\r
+ unsigned i = 0;\r
+ for ( ; i < index; i++)\r
+ new_argv[i] = copy_string(m_argv[i]);\r
+ i++;\r
+ for ( ; i < size(); i++)\r
+ new_argv[i-1] = copy_string(m_argv[i]);\r
+ new_argv[i-1] = 0;\r
+ clear();\r
+ m_argv = new_argv;\r
+ }\r
+\r
+ void arg_vector::clear(void)\r
+ {\r
+ if (m_argv)\r
+ {\r
+ for (unsigned i = 0; m_argv[i]; i++)\r
+ delete[] m_argv[i];\r
+ delete[] m_argv;\r
+ m_argv = 0;\r
+ }\r
+ }\r
+\r
+ unsigned arg_vector::size (void) const\r
+ {\r
+ unsigned i = 0;\r
+ if (m_argv)\r
+ while (m_argv[i])\r
+ i++;\r
+ return i;\r
+ }\r
+\r
+ arg_vector::operator char** (void) const\r
+ {\r
+ return m_argv;\r
+ }\r
+\r
+ char** arg_vector::argv (void) const\r
+ {\r
+ return m_argv;\r
+ }\r
+\r
+ char* arg_vector::operator [] (unsigned index) const throw(std::out_of_range)\r
+ {\r
+ if (index >= size()) throw std::out_of_range("arg_vector::operator[]");\r
+ return m_argv[index];\r
+ }\r
+\r
+ char* arg_vector::argv0 (void) const throw(std::out_of_range)\r
+ {\r
+ return operator [] (0);\r
+ }\r
+\r
+ std::string arg_vector::image (void) const\r
+ {\r
+ std::string result;\r
+ for (unsigned i = 0; i < size(); i++)\r
+ {\r
+ if (i) result += ' ';\r
+ result += make_argument(m_argv[i]);\r
+ }\r
+ return result;\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // environment-vector\r
+\r
+ // Windoze environment is a single string containing null-terminated\r
+ // name=value strings and the whole terminated by a null\r
+\r
+ // Unix environment is a null-terminated vector of pointers to null-terminated strings\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // platform specifics\r
+\r
+#ifdef MSWINDOWS\r
+ // Windows utilities\r
+\r
+ // Windows environment variables are case-insensitive and I do comparisons by converting to lowercase\r
+ static std::string lowercase(const std::string& val)\r
+ {\r
+ std::string text = val;\r
+ for (unsigned i = 0; i < text.size(); i++)\r
+ text[i] = tolower(text[i]);\r
+ return text;\r
+ }\r
+\r
+ static unsigned envp_size(const char* envp)\r
+ {\r
+ unsigned size = 0;\r
+ while (envp[size] || (size > 0 && envp[size-1])) size++;\r
+ size++;\r
+ return size;\r
+ }\r
+\r
+ static void envp_extract(std::string& name, std::string& value, const char* envp, unsigned& envi)\r
+ {\r
+ name.erase();\r
+ value.erase();\r
+ if (!envp[envi]) return;\r
+ // some special variables start with '=' so ensure at least one character in the name\r
+ name += envp[envi++];\r
+ while(envp[envi] != '=')\r
+ name += envp[envi++];\r
+ envi++;\r
+ while(envp[envi])\r
+ value += envp[envi++];\r
+ envi++;\r
+ }\r
+\r
+ static void envp_append(const std::string& name, const std::string& value, char* envp, unsigned& envi)\r
+ {\r
+ for (unsigned i = 0; i < name.size(); i++)\r
+ envp[envi++] = name[i];\r
+ envp[envi++] = '=';\r
+ for (unsigned j = 0; j < value.size(); j++)\r
+ envp[envi++] = value[j];\r
+ envp[envi++] = '\0';\r
+ envp[envi] = '\0';\r
+ }\r
+\r
+ static char* envp_copy(const char* envp)\r
+ {\r
+ unsigned size = envp_size(envp);\r
+ char* result = new char[size];\r
+ result[0] = '\0';\r
+ unsigned i = 0;\r
+ unsigned j = 0;\r
+ while(envp[i])\r
+ {\r
+ std::string name;\r
+ std::string value;\r
+ envp_extract(name, value, envp, i);\r
+ envp_append(name, value, result, j);\r
+ }\r
+ return result;\r
+ }\r
+\r
+ static void envp_clear(char*& envp)\r
+ {\r
+ if (envp)\r
+ {\r
+ delete[] envp;\r
+ envp = 0;\r
+ }\r
+ }\r
+\r
+ static bool envp_equal(const std::string& left, const std::string& right)\r
+ {\r
+ return lowercase(left) == lowercase(right);\r
+ }\r
+\r
+ static bool envp_less(const std::string& left, const std::string& right)\r
+ {\r
+ return lowercase(left) < lowercase(right);\r
+ }\r
+\r
+#else\r
+ // Unix utilities\r
+\r
+ static unsigned envp_size(char* const* envp)\r
+ {\r
+ unsigned size = 0;\r
+ while(envp[size]) size++;\r
+ size++;\r
+ return size;\r
+ }\r
+\r
+ static void envp_extract(std::string& name, std::string& value, char* const* envp, unsigned& envi)\r
+ {\r
+ name = "";\r
+ value = "";\r
+ if (!envp[envi]) return;\r
+ unsigned i = 0;\r
+ while(envp[envi][i] != '=')\r
+ name += envp[envi][i++];\r
+ i++;\r
+ while(envp[envi][i])\r
+ value += envp[envi][i++];\r
+ envi++;\r
+ }\r
+\r
+ static void envp_append(const std::string& name, const std::string& value, char** envp, unsigned& envi)\r
+ {\r
+ std::string entry = name + "=" + value;\r
+ envp[envi] = copy_string(entry.c_str());\r
+ envi++;\r
+ envp[envi] = 0;\r
+ }\r
+\r
+ static char** envp_copy(char* const* envp)\r
+ {\r
+ unsigned size = envp_size(envp);\r
+ char** result = new char*[size];\r
+ unsigned i = 0;\r
+ unsigned j = 0;\r
+ while(envp[i])\r
+ {\r
+ std::string name;\r
+ std::string value;\r
+ envp_extract(name, value, envp, i);\r
+ envp_append(name, value, result, j);\r
+ }\r
+ return result;\r
+ }\r
+\r
+ static void envp_clear(char**& envp)\r
+ {\r
+ if (envp)\r
+ {\r
+ for (unsigned i = 0; envp[i]; i++)\r
+ delete[] envp[i];\r
+ delete[] envp;\r
+ envp = 0;\r
+ }\r
+ }\r
+\r
+ static bool envp_equal(const std::string& left, const std::string& right)\r
+ {\r
+ return left == right;\r
+ }\r
+\r
+ static bool envp_less(const std::string& left, const std::string& right)\r
+ {\r
+ return left < right;\r
+ }\r
+\r
+#endif\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+\r
+ env_vector::env_vector(void)\r
+ {\r
+#ifdef MSWINDOWS\r
+ char* env = (char*)GetEnvironmentStringsA();\r
+ m_env = envp_copy(env);\r
+ FreeEnvironmentStringsA(env);\r
+#else\r
+ m_env = envp_copy(::environ);\r
+#endif\r
+ }\r
+\r
+ env_vector::env_vector (const env_vector& a)\r
+ {\r
+ m_env = 0;\r
+ *this = a;\r
+ }\r
+\r
+ env_vector::~env_vector (void)\r
+ {\r
+ clear();\r
+ }\r
+\r
+ env_vector& env_vector::operator = (const env_vector& a)\r
+ {\r
+ clear();\r
+ m_env = envp_copy(a.m_env);\r
+ return *this;\r
+ }\r
+\r
+ void env_vector::clear(void)\r
+ {\r
+ envp_clear(m_env);\r
+ }\r
+\r
+ void env_vector::add(const std::string& name, const std::string& value)\r
+ {\r
+ // the trick is to add the value in alphabetic order\r
+ // this is done by copying the existing environment string to a new\r
+ // string, inserting the new value when a name greater than it is found\r
+ unsigned size = envp_size(m_env);\r
+#ifdef MSWINDOWS\r
+ unsigned new_size = size + name.size() + value.size() + 2;\r
+ char* new_v = new char[new_size];\r
+ new_v[0] = '\0';\r
+#else\r
+ unsigned new_size = size + 1;\r
+ char** new_v = new char*[new_size];\r
+ new_v[0] = 0;\r
+#endif\r
+ // now extract each name=value pair and check the ordering\r
+ bool added = false;\r
+ unsigned i = 0;\r
+ unsigned j = 0;\r
+ while(m_env[i])\r
+ {\r
+ std::string current_name;\r
+ std::string current_value;\r
+ envp_extract(current_name, current_value, m_env, i);\r
+ if (envp_equal(name,current_name))\r
+ {\r
+ // replace an existing value\r
+ envp_append(name, value, new_v, j);\r
+ }\r
+ else if (!added && envp_less(name,current_name))\r
+ {\r
+ // add the new value first, then the existing one\r
+ envp_append(name, value, new_v, j);\r
+ envp_append(current_name, current_value, new_v, j);\r
+ added = true;\r
+ }\r
+ else\r
+ {\r
+ // just add the existing value\r
+ envp_append(current_name, current_value, new_v, j);\r
+ }\r
+ }\r
+ if (!added)\r
+ envp_append(name, value, new_v, j);\r
+ envp_clear(m_env);\r
+ m_env = new_v;\r
+ }\r
+\r
+\r
+ bool env_vector::remove (const std::string& name)\r
+ {\r
+ bool result = false;\r
+ // this is done by copying the existing environment string to a new string, but excluding the specified name\r
+ unsigned size = envp_size(m_env);\r
+#ifdef MSWINDOWS\r
+ char* new_v = new char[size];\r
+ new_v[0] = '\0';\r
+#else\r
+ char** new_v = new char*[size];\r
+ new_v[0] = 0;\r
+#endif\r
+ unsigned i = 0;\r
+ unsigned j = 0;\r
+ while(m_env[i])\r
+ {\r
+ std::string current_name;\r
+ std::string current_value;\r
+ envp_extract(current_name, current_value, m_env, i);\r
+ if (envp_equal(name,current_name))\r
+ result = true;\r
+ else\r
+ envp_append(current_name, current_value, new_v, j);\r
+ }\r
+ envp_clear(m_env);\r
+ m_env = new_v;\r
+ return result;\r
+ }\r
+\r
+ bool env_vector::present (const std::string& name) const\r
+ {\r
+ unsigned i = 0;\r
+ while(m_env[i])\r
+ {\r
+ std::string current_name;\r
+ std::string current_value;\r
+ envp_extract(current_name, current_value, m_env, i);\r
+ if (envp_equal(name,current_name))\r
+ return true;\r
+ }\r
+ return false;\r
+ }\r
+\r
+ std::string env_vector::operator [] (const std::string& name) const\r
+ {\r
+ return get(name);\r
+ }\r
+\r
+ std::string env_vector::get (const std::string& name) const\r
+ {\r
+ unsigned i = 0;\r
+ while(m_env[i])\r
+ {\r
+ std::string current_name;\r
+ std::string current_value;\r
+ envp_extract(current_name, current_value, m_env, i);\r
+ if (envp_equal(name,current_name))\r
+ return current_value;\r
+ }\r
+ return std::string();\r
+ }\r
+\r
+ unsigned env_vector::size (void) const\r
+ {\r
+ unsigned i = 0;\r
+#ifdef MSWINDOWS\r
+ unsigned offset = 0;\r
+ while(m_env[offset])\r
+ {\r
+ std::string current_name;\r
+ std::string current_value;\r
+ envp_extract(current_name, current_value, m_env, offset);\r
+ i++;\r
+ }\r
+#else\r
+ while(m_env[i])\r
+ i++;\r
+#endif\r
+\r
+ return i;\r
+ }\r
+\r
+ std::pair<std::string,std::string> env_vector::operator [] (unsigned index) const throw(std::out_of_range)\r
+ {\r
+ return get(index);\r
+ }\r
+\r
+ std::pair<std::string,std::string> env_vector::get (unsigned index) const throw(std::out_of_range)\r
+ {\r
+ if (index >= size()) throw std::out_of_range("arg_vector::get");\r
+ unsigned j = 0;\r
+ for (unsigned i = 0; i < index; i++)\r
+ {\r
+ std::string current_name;\r
+ std::string current_value;\r
+ envp_extract(current_name, current_value, m_env, j);\r
+ }\r
+ std::string name;\r
+ std::string value;\r
+ envp_extract(name, value, m_env, j);\r
+ return std::make_pair(name,value);\r
+ }\r
+\r
+ ENVIRON_TYPE env_vector::envp (void) const\r
+ {\r
+ return m_env;\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // Synchronous subprocess\r
+ // Win32 implementation mostly cribbed from MSDN examples and then made (much) more readable\r
+ // Unix implementation mostly from man pages and bitter experience\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+\r
+#ifdef MSWINDOWS\r
+\r
+ subprocess::subprocess(void)\r
+ {\r
+ m_pid.hProcess = 0;\r
+ m_job = 0;\r
+ m_child_in = 0;\r
+ m_child_out = 0;\r
+ m_child_err = 0;\r
+ m_err = 0;\r
+ m_status = 0;\r
+ }\r
+\r
+#else\r
+\r
+ subprocess::subprocess(void)\r
+ {\r
+ m_pid = -1;\r
+ m_child_in = -1;\r
+ m_child_out = -1;\r
+ m_child_err = -1;\r
+ m_err = 0;\r
+ m_status = 0;\r
+ }\r
+\r
+#endif\r
+\r
+#ifdef MSWINDOWS\r
+\r
+ subprocess::~subprocess(void)\r
+ {\r
+ if (m_pid.hProcess != 0)\r
+ {\r
+ close_stdin();\r
+ close_stdout();\r
+ close_stderr();\r
+ kill();\r
+ WaitForSingleObject(m_pid.hProcess, INFINITE);\r
+ CloseHandle(m_pid.hThread);\r
+ CloseHandle(m_pid.hProcess);\r
+ CloseHandle(m_job);\r
+ }\r
+ }\r
+\r
+#else\r
+\r
+ subprocess::~subprocess(void)\r
+ {\r
+ if (m_pid != -1)\r
+ {\r
+ close_stdin();\r
+ close_stdout();\r
+ close_stderr();\r
+ kill();\r
+ for (;;)\r
+ {\r
+ int wait_status = 0;\r
+ int wait_ret_val = waitpid(m_pid, &wait_status, 0);\r
+ if (wait_ret_val != -1 || errno != EINTR) break;\r
+ }\r
+ }\r
+ }\r
+\r
+#endif\r
+\r
+ void subprocess::set_error(int e)\r
+ {\r
+ m_err = e;\r
+ }\r
+\r
+ void subprocess::add_variable(const std::string& name, const std::string& value)\r
+ {\r
+ m_env.add(name, value);\r
+ }\r
+\r
+ bool subprocess::remove_variable(const std::string& name)\r
+ {\r
+ return m_env.remove(name);\r
+ }\r
+\r
+ const env_vector& subprocess::get_variables(void) const\r
+ {\r
+ return m_env;\r
+ }\r
+\r
+#ifdef MSWINDOWS\r
+\r
+ bool subprocess::spawn(const std::string& path, const arg_vector& argv,\r
+ bool connect_stdin, bool connect_stdout, bool connect_stderr)\r
+ {\r
+ bool result = true;\r
+ // first create the pipes to be used to connect to the child stdin/out/err\r
+ // If no pipes requested, then connect to the parent stdin/out/err\r
+ // for some reason you have to create a pipe handle, then duplicate it\r
+ // This is not well explained in MSDN but seems to work\r
+ PIPE_TYPE parent_stdin = 0;\r
+ if (!connect_stdin)\r
+ parent_stdin = GetStdHandle(STD_INPUT_HANDLE);\r
+ else\r
+ {\r
+ PIPE_TYPE tmp = 0;\r
+ SECURITY_ATTRIBUTES inherit_handles = {sizeof(SECURITY_ATTRIBUTES), 0, TRUE};\r
+ CreatePipe(&parent_stdin, &tmp, &inherit_handles, 0);\r
+ DuplicateHandle(GetCurrentProcess(), tmp, GetCurrentProcess(), &m_child_in, 0, FALSE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS);\r
+ }\r
+\r
+ PIPE_TYPE parent_stdout = 0;\r
+ if (!connect_stdout)\r
+ parent_stdout = GetStdHandle(STD_OUTPUT_HANDLE);\r
+ else\r
+ {\r
+ PIPE_TYPE tmp = 0;\r
+ SECURITY_ATTRIBUTES inherit_handles = {sizeof(SECURITY_ATTRIBUTES), 0, TRUE};\r
+ CreatePipe(&tmp, &parent_stdout, &inherit_handles, 0);\r
+ DuplicateHandle(GetCurrentProcess(), tmp, GetCurrentProcess(), &m_child_out, 0, FALSE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS);\r
+ }\r
+\r
+ PIPE_TYPE parent_stderr = 0;\r
+ if (!connect_stderr)\r
+ parent_stderr = GetStdHandle(STD_ERROR_HANDLE);\r
+ else\r
+ {\r
+ PIPE_TYPE tmp = 0;\r
+ SECURITY_ATTRIBUTES inherit_handles = {sizeof(SECURITY_ATTRIBUTES), 0, TRUE};\r
+ CreatePipe(&tmp, &parent_stderr, &inherit_handles, 0);\r
+ DuplicateHandle(GetCurrentProcess(), tmp, GetCurrentProcess(), &m_child_err, 0, FALSE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS);\r
+ }\r
+\r
+ // Now create the subprocess\r
+ // The horrible trick of creating a console window and hiding it seems to be required for the pipes to work\r
+ // Note that the child will inherit a copy of the pipe handles\r
+ STARTUPINFOA startup = {sizeof(STARTUPINFO),0,0,0,0,0,0,0,0,0,0,\r
+ STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW,SW_HIDE,0,0,\r
+ parent_stdin,parent_stdout,parent_stderr};\r
+ bool created = CreateProcessA(path.c_str(),(char*)argv.image().c_str(),0,0,TRUE,CREATE_SUSPENDED,m_env.envp(),0,&startup,&m_pid) != 0;\r
+ // close the parent copy of the pipe handles so that the pipes will be closed when the child releases them\r
+ if (connect_stdin) CloseHandle(parent_stdin);\r
+ if (connect_stdout) CloseHandle(parent_stdout);\r
+ if (connect_stderr) CloseHandle(parent_stderr);\r
+ if (!created)\r
+ {\r
+ set_error(GetLastError());\r
+ close_stdin();\r
+ close_stdout();\r
+ close_stderr();\r
+ result = false;\r
+ }\r
+ else\r
+ {\r
+ m_job = CreateJobObject(NULL, NULL);\r
+ AssignProcessToJobObject(m_job, m_pid.hProcess);\r
+ ResumeThread(m_pid.hThread);\r
+\r
+ // The child process is now running so call the user's callback\r
+ // The convention is that the callback can return false, in which case this will kill the child (if its still running)\r
+ if (!callback())\r
+ {\r
+ result = false;\r
+ kill();\r
+ }\r
+ close_stdin();\r
+ close_stdout();\r
+ close_stderr();\r
+ // wait for the child to finish\r
+ // TODO - kill the child if a timeout happens\r
+ WaitForSingleObject(m_pid.hProcess, INFINITE);\r
+ DWORD exit_status = 0;\r
+ if (!GetExitCodeProcess(m_pid.hProcess, &exit_status))\r
+ {\r
+ set_error(GetLastError());\r
+ result = false;\r
+ }\r
+ else if (exit_status != 0)\r
+ result = false;\r
+ m_status = (int)exit_status;\r
+ CloseHandle(m_pid.hThread);\r
+ CloseHandle(m_pid.hProcess);\r
+ CloseHandle(m_job);\r
+ }\r
+ m_pid.hProcess = 0;\r
+ return result;\r
+ }\r
+\r
+#else\r
+\r
+ bool subprocess::spawn(const std::string& path, const arg_vector& argv,\r
+ bool connect_stdin, bool connect_stdout, bool connect_stderr)\r
+ {\r
+ bool result = true;\r
+ // first create the pipes to be used to connect to the child stdin/out/err\r
+\r
+ int stdin_pipe [2] = {-1, -1};\r
+ if (connect_stdin)\r
+ if (::pipe(stdin_pipe) != 0)\r
+ set_error(errno);\r
+\r
+ int stdout_pipe [2] = {-1, -1};\r
+ if (connect_stdout)\r
+ if (::pipe(stdout_pipe) != 0)\r
+ set_error(errno);\r
+\r
+ int stderr_pipe [2] = {-1, -1};\r
+ if (connect_stderr)\r
+ if (::pipe(stderr_pipe) != 0)\r
+ set_error(errno);\r
+\r
+ // now create the subprocess\r
+ // In Unix, this is done by forking (creating two copies of the parent), then overwriting the child copy using exec\r
+ m_pid = ::fork();\r
+ switch(m_pid)\r
+ {\r
+ case -1: // failed to fork\r
+ set_error(errno);\r
+ if (connect_stdin)\r
+ {\r
+ ::close(stdin_pipe[0]);\r
+ ::close(stdin_pipe[1]);\r
+ }\r
+ if (connect_stdout)\r
+ {\r
+ ::close(stdout_pipe[0]);\r
+ ::close(stdout_pipe[1]);\r
+ }\r
+ if (connect_stderr)\r
+ {\r
+ ::close(stderr_pipe[0]);\r
+ ::close(stderr_pipe[1]);\r
+ }\r
+ result = false;\r
+ break;\r
+ case 0: // in child;\r
+ {\r
+ // for each pipe, close the end of the duplicated pipe that is being used by the parent\r
+ // and connect the child's end of the pipe to the appropriate standard I/O device\r
+ if (connect_stdin)\r
+ {\r
+ ::close(stdin_pipe[1]);\r
+ ::dup2(stdin_pipe[0],STDIN_FILENO);\r
+ }\r
+ if (connect_stdout)\r
+ {\r
+ ::close(stdout_pipe[0]);\r
+ ::dup2(stdout_pipe[1],STDOUT_FILENO);\r
+ }\r
+ if (connect_stderr)\r
+ {\r
+ ::close(stderr_pipe[0]);\r
+ ::dup2(stderr_pipe[1],STDERR_FILENO);\r
+ }\r
+ execve(path.c_str(), argv.argv(), m_env.envp());\r
+ // will only ever get here if the exec() failed completely - *must* now exit the child process\r
+ // by using errno, the parent has some chance of diagnosing the cause of the problem\r
+ exit(errno);\r
+ }\r
+ break;\r
+ default: // in parent\r
+ {\r
+ // for each pipe, close the end of the duplicated pipe that is being used by the child\r
+ // and connect the parent's end of the pipe to the class members so that they are visible to the parent() callback\r
+ if (connect_stdin)\r
+ {\r
+ ::close(stdin_pipe[0]);\r
+ m_child_in = stdin_pipe[1];\r
+ }\r
+ if (connect_stdout)\r
+ {\r
+ ::close(stdout_pipe[1]);\r
+ m_child_out = stdout_pipe[0];\r
+ }\r
+ if (connect_stderr)\r
+ {\r
+ ::close(stderr_pipe[1]);\r
+ m_child_err = stderr_pipe[0];\r
+ }\r
+ // call the user's callback\r
+ if (!callback())\r
+ {\r
+ result = false;\r
+ kill();\r
+ }\r
+ // close the pipes and wait for the child to finish\r
+ // wait exits on a signal which may be the child signalling its exit or may be an interrupt\r
+ close_stdin();\r
+ close_stdout();\r
+ close_stderr();\r
+ int wait_status = 0;\r
+ for (;;)\r
+ {\r
+ int wait_ret_val = waitpid(m_pid, &wait_status, 0);\r
+ if (wait_ret_val != -1 || errno != EINTR) break;\r
+ }\r
+ // establish whether an error occurred\r
+ if (WIFSIGNALED(wait_status))\r
+ {\r
+ // set_error(errno);\r
+ m_status = WTERMSIG(wait_status);\r
+ result = false;\r
+ }\r
+ else if (WIFEXITED(wait_status))\r
+ {\r
+ m_status = WEXITSTATUS(wait_status);\r
+ if (m_status != 0)\r
+ result = false;\r
+ }\r
+ m_pid = -1;\r
+ }\r
+ break;\r
+ }\r
+ return result;\r
+ }\r
+\r
+#endif\r
+\r
+ bool subprocess::spawn(const std::string& command_line,\r
+ bool connect_stdin, bool connect_stdout, bool connect_stderr)\r
+ {\r
+ arg_vector arguments = command_line;\r
+ if (arguments.size() == 0) return false;\r
+ std::string path = path_lookup(arguments.argv0());\r
+ if (path.empty()) return false;\r
+ return spawn(path, arguments, connect_stdin, connect_stdout, connect_stderr);\r
+ }\r
+\r
+ bool subprocess::callback(void)\r
+ {\r
+ return true;\r
+ }\r
+\r
+#ifdef MSWINDOWS\r
+\r
+ bool subprocess::kill (void)\r
+ {\r
+ if (!m_pid.hProcess) return false;\r
+ close_stdin();\r
+ close_stdout();\r
+ close_stderr();\r
+ if (!TerminateJobObject(m_job, (UINT)-1))\r
+ {\r
+ set_error(GetLastError());\r
+ return false;\r
+ }\r
+ return true;\r
+ }\r
+\r
+#else\r
+\r
+ bool subprocess::kill (void)\r
+ {\r
+ if (m_pid == -1) return false;\r
+ close_stdin();\r
+ close_stdout();\r
+ close_stderr();\r
+ if (::kill(m_pid, SIGINT) == -1)\r
+ {\r
+ set_error(errno);\r
+ return false;\r
+ }\r
+ return true;\r
+ }\r
+\r
+#endif\r
+\r
+#ifdef MSWINDOWS\r
+\r
+ int subprocess::write_stdin (std::string& buffer)\r
+ {\r
+ if (m_child_in == 0) return -1;\r
+ // do a blocking write of the whole buffer\r
+ DWORD bytes = 0;\r
+ if (!WriteFile(m_child_in, buffer.c_str(), (DWORD)buffer.size(), &bytes, 0))\r
+ {\r
+ set_error(GetLastError());\r
+ close_stdin();\r
+ return -1;\r
+ }\r
+ // now discard that part of the buffer that was written\r
+ if (bytes > 0)\r
+ buffer.erase(0, bytes);\r
+ return bytes;\r
+ }\r
+\r
+#else\r
+\r
+ int subprocess::write_stdin (std::string& buffer)\r
+ {\r
+ if (m_child_in == -1) return -1;\r
+ // do a blocking write of the whole buffer\r
+ int bytes = write(m_child_in, buffer.c_str(), buffer.size());\r
+ if (bytes == -1)\r
+ {\r
+ set_error(errno);\r
+ close_stdin();\r
+ return -1;\r
+ }\r
+ // now discard that part of the buffer that was written\r
+ if (bytes > 0)\r
+ buffer.erase(0, bytes);\r
+ return bytes;\r
+ }\r
+\r
+#endif\r
+\r
+#ifdef MSWINDOWS\r
+\r
+ int subprocess::read_stdout (std::string& buffer)\r
+ {\r
+ if (m_child_out == 0) return -1;\r
+ DWORD bytes = 0;\r
+ DWORD buffer_size = 256;\r
+ char* tmp = new char[buffer_size];\r
+ if (!ReadFile(m_child_out, tmp, buffer_size, &bytes, 0))\r
+ {\r
+ if (GetLastError() != ERROR_BROKEN_PIPE)\r
+ set_error(GetLastError());\r
+ close_stdout();\r
+ delete[] tmp;\r
+ return -1;\r
+ }\r
+ if (bytes == 0)\r
+ {\r
+ // EOF\r
+ close_stdout();\r
+ delete[] tmp;\r
+ return -1;\r
+ }\r
+ buffer.append(tmp, bytes);\r
+ delete[] tmp;\r
+ return (int)bytes;\r
+ }\r
+\r
+#else\r
+\r
+ int subprocess::read_stdout (std::string& buffer)\r
+ {\r
+ if (m_child_out == -1) return -1;\r
+ int buffer_size = 256;\r
+ char* tmp = new char[buffer_size];\r
+ int bytes = read(m_child_out, tmp, buffer_size);\r
+ if (bytes == -1)\r
+ {\r
+ set_error(errno);\r
+ close_stdout();\r
+ delete[] tmp;\r
+ return -1;\r
+ }\r
+ if (bytes == 0)\r
+ {\r
+ // EOF\r
+ close_stdout();\r
+ delete[] tmp;\r
+ return -1;\r
+ }\r
+ buffer.append(tmp, bytes);\r
+ delete[] tmp;\r
+ return bytes;\r
+ }\r
+\r
+#endif\r
+\r
+#ifdef MSWINDOWS\r
+\r
+ int subprocess::read_stderr(std::string& buffer)\r
+ {\r
+ if (m_child_err == 0) return -1;\r
+ DWORD bytes = 0;\r
+ DWORD buffer_size = 256;\r
+ char* tmp = new char[buffer_size];\r
+ if (!ReadFile(m_child_err, tmp, buffer_size, &bytes, 0))\r
+ {\r
+ if (GetLastError() != ERROR_BROKEN_PIPE)\r
+ set_error(GetLastError());\r
+ close_stderr();\r
+ delete[] tmp;\r
+ return -1;\r
+ }\r
+ if (bytes == 0)\r
+ {\r
+ // EOF\r
+ close_stderr();\r
+ delete[] tmp;\r
+ return -1;\r
+ }\r
+ buffer.append(tmp, bytes);\r
+ delete[] tmp;\r
+ return (int)bytes;\r
+ }\r
+\r
+#else\r
+\r
+ int subprocess::read_stderr (std::string& buffer)\r
+ {\r
+ if (m_child_err == -1) return -1;\r
+ int buffer_size = 256;\r
+ char* tmp = new char[buffer_size];\r
+ int bytes = read(m_child_err, tmp, buffer_size);\r
+ if (bytes == -1)\r
+ {\r
+ set_error(errno);\r
+ close_stderr();\r
+ delete[] tmp;\r
+ return -1;\r
+ }\r
+ if (bytes == 0)\r
+ {\r
+ // EOF\r
+ close_stderr();\r
+ delete[] tmp;\r
+ return -1;\r
+ }\r
+ buffer.append(tmp, bytes);\r
+ delete[] tmp;\r
+ return bytes;\r
+ }\r
+\r
+#endif\r
+\r
+#ifdef MSWINDOWS\r
+\r
+ void subprocess::close_stdin (void)\r
+ {\r
+ if (m_child_in)\r
+ {\r
+ CloseHandle(m_child_in);\r
+ m_child_in = 0;\r
+ }\r
+ }\r
+\r
+#else\r
+\r
+ void subprocess::close_stdin (void)\r
+ {\r
+ if (m_child_in != -1)\r
+ {\r
+ ::close(m_child_in);\r
+ m_child_in = -1;\r
+ }\r
+ }\r
+\r
+#endif\r
+\r
+#ifdef MSWINDOWS\r
+\r
+ void subprocess::close_stdout (void)\r
+ {\r
+ if (m_child_out)\r
+ {\r
+ CloseHandle(m_child_out);\r
+ m_child_out = 0;\r
+ }\r
+ }\r
+\r
+#else\r
+\r
+ void subprocess::close_stdout (void)\r
+ {\r
+ if (m_child_out != -1)\r
+ {\r
+ ::close(m_child_out);\r
+ m_child_out = -1;\r
+ }\r
+ }\r
+\r
+#endif\r
+\r
+#ifdef MSWINDOWS\r
+\r
+ void subprocess::close_stderr (void)\r
+ {\r
+ if (m_child_err)\r
+ {\r
+ CloseHandle(m_child_err);\r
+ m_child_err = 0;\r
+ }\r
+ }\r
+\r
+#else\r
+\r
+ void subprocess::close_stderr (void)\r
+ {\r
+ if (m_child_err != -1)\r
+ {\r
+ ::close(m_child_err);\r
+ m_child_err = -1;\r
+ }\r
+ }\r
+\r
+#endif\r
+\r
+ bool subprocess::error(void) const\r
+ {\r
+ return m_err != 0;\r
+ }\r
+\r
+ int subprocess::error_number(void) const\r
+ {\r
+ return m_err;\r
+ }\r
+\r
+#ifdef MSWINDOWS\r
+\r
+ std::string subprocess::error_text(void) const\r
+ {\r
+ if (!error()) return std::string();\r
+ char* message;\r
+ FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,\r
+ 0,\r
+ m_err,\r
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),\r
+ (LPTSTR)&message,\r
+ 0,0);\r
+ std::string result = message;\r
+ LocalFree(message);\r
+ // the error message is for some perverse reason newline terminated - remove this\r
+ if (result[result.size()-1] == '\n')\r
+ result.erase(result.end()-1);\r
+ if (result[result.size()-1] == '\r')\r
+ result.erase(result.end()-1);\r
+ return result;\r
+ }\r
+\r
+#else\r
+\r
+ std::string subprocess::error_text(void) const\r
+ {\r
+ if (!error()) return std::string();\r
+ char* text = strerror(m_err);\r
+ if (text) return std::string(text);\r
+ return "error number " + dformat("%d",m_err);\r
+ }\r
+\r
+#endif\r
+\r
+ int subprocess::exit_status(void) const\r
+ {\r
+ return m_status;\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // backtick subprocess and operations\r
+\r
+ backtick_subprocess::backtick_subprocess(void) : subprocess()\r
+ {\r
+ }\r
+\r
+ bool backtick_subprocess::callback(void)\r
+ {\r
+ for (;;)\r
+ {\r
+ std::string buffer;\r
+ int read_size = read_stdout(buffer);\r
+ if (read_size < 0) break;\r
+ m_text += buffer;\r
+ }\r
+ return !error();\r
+ }\r
+\r
+ bool backtick_subprocess::spawn(const std::string& path, const arg_vector& argv)\r
+ {\r
+ return subprocess::spawn(path, argv, false, true, false);\r
+ }\r
+\r
+ bool backtick_subprocess::spawn(const std::string& command_line)\r
+ {\r
+ return subprocess::spawn(command_line, false, true, false);\r
+ }\r
+\r
+ std::vector<std::string> backtick_subprocess::text(void) const\r
+ {\r
+ std::vector<std::string> result;\r
+ // convert the raw text into a vector of strings, each corresponding to a line\r
+ // in the process, strip out platform-specific line-endings\r
+ result.push_back(std::string());\r
+ for (unsigned i = 0; i < m_text.size(); i++)\r
+ {\r
+ // handle any kind of line-ending - Dos, Unix or MacOS\r
+ switch(m_text[i])\r
+ {\r
+ case '\xd': // carriage-return - optionally followed by linefeed\r
+ {\r
+ // discard optional following linefeed\r
+ if ((i+1 < m_text.size()) && (m_text[i+1] == '\xa'))\r
+ i++;\r
+ // add a new line to the end of the vector\r
+ result.push_back(std::string());\r
+ break;\r
+ }\r
+ case '\xa': // linefeed\r
+ {\r
+ // add a new line to the end of the vector\r
+ result.push_back(std::string());\r
+ break;\r
+ }\r
+ default:\r
+ {\r
+ result.back() += m_text[i];\r
+ break;\r
+ }\r
+ }\r
+ }\r
+ // tidy up - if the last line ended with a newline, the vector will end with an empty string - discard this\r
+ if ((result.size()) > 0 && result.back().empty())\r
+ result.erase(result.end()-1);\r
+ return result;\r
+ }\r
+\r
+ std::vector<std::string> backtick(const std::string& path, const arg_vector& argv)\r
+ {\r
+ backtick_subprocess sub;\r
+ sub.spawn(path, argv);\r
+ return sub.text();\r
+ }\r
+\r
+ std::vector<std::string> backtick(const std::string& command_line)\r
+ {\r
+ backtick_subprocess sub;\r
+ sub.spawn(command_line);\r
+ return sub.text();\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // Asynchronous subprocess\r
+\r
+#ifdef MSWINDOWS\r
+\r
+ async_subprocess::async_subprocess(void)\r
+ {\r
+ m_pid.hProcess = 0;\r
+ m_job = 0;\r
+ m_child_in = 0;\r
+ m_child_out = 0;\r
+ m_child_err = 0;\r
+ m_err = 0;\r
+ m_status = 0;\r
+ }\r
+\r
+#else\r
+\r
+ async_subprocess::async_subprocess(void)\r
+ {\r
+ m_pid = -1;\r
+ m_child_in = -1;\r
+ m_child_out = -1;\r
+ m_child_err = -1;\r
+ m_err = 0;\r
+ m_status = 0;\r
+ }\r
+\r
+#endif\r
+\r
+#ifdef MSWINDOWS\r
+\r
+ async_subprocess::~async_subprocess(void)\r
+ {\r
+ if (m_pid.hProcess != 0)\r
+ {\r
+ close_stdin();\r
+ close_stdout();\r
+ close_stderr();\r
+ kill();\r
+ WaitForSingleObject(m_pid.hProcess, INFINITE);\r
+ CloseHandle(m_pid.hThread);\r
+ CloseHandle(m_pid.hProcess);\r
+ CloseHandle(m_job);\r
+ }\r
+ }\r
+\r
+#else\r
+\r
+ async_subprocess::~async_subprocess(void)\r
+ {\r
+ if (m_pid != -1)\r
+ {\r
+ close_stdin();\r
+ close_stdout();\r
+ close_stderr();\r
+ kill();\r
+ for (;;)\r
+ {\r
+ int wait_status = 0;\r
+ int wait_ret_val = waitpid(m_pid, &wait_status, 0);\r
+ if (wait_ret_val != -1 || errno != EINTR) break;\r
+ }\r
+ }\r
+ }\r
+\r
+#endif\r
+\r
+ void async_subprocess::set_error(int e)\r
+ {\r
+ m_err = e;\r
+ }\r
+\r
+ void async_subprocess::add_variable(const std::string& name, const std::string& value)\r
+ {\r
+ m_env.add(name, value);\r
+ }\r
+\r
+ bool async_subprocess::remove_variable(const std::string& name)\r
+ {\r
+ return m_env.remove(name);\r
+ }\r
+\r
+ const env_vector& async_subprocess::get_variables(void) const\r
+ {\r
+ return m_env;\r
+ }\r
+\r
+#ifdef MSWINDOWS\r
+\r
+ bool async_subprocess::spawn(const std::string& path, const arg_vector& argv,\r
+ bool connect_stdin, bool connect_stdout, bool connect_stderr)\r
+ {\r
+ bool result = true;\r
+ // first create the pipes to be used to connect to the child stdin/out/err\r
+ // If no pipes requested, then connect to the parent stdin/out/err\r
+ // for some reason you have to create a pipe handle, then duplicate it\r
+ // This is not well explained in MSDN but seems to work\r
+ PIPE_TYPE parent_stdin = 0;\r
+ if (!connect_stdin)\r
+ parent_stdin = GetStdHandle(STD_INPUT_HANDLE);\r
+ else\r
+ {\r
+ PIPE_TYPE tmp = 0;\r
+ SECURITY_ATTRIBUTES inherit_handles = {sizeof(SECURITY_ATTRIBUTES), 0, TRUE};\r
+ CreatePipe(&parent_stdin, &tmp, &inherit_handles, 0);\r
+ DuplicateHandle(GetCurrentProcess(), tmp, GetCurrentProcess(), &m_child_in, 0, FALSE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS);\r
+ }\r
+\r
+ PIPE_TYPE parent_stdout = 0;\r
+ if (!connect_stdout)\r
+ parent_stdout = GetStdHandle(STD_OUTPUT_HANDLE);\r
+ else\r
+ {\r
+ PIPE_TYPE tmp = 0;\r
+ SECURITY_ATTRIBUTES inherit_handles = {sizeof(SECURITY_ATTRIBUTES), 0, TRUE};\r
+ CreatePipe(&tmp, &parent_stdout, &inherit_handles, 0);\r
+ DuplicateHandle(GetCurrentProcess(), tmp, GetCurrentProcess(), &m_child_out, 0, FALSE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS);\r
+ }\r
+\r
+ PIPE_TYPE parent_stderr = 0;\r
+ if (!connect_stderr)\r
+ parent_stderr = GetStdHandle(STD_ERROR_HANDLE);\r
+ else\r
+ {\r
+ PIPE_TYPE tmp = 0;\r
+ SECURITY_ATTRIBUTES inherit_handles = {sizeof(SECURITY_ATTRIBUTES), 0, TRUE};\r
+ CreatePipe(&tmp, &parent_stderr, &inherit_handles, 0);\r
+ DuplicateHandle(GetCurrentProcess(), tmp, GetCurrentProcess(), &m_child_err, 0, FALSE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS);\r
+ }\r
+\r
+ // Now create the subprocess\r
+ // The horrible trick of creating a console window and hiding it seems to be required for the pipes to work\r
+ // Note that the child will inherit a copy of the pipe handles\r
+ STARTUPINFOA startup = {sizeof(STARTUPINFO),0,0,0,0,0,0,0,0,0,0,\r
+ STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW,SW_HIDE,0,0,\r
+ parent_stdin,parent_stdout,parent_stderr};\r
+ bool created = CreateProcessA(path.c_str(),(char*)argv.image().c_str(),0,0,TRUE,CREATE_SUSPENDED,m_env.envp(),0,&startup,&m_pid) != 0;\r
+ // close the parent copy of the pipe handles so that the pipes will be closed when the child releases them\r
+ if (connect_stdin) CloseHandle(parent_stdin);\r
+ if (connect_stdout) CloseHandle(parent_stdout);\r
+ if (connect_stderr) CloseHandle(parent_stderr);\r
+ if (!created)\r
+ {\r
+ set_error(GetLastError());\r
+ close_stdin();\r
+ close_stdout();\r
+ close_stderr();\r
+ result = false;\r
+ }\r
+ else\r
+ {\r
+ m_job = CreateJobObject(NULL, NULL);\r
+ AssignProcessToJobObject(m_job, m_pid.hProcess);\r
+ ResumeThread(m_pid.hThread);\r
+ }\r
+ return result;\r
+ }\r
+\r
+#else\r
+\r
+ bool async_subprocess::spawn(const std::string& path, const arg_vector& argv,\r
+ bool connect_stdin, bool connect_stdout, bool connect_stderr)\r
+ {\r
+ bool result = true;\r
+ // first create the pipes to be used to connect to the child stdin/out/err\r
+\r
+ int stdin_pipe [2] = {-1, -1};\r
+ if (connect_stdin)\r
+ if (::pipe(stdin_pipe) != 0)\r
+ set_error(errno);\r
+\r
+ int stdout_pipe [2] = {-1, -1};\r
+ if (connect_stdout)\r
+ if (::pipe(stdout_pipe) != 0)\r
+ set_error(errno);\r
+\r
+ int stderr_pipe [2] = {-1, -1};\r
+ if (connect_stderr)\r
+ if (::pipe(stderr_pipe) != 0)\r
+ set_error(errno);\r
+\r
+ // now create the subprocess\r
+ // In Unix, this is done by forking (creating two copies of the parent), then overwriting the child copy using exec\r
+ m_pid = ::fork();\r
+ switch(m_pid)\r
+ {\r
+ case -1: // failed to fork\r
+ set_error(errno);\r
+ if (connect_stdin)\r
+ {\r
+ ::close(stdin_pipe[0]);\r
+ ::close(stdin_pipe[1]);\r
+ }\r
+ if (connect_stdout)\r
+ {\r
+ ::close(stdout_pipe[0]);\r
+ ::close(stdout_pipe[1]);\r
+ }\r
+ if (connect_stderr)\r
+ {\r
+ ::close(stderr_pipe[0]);\r
+ ::close(stderr_pipe[1]);\r
+ }\r
+ result = false;\r
+ break;\r
+ case 0: // in child;\r
+ {\r
+ // for each pipe, close the end of the duplicated pipe that is being used by the parent\r
+ // and connect the child's end of the pipe to the appropriate standard I/O device\r
+ if (connect_stdin)\r
+ {\r
+ ::close(stdin_pipe[1]);\r
+ ::dup2(stdin_pipe[0],STDIN_FILENO);\r
+ }\r
+ if (connect_stdout)\r
+ {\r
+ ::close(stdout_pipe[0]);\r
+ ::dup2(stdout_pipe[1],STDOUT_FILENO);\r
+ }\r
+ if (connect_stderr)\r
+ {\r
+ ::close(stderr_pipe[0]);\r
+ ::dup2(stderr_pipe[1],STDERR_FILENO);\r
+ }\r
+ ::execve(path.c_str(), argv.argv(), m_env.envp());\r
+ // will only ever get here if the exec() failed completely - *must* now exit the child process\r
+ // by using errno, the parent has some chance of diagnosing the cause of the problem\r
+ ::exit(errno);\r
+ }\r
+ break;\r
+ default: // in parent\r
+ {\r
+ // for each pipe, close the end of the duplicated pipe that is being used by the child\r
+ // and connect the parent's end of the pipe to the class members so that they are visible to the parent() callback\r
+ if (connect_stdin)\r
+ {\r
+ ::close(stdin_pipe[0]);\r
+ m_child_in = stdin_pipe[1];\r
+ if (fcntl(m_child_in, F_SETFL, O_NONBLOCK) == -1)\r
+ {\r
+ set_error(errno);\r
+ result = false;\r
+ }\r
+ }\r
+ if (connect_stdout)\r
+ {\r
+ ::close(stdout_pipe[1]);\r
+ m_child_out = stdout_pipe[0];\r
+ if (fcntl(m_child_out, F_SETFL, O_NONBLOCK) == -1)\r
+ {\r
+ set_error(errno);\r
+ result = false;\r
+ }\r
+ }\r
+ if (connect_stderr)\r
+ {\r
+ ::close(stderr_pipe[1]);\r
+ m_child_err = stderr_pipe[0];\r
+ if (fcntl(m_child_err, F_SETFL, O_NONBLOCK) == -1)\r
+ {\r
+ set_error(errno);\r
+ result = false;\r
+ }\r
+ }\r
+ }\r
+ break;\r
+ }\r
+ return result;\r
+ }\r
+\r
+#endif\r
+\r
+ bool async_subprocess::spawn(const std::string& command_line,\r
+ bool connect_stdin, bool connect_stdout, bool connect_stderr)\r
+ {\r
+ arg_vector arguments = command_line;\r
+ if (arguments.size() == 0) return false;\r
+ std::string path = path_lookup(arguments.argv0());\r
+ if (path.empty()) return false;\r
+ return spawn(path, arguments, connect_stdin, connect_stdout, connect_stderr);\r
+ }\r
+\r
+ bool async_subprocess::callback(void)\r
+ {\r
+ return true;\r
+ }\r
+\r
+#ifdef MSWINDOWS\r
+\r
+ bool async_subprocess::tick(void)\r
+ {\r
+ bool result = true;\r
+ if (!callback())\r
+ kill();\r
+ DWORD exit_status = 0;\r
+ if (!GetExitCodeProcess(m_pid.hProcess, &exit_status))\r
+ {\r
+ set_error(GetLastError());\r
+ result = false;\r
+ }\r
+ else if (exit_status != STILL_ACTIVE)\r
+ {\r
+ CloseHandle(m_pid.hThread);\r
+ CloseHandle(m_pid.hProcess);\r
+ CloseHandle(m_job);\r
+ m_pid.hProcess = 0;\r
+ result = false;\r
+ }\r
+ m_status = (int)exit_status;\r
+ return result;\r
+ }\r
+\r
+#else\r
+\r
+ bool async_subprocess::tick(void)\r
+ {\r
+ bool result = true;\r
+ if (!callback())\r
+ kill();\r
+ int wait_status = 0;\r
+ int wait_ret_val = waitpid(m_pid, &wait_status, WNOHANG);\r
+ if (wait_ret_val == -1 && errno != EINTR)\r
+ {\r
+ set_error(errno);\r
+ result = false;\r
+ }\r
+ else if (wait_ret_val != 0)\r
+ {\r
+ // the only states that indicate a terminated child are WIFSIGNALLED and WIFEXITED\r
+ if (WIFSIGNALED(wait_status))\r
+ {\r
+ // set_error(errno);\r
+ m_status = WTERMSIG(wait_status);\r
+ result = false;\r
+ }\r
+ else if (WIFEXITED(wait_status))\r
+ {\r
+ // child has exited\r
+ m_status = WEXITSTATUS(wait_status);\r
+ result = false;\r
+ }\r
+ }\r
+ if (!result)\r
+ m_pid = -1;\r
+ return result;\r
+ }\r
+\r
+#endif\r
+\r
+#ifdef MSWINDOWS\r
+\r
+ bool async_subprocess::kill(void)\r
+ {\r
+ if (!m_pid.hProcess) return false;\r
+ close_stdin();\r
+ close_stdout();\r
+ close_stderr();\r
+ if (!TerminateJobObject(m_job, (UINT)-1))\r
+ {\r
+ set_error(GetLastError());\r
+ return false;\r
+ }\r
+ return true;\r
+ }\r
+\r
+#else\r
+\r
+ bool async_subprocess::kill(void)\r
+ {\r
+ if (m_pid == -1) return false;\r
+ close_stdin();\r
+ close_stdout();\r
+ close_stderr();\r
+ if (::kill(m_pid, SIGINT) == -1)\r
+ {\r
+ set_error(errno);\r
+ return false;\r
+ }\r
+ return true;\r
+ }\r
+\r
+#endif\r
+\r
+#ifdef MSWINDOWS\r
+\r
+ int async_subprocess::write_stdin (std::string& buffer)\r
+ {\r
+ if (m_child_in == 0) return -1;\r
+ // there doesn't seem to be a way of doing non-blocking writes under Windoze\r
+ DWORD bytes = 0;\r
+ if (!WriteFile(m_child_in, buffer.c_str(), (DWORD)buffer.size(), &bytes, 0))\r
+ {\r
+ set_error(GetLastError());\r
+ close_stdin();\r
+ return -1;\r
+ }\r
+ // now discard that part of the buffer that was written\r
+ if (bytes > 0)\r
+ buffer.erase(0, bytes);\r
+ return (int)bytes;\r
+ }\r
+\r
+#else\r
+\r
+ int async_subprocess::write_stdin (std::string& buffer)\r
+ {\r
+ if (m_child_in == -1) return -1;\r
+ // relies on the pipe being non-blocking\r
+ // This does block under Windoze\r
+ int bytes = write(m_child_in, buffer.c_str(), buffer.size());\r
+ if (bytes == -1 && errno == EAGAIN)\r
+ {\r
+ // not ready\r
+ return 0;\r
+ }\r
+ if (bytes == -1)\r
+ {\r
+ // error on write - close the pipe and give up\r
+ set_error(errno);\r
+ close_stdin();\r
+ return -1;\r
+ }\r
+ // successful write\r
+ // now discard that part of the buffer that was written\r
+ if (bytes > 0)\r
+ buffer.erase(0, bytes);\r
+ return bytes;\r
+ }\r
+\r
+#endif\r
+\r
+#ifdef MSWINDOWS\r
+\r
+ int async_subprocess::read_stdout (std::string& buffer)\r
+ {\r
+ if (m_child_out == 0) return -1;\r
+ // peek at the buffer to see how much data there is in the first place\r
+ DWORD buffer_size = 0;\r
+ if (!PeekNamedPipe(m_child_out, 0, 0, 0, &buffer_size, 0))\r
+ {\r
+ if (GetLastError() != ERROR_BROKEN_PIPE)\r
+ set_error(GetLastError());\r
+ close_stdout();\r
+ return -1;\r
+ }\r
+ if (buffer_size == 0) return 0;\r
+ DWORD bytes = 0;\r
+ char* tmp = new char[buffer_size];\r
+ if (!ReadFile(m_child_out, tmp, buffer_size, &bytes, 0))\r
+ {\r
+ set_error(GetLastError());\r
+ close_stdout();\r
+ delete[] tmp;\r
+ return -1;\r
+ }\r
+ if (bytes == 0)\r
+ {\r
+ // EOF\r
+ close_stdout();\r
+ delete[] tmp;\r
+ return -1;\r
+ }\r
+ buffer.append(tmp, bytes);\r
+ delete[] tmp;\r
+ return (int)bytes;\r
+ }\r
+\r
+#else\r
+\r
+ int async_subprocess::read_stdout (std::string& buffer)\r
+ {\r
+ if (m_child_out == -1) return -1;\r
+ // rely on the pipe being non-blocking\r
+ int buffer_size = 256;\r
+ char* tmp = new char[buffer_size];\r
+ int bytes = read(m_child_out, tmp, buffer_size);\r
+ if (bytes == -1 && errno == EAGAIN)\r
+ {\r
+ // not ready\r
+ delete[] tmp;\r
+ return 0;\r
+ }\r
+ if (bytes == -1)\r
+ {\r
+ // error\r
+ set_error(errno);\r
+ close_stdout();\r
+ delete[] tmp;\r
+ return -1;\r
+ }\r
+ if (bytes == 0)\r
+ {\r
+ // EOF\r
+ close_stdout();\r
+ delete[] tmp;\r
+ return -1;\r
+ }\r
+ // successful read\r
+ buffer.append(tmp, bytes);\r
+ delete[] tmp;\r
+ return bytes;\r
+ }\r
+\r
+#endif\r
+\r
+#ifdef MSWINDOWS\r
+\r
+ int async_subprocess::read_stderr (std::string& buffer)\r
+ {\r
+ if (m_child_err == 0) return -1;\r
+ // peek at the buffer to see how much data there is in the first place\r
+ DWORD buffer_size = 0;\r
+ if (!PeekNamedPipe(m_child_err, 0, 0, 0, &buffer_size, 0))\r
+ {\r
+ if (GetLastError() != ERROR_BROKEN_PIPE)\r
+ set_error(GetLastError());\r
+ close_stderr();\r
+ return -1;\r
+ }\r
+ if (buffer_size == 0) return 0;\r
+ DWORD bytes = 0;\r
+ char* tmp = new char[buffer_size];\r
+ if (!ReadFile(m_child_err, tmp, buffer_size, &bytes, 0))\r
+ {\r
+ set_error(GetLastError());\r
+ close_stderr();\r
+ delete[] tmp;\r
+ return -1;\r
+ }\r
+ if (bytes == 0)\r
+ {\r
+ // EOF\r
+ close_stderr();\r
+ delete[] tmp;\r
+ return -1;\r
+ }\r
+ buffer.append(tmp, bytes);\r
+ delete[] tmp;\r
+ return (int)bytes;\r
+ }\r
+\r
+#else\r
+\r
+ int async_subprocess::read_stderr (std::string& buffer)\r
+ {\r
+ if (m_child_err == -1) return -1;\r
+ // rely on the pipe being non-blocking\r
+ int buffer_size = 256;\r
+ char* tmp = new char[buffer_size];\r
+ int bytes = read(m_child_err, tmp, buffer_size);\r
+ if (bytes == -1 && errno == EAGAIN)\r
+ {\r
+ // not ready\r
+ delete[] tmp;\r
+ return 0;\r
+ }\r
+ if (bytes == -1)\r
+ {\r
+ // error\r
+ set_error(errno);\r
+ close_stderr();\r
+ delete[] tmp;\r
+ return -1;\r
+ }\r
+ if (bytes == 0)\r
+ {\r
+ // EOF\r
+ close_stderr();\r
+ delete[] tmp;\r
+ return -1;\r
+ }\r
+ // successful read\r
+ buffer.append(tmp, bytes);\r
+ delete[] tmp;\r
+ return bytes;\r
+ }\r
+\r
+#endif\r
+\r
+#ifdef MSWINDOWS\r
+\r
+ void async_subprocess::close_stdin (void)\r
+ {\r
+ if (m_child_in)\r
+ {\r
+ CloseHandle(m_child_in);\r
+ m_child_in = 0;\r
+ }\r
+ }\r
+\r
+#else\r
+\r
+ void async_subprocess::close_stdin (void)\r
+ {\r
+ if (m_child_in != -1)\r
+ {\r
+ ::close(m_child_in);\r
+ m_child_in = -1;\r
+ }\r
+ }\r
+\r
+#endif\r
+\r
+#ifdef MSWINDOWS\r
+\r
+ void async_subprocess::close_stdout (void)\r
+ {\r
+ if (m_child_out)\r
+ {\r
+ CloseHandle(m_child_out);\r
+ m_child_out = 0;\r
+ }\r
+ }\r
+\r
+#else\r
+\r
+ void async_subprocess::close_stdout (void)\r
+ {\r
+ if (m_child_out != -1)\r
+ {\r
+ ::close(m_child_out);\r
+ m_child_out = -1;\r
+ }\r
+ }\r
+\r
+#endif\r
+\r
+#ifdef MSWINDOWS\r
+\r
+ void async_subprocess::close_stderr (void)\r
+ {\r
+ if (m_child_err)\r
+ {\r
+ CloseHandle(m_child_err);\r
+ m_child_err = 0;\r
+ }\r
+ }\r
+\r
+#else\r
+\r
+ void async_subprocess::close_stderr (void)\r
+ {\r
+ if (m_child_err != -1)\r
+ {\r
+ ::close(m_child_err);\r
+ m_child_err = -1;\r
+ }\r
+ }\r
+\r
+#endif\r
+\r
+ bool async_subprocess::error(void) const\r
+ {\r
+ return m_err != 0;\r
+ }\r
+\r
+ int async_subprocess::error_number(void) const\r
+ {\r
+ return m_err;\r
+ }\r
+\r
+#ifdef MSWINDOWS\r
+\r
+ std::string async_subprocess::error_text(void) const\r
+ {\r
+ if (!error()) return std::string();\r
+ char* message;\r
+ FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,\r
+ 0,\r
+ m_err,\r
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),\r
+ (LPTSTR)&message,\r
+ 0,0);\r
+ std::string result = message;\r
+ LocalFree(message);\r
+ // the error message is for some perverse reason newline terminated - remove this\r
+ if (result[result.size()-1] == '\n')\r
+ result.erase(result.end()-1);\r
+ if (result[result.size()-1] == '\r')\r
+ result.erase(result.end()-1);\r
+ return result;\r
+ }\r
+\r
+#else\r
+\r
+ std::string async_subprocess::error_text(void) const\r
+ {\r
+ if (!error()) return std::string();\r
+ char* text = strerror(m_err);\r
+ if (text) return std::string(text);\r
+ return "error number " + dformat("%d",m_err);\r
+ }\r
+\r
+#endif\r
+\r
+ int async_subprocess::exit_status(void) const\r
+ {\r
+ return m_status;\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
-#ifndef STLPLUS_SUBPROCESSES
-#define STLPLUS_SUBPROCESSES
-////////////////////////////////////////////////////////////////////////////////
-
-// Author: Andy Rushton
-// Copyright: (c) Southampton University 1999-2004
-// (c) Andy Rushton 2004-2009
-// License: BSD License, see ../docs/license.html
-
-// Platform-independent wrapper around the very platform-specific handling of
-// subprocesses. Uses the C++ convention that all resources must be contained in
-// an object so that when a subprocess object goes out of scope the subprocess
-// itself gets closed down.
-
-////////////////////////////////////////////////////////////////////////////////
-#include "portability_fixes.hpp"
-#ifdef MSWINDOWS
-#include <windows.h>
-#endif
-#include <stdexcept>
-#include <vector>
-#include <string>
-#include <map> // for std::pair - why is this not defined separately?
-
-////////////////////////////////////////////////////////////////////////////////
-
-namespace stlplus
-{
-
- ////////////////////////////////////////////////////////////////////////////////
- // Argument vector class
- // allows manipulation of argv-like vectors
- // includes splitting of command lines into argvectors as per the shell
- // (removing quotes) and the reverse conversion (adding quotes where necessary)
-
- class arg_vector
- {
- private:
- char** m_argv;
-
- public:
- // create an empty vector
- arg_vector (void);
-
- // copy constructor (yes it copies)
- arg_vector (const arg_vector&);
-
- // construct from an argv
- arg_vector (char**);
-
- // construct from a command-line string
- // includes de-quoting of values
- arg_vector (const std::string&);
- arg_vector (const char*);
-
- ~arg_vector (void);
-
- // assignment operators are compatible with the constructors
- arg_vector& operator = (const arg_vector&);
- arg_vector& operator = (char**);
- arg_vector& operator = (const std::string&);
- arg_vector& operator = (const char*);
-
- // add an argument to the vector
- arg_vector& operator += (const std::string&);
- arg_vector& operator -= (const std::string&);
-
- // insert/clear an argument at a certain index
- // adding is like the other array classes - it moves the current item at index
- // up one (and all subsequent values) to make room
- void insert (unsigned index, const std::string&) throw(std::out_of_range);
- void clear (unsigned index) throw(std::out_of_range);
- void clear (void);
-
- // number of values in the vector (including argv[0], the command itself
- unsigned size (void) const;
-
- // type conversion to the argv type
- operator char** (void) const;
- // function-based version of the above for people who don't like type conversions
- char** argv (void) const;
-
- // access individual values in the vector
- char* operator [] (unsigned index) const throw(std::out_of_range);
-
- // special-case access of the command name (e.g. to do path lookup on the command)
- char* argv0 (void) const throw(std::out_of_range);
-
- // get the command-line string represented by this vector
- // includes escaping of special characters and quoting
- std::string image (void) const;
- };
-
- ////////////////////////////////////////////////////////////////////////////////
- // Environment class
- // Allows manipulation of an environment vector
- // This is typically used to create an environment to be used by a subprocess
- // It does NOT modify the environment of the current process
-
-#ifdef MSWINDOWS
-#define ENVIRON_TYPE char*
-#else
-#define ENVIRON_TYPE char**
-#endif
-
- class env_vector
- {
- private:
- ENVIRON_TYPE m_env;
-
- public:
- // create an env_vector vector from the current process
- env_vector (void);
- env_vector (const env_vector&);
- ~env_vector (void);
-
- env_vector& operator = (const env_vector&);
-
- void clear (void);
-
- // manipulate the env_vector by adding or removing variables
- // adding a name that already exists replaces its value
- void add (const std::string& name, const std::string& value);
- bool remove (const std::string& name);
-
- // get the value associated with a name
- // the first uses an indexed notation (e.g. env["PATH"] )
- // the second is a function based form (e.g. env.get("PATH"))
- std::string operator [] (const std::string& name) const;
- std::string get (const std::string& name) const;
-
- // number of name=value pairs in the env_vector
- unsigned size (void) const;
-
- // get the name=value pairs by index (in the range 0 to size()-1)
- std::pair<std::string,std::string> operator [] (unsigned index) const throw(std::out_of_range);
- std::pair<std::string,std::string> get (unsigned index) const throw(std::out_of_range);
-
- // access the env_vector as an envp type - used for passing to subprocesses
- ENVIRON_TYPE envp (void) const;
- };
-
- ////////////////////////////////////////////////////////////////////////////////
-
-#ifdef MSWINDOWS
-#define PID_TYPE PROCESS_INFORMATION
-#define PIPE_TYPE HANDLE
-#else
-#define PID_TYPE int
-#define PIPE_TYPE int
-#endif
-
- ////////////////////////////////////////////////////////////////////////////////
- // Synchronous subprocess
-
- class subprocess
- {
- private:
-
- PID_TYPE m_pid;
-#ifdef MSWINDOWS
- HANDLE m_job;
-#endif
- PIPE_TYPE m_child_in;
- PIPE_TYPE m_child_out;
- PIPE_TYPE m_child_err;
- env_vector m_env;
- int m_err;
- int m_status;
-
- public:
- subprocess(void);
- virtual ~subprocess(void);
-
- void add_variable(const std::string& name, const std::string& value);
- bool remove_variable(const std::string& name);
-
- bool spawn(const std::string& path, const arg_vector& argv,
- bool connect_stdin = false, bool connect_stdout = false, bool connect_stderr = false);
- bool spawn(const std::string& command_line,
- bool connect_stdin = false, bool connect_stdout = false, bool connect_stderr = false);
-
- virtual bool callback(void);
- bool kill(void);
-
- int write_stdin(std::string& buffer);
- int read_stdout(std::string& buffer);
- int read_stderr(std::string& buffer);
-
- void close_stdin(void);
- void close_stdout(void);
- void close_stderr(void);
-
- bool error(void) const;
- int error_number(void) const;
- std::string error_text(void) const;
-
- int exit_status(void) const;
-
- private:
- // disallow copying
- subprocess(const subprocess&);
- subprocess& operator=(const subprocess&);
- };
-
- ////////////////////////////////////////////////////////////////////////////////
- // Preconfigured subprocess which executes a command and captures its output
-
- class backtick_subprocess : public subprocess
- {
- private:
- std::string m_text;
- public:
- backtick_subprocess(void);
- virtual bool callback(void);
- bool spawn(const std::string& path, const arg_vector& argv);
- bool spawn(const std::string& command_line);
- std::vector<std::string> text(void) const;
- };
-
- std::vector<std::string> backtick(const std::string& path, const arg_vector& argv);
- std::vector<std::string> backtick(const std::string& command_line);
-
- ////////////////////////////////////////////////////////////////////////////////
- // Asynchronous subprocess
-
- class async_subprocess
- {
- private:
- PID_TYPE m_pid;
-#ifdef MSWINDOWS
- HANDLE m_job;
-#endif
- PIPE_TYPE m_child_in;
- PIPE_TYPE m_child_out;
- PIPE_TYPE m_child_err;
- env_vector m_env;
- int m_err;
- int m_status;
- void set_error(int);
-
- public:
- async_subprocess(void);
- virtual ~async_subprocess(void);
-
- void add_variable(const std::string& name, const std::string& value);
- bool remove_variable(const std::string& name);
-
- bool spawn(const std::string& path, const arg_vector& argv,
- bool connect_stdin = false, bool connect_stdout = false, bool connect_stderr = false);
- bool spawn(const std::string& command_line,
- bool connect_stdin = false, bool connect_stdout = false, bool connect_stderr = false);
-
- virtual bool callback(void);
- bool tick(void);
- bool kill(void);
-
- int write_stdin(std::string& buffer);
- int read_stdout(std::string& buffer);
- int read_stderr(std::string& buffer);
-
- void close_stdin(void);
- void close_stdout(void);
- void close_stderr(void);
-
- bool error(void) const;
- int error_number(void) const;
- std::string error_text(void) const;
-
- int exit_status(void) const;
-
- private:
- // disallow copying
- async_subprocess(const async_subprocess&);
- async_subprocess& operator=(const async_subprocess&);
- };
-
- ////////////////////////////////////////////////////////////////////////////////
-
-} // end namespace stlplus
-
-#endif
+#ifndef STLPLUS_SUBPROCESSES\r
+#define STLPLUS_SUBPROCESSES\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+// Author: Andy Rushton\r
+// Copyright: (c) Southampton University 1999-2004\r
+// (c) Andy Rushton 2004 onwards\r
+// License: BSD License, see ../docs/license.html\r
+\r
+// Platform-independent wrapper around the very platform-specific handling of\r
+// subprocesses. Uses the C++ convention that all resources must be contained in\r
+// an object so that when a subprocess object goes out of scope the subprocess\r
+// itself gets closed down.\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "portability_fixes.hpp"\r
+#ifdef MSWINDOWS\r
+#include <windows.h>\r
+#endif\r
+#include <stdexcept>\r
+#include <vector>\r
+#include <string>\r
+#include <map> // for std::pair - why is this not defined separately?\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // Argument vector class\r
+ // allows manipulation of argv-like vectors\r
+ // includes splitting of command lines into argvectors as per the shell\r
+ // (removing quotes) and the reverse conversion (adding quotes where necessary)\r
+\r
+ class arg_vector\r
+ {\r
+ private:\r
+ char** m_argv;\r
+\r
+ public:\r
+ // create an empty vector\r
+ arg_vector (void);\r
+\r
+ // copy constructor (yes it copies)\r
+ arg_vector (const arg_vector&);\r
+\r
+ // construct from an argv\r
+ arg_vector (char**);\r
+\r
+ // construct from a command-line string\r
+ // includes de-quoting of values\r
+ arg_vector (const std::string&);\r
+ arg_vector (const char*);\r
+\r
+ ~arg_vector (void);\r
+\r
+ // assignment operators are compatible with the constructors\r
+ arg_vector& operator = (const arg_vector&);\r
+ arg_vector& operator = (char**);\r
+ arg_vector& operator = (const std::string&);\r
+ arg_vector& operator = (const char*);\r
+\r
+ // add an argument to the vector\r
+ arg_vector& operator += (const std::string&);\r
+ arg_vector& operator -= (const std::string&);\r
+\r
+ // insert/clear an argument at a certain index\r
+ // adding is like the other array classes - it moves the current item at index\r
+ // up one (and all subsequent values) to make room\r
+ void insert (unsigned index, const std::string&) throw(std::out_of_range);\r
+ void clear (unsigned index) throw(std::out_of_range);\r
+ void clear (void);\r
+\r
+ // number of values in the vector (including argv[0], the command itself\r
+ unsigned size (void) const;\r
+\r
+ // type conversion to the argv type\r
+ operator char** (void) const;\r
+ // function-based version of the above for people who don't like type conversions\r
+ char** argv (void) const;\r
+\r
+ // access individual values in the vector\r
+ char* operator [] (unsigned index) const throw(std::out_of_range);\r
+\r
+ // special-case access of the command name (e.g. to do path lookup on the command)\r
+ char* argv0 (void) const throw(std::out_of_range);\r
+\r
+ // get the command-line string represented by this vector\r
+ // includes escaping of special characters and quoting\r
+ std::string image (void) const;\r
+ };\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // Environment class\r
+ // Allows manipulation of an environment vector\r
+ // This is typically used to create an environment to be used by a subprocess\r
+ // It does NOT modify the environment of the current process\r
+\r
+#ifdef MSWINDOWS\r
+#define ENVIRON_TYPE char*\r
+#else\r
+#define ENVIRON_TYPE char**\r
+#endif\r
+ class subprocess;\r
+ class async_subprocess;\r
+\r
+ class env_vector\r
+ {\r
+ private:\r
+ ENVIRON_TYPE m_env;\r
+ friend class subprocess;\r
+ friend class async_subprocess;\r
+ // access the env_vector as an envp type - used for passing to subprocesses\r
+ ENVIRON_TYPE envp (void) const;\r
+\r
+ public:\r
+ // create an env_vector vector from the current process\r
+ env_vector (void);\r
+ env_vector (const env_vector&);\r
+ ~env_vector (void);\r
+\r
+ env_vector& operator = (const env_vector&);\r
+\r
+ // manipulate the env_vector by adding or removing variables\r
+ // adding a name that already exists replaces its value\r
+ void add (const std::string& name, const std::string& value);\r
+ bool remove (const std::string& name);\r
+ void clear (void);\r
+\r
+ // get the value associated with a name\r
+ // the first uses an indexed notation (e.g. env["PATH"] )\r
+ // the second is a function based form (e.g. env.get("PATH"))\r
+ bool present(const std::string& name) const;\r
+ std::string operator [] (const std::string& name) const;\r
+ std::string get (const std::string& name) const;\r
+\r
+ // number of name=value pairs in the env_vector\r
+ unsigned size (void) const;\r
+\r
+ // get the name=value pairs by index (in the range 0 to size()-1)\r
+ std::pair<std::string,std::string> operator [] (unsigned index) const throw(std::out_of_range);\r
+ std::pair<std::string,std::string> get (unsigned index) const throw(std::out_of_range);\r
+ };\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+\r
+#ifdef MSWINDOWS\r
+#define PID_TYPE PROCESS_INFORMATION\r
+#define PIPE_TYPE HANDLE\r
+#else\r
+#define PID_TYPE int\r
+#define PIPE_TYPE int\r
+#endif\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // Synchronous subprocess\r
+\r
+ class subprocess\r
+ {\r
+ protected:\r
+\r
+ PID_TYPE m_pid;\r
+#ifdef MSWINDOWS\r
+ HANDLE m_job;\r
+#endif\r
+ PIPE_TYPE m_child_in;\r
+ PIPE_TYPE m_child_out;\r
+ PIPE_TYPE m_child_err;\r
+ env_vector m_env;\r
+ int m_err;\r
+ int m_status;\r
+ void set_error(int);\r
+\r
+ public:\r
+ subprocess(void);\r
+ virtual ~subprocess(void);\r
+\r
+ void add_variable(const std::string& name, const std::string& value);\r
+ bool remove_variable(const std::string& name);\r
+ const env_vector& get_variables(void) const;\r
+\r
+ bool spawn(const std::string& path, const arg_vector& argv,\r
+ bool connect_stdin = false, bool connect_stdout = false, bool connect_stderr = false);\r
+ bool spawn(const std::string& command_line,\r
+ bool connect_stdin = false, bool connect_stdout = false, bool connect_stderr = false);\r
+\r
+ virtual bool callback(void);\r
+ bool kill(void);\r
+\r
+ int write_stdin(std::string& buffer);\r
+ int read_stdout(std::string& buffer);\r
+ int read_stderr(std::string& buffer);\r
+\r
+ void close_stdin(void);\r
+ void close_stdout(void);\r
+ void close_stderr(void);\r
+\r
+ bool error(void) const;\r
+ int error_number(void) const;\r
+ std::string error_text(void) const;\r
+\r
+ int exit_status(void) const;\r
+\r
+ private:\r
+ // disallow copying\r
+ subprocess(const subprocess&);\r
+ subprocess& operator=(const subprocess&);\r
+ };\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // Preconfigured subprocess which executes a command and captures its output\r
+\r
+ class backtick_subprocess : public subprocess\r
+ {\r
+ protected:\r
+ std::string m_text;\r
+ public:\r
+ backtick_subprocess(void);\r
+ virtual bool callback(void);\r
+ bool spawn(const std::string& path, const arg_vector& argv);\r
+ bool spawn(const std::string& command_line);\r
+ std::vector<std::string> text(void) const;\r
+ };\r
+\r
+ std::vector<std::string> backtick(const std::string& path, const arg_vector& argv);\r
+ std::vector<std::string> backtick(const std::string& command_line);\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // Asynchronous subprocess\r
+\r
+ class async_subprocess\r
+ {\r
+ protected:\r
+ PID_TYPE m_pid;\r
+#ifdef MSWINDOWS\r
+ HANDLE m_job;\r
+#endif\r
+ PIPE_TYPE m_child_in;\r
+ PIPE_TYPE m_child_out;\r
+ PIPE_TYPE m_child_err;\r
+ env_vector m_env;\r
+ int m_err;\r
+ int m_status;\r
+ void set_error(int);\r
+\r
+ public:\r
+ async_subprocess(void);\r
+ virtual ~async_subprocess(void);\r
+\r
+ void add_variable(const std::string& name, const std::string& value);\r
+ bool remove_variable(const std::string& name);\r
+ const env_vector& get_variables(void) const;\r
+\r
+ bool spawn(const std::string& path, const arg_vector& argv,\r
+ bool connect_stdin = false, bool connect_stdout = false, bool connect_stderr = false);\r
+ bool spawn(const std::string& command_line,\r
+ bool connect_stdin = false, bool connect_stdout = false, bool connect_stderr = false);\r
+\r
+ virtual bool callback(void);\r
+ bool tick(void);\r
+ bool kill(void);\r
+\r
+ int write_stdin(std::string& buffer);\r
+ int read_stdout(std::string& buffer);\r
+ int read_stderr(std::string& buffer);\r
+\r
+ void close_stdin(void);\r
+ void close_stdout(void);\r
+ void close_stderr(void);\r
+\r
+ bool error(void) const;\r
+ int error_number(void) const;\r
+ std::string error_text(void) const;\r
+\r
+ int exit_status(void) const;\r
+\r
+ private:\r
+ // disallow copying\r
+ async_subprocess(const async_subprocess&);\r
+ async_subprocess& operator=(const async_subprocess&);\r
+ };\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
+\r
+#endif\r
-#ifndef STLPLUS_TCP
-#define STLPLUS_TCP
-////////////////////////////////////////////////////////////////////////////////
-
-// Author: Andy Rushton
-// Copyright: (c) Southampton University 1999-2004
-// (c) Andy Rushton 2004-2009
-// License: BSD License, see ../docs/license.html
-
-// A deprecated legacy header - please use tcp_socket.hpp
-
-////////////////////////////////////////////////////////////////////////////////
-
-#include "portability_fixes.hpp"
-#include "tcp_sockets.hpp"
-
-#endif
+#ifndef STLPLUS_TCP\r
+#define STLPLUS_TCP\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+// Author: Andy Rushton\r
+// Copyright: (c) Southampton University 1999-2004\r
+// (c) Andy Rushton 2004 onwards\r
+// License: BSD License, see ../docs/license.html\r
+\r
+// A deprecated legacy header - please use tcp_socket.hpp\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+#include "portability_fixes.hpp"\r
+#include "tcp_sockets.hpp"\r
+\r
+#endif\r
-////////////////////////////////////////////////////////////////////////////////
-
-// Author: Andy Rushton
-// Copyright: (c) Southampton University 1999-2004
-// (c) Andy Rushton 2004-2009
-// License: BSD License, see ../docs/license.html
-
-////////////////////////////////////////////////////////////////////////////////
-
-#include "tcp_sockets.hpp"
-
-////////////////////////////////////////////////////////////////////////////////
-
-namespace stlplus
-{
-
- //////////////////////////////////////////////////////////////////////////////
- // TCP Connection
-
-
- TCP_connection::TCP_connection(const IP_socket& socket) : IP_socket(socket)
- {
- }
-
- TCP_connection::TCP_connection(void) : IP_socket(TCP)
- {
- }
-
- unsigned short TCP_connection::port(void) const
- {
- return remote_port();
- }
-
- ////////////////////////////////////////////////////////////////////////////////
- // Server
-
- TCP_server::TCP_server(void) : IP_socket(TCP)
- {
- }
-
- TCP_server::TCP_server(unsigned short port, unsigned short queue) : IP_socket(TCP)
- {
- initialise(port,queue);
- }
-
- bool TCP_server::initialise(unsigned short port, unsigned short queue)
- {
- if (!IP_socket::bind_any(port)) return false;
- return IP_socket::listen(queue);
- }
-
- TCP_connection TCP_server::accept(void)
- {
- return TCP_connection(IP_socket::accept());
- }
-
- bool TCP_server::connection_ready(unsigned timeout)
- {
- return accept_ready(timeout);
- }
-
- TCP_connection TCP_server::connection(void)
- {
- return accept();
- }
-
- //////////////////////////////////////////////////////////////////////////////
- // Client
-
- TCP_client::TCP_client(void) : IP_socket(TCP)
- {
- }
-
- TCP_client::TCP_client(const std::string& address, unsigned short port, unsigned int timeout) : IP_socket(TCP)
- {
- initialise(address,port,timeout);
- }
-
- TCP_client::TCP_client(unsigned long address, unsigned short port, unsigned int timeout) : IP_socket(TCP)
- {
- initialise(address,port,timeout);
- }
-
- bool TCP_client::initialise(unsigned long remote_address, unsigned short remote_port, unsigned int timeout)
- {
- if (!IP_socket::connect(remote_address, remote_port))
- {
- close();
- return false;
- }
- if (timeout && !IP_socket::connected(timeout))
- {
- close();
- return false;
- }
- return true;
- }
-
- bool TCP_client::initialise(const std::string& address, unsigned short remote_port, unsigned int timeout)
- {
- // lookup the address and convert it into an IP number
- unsigned long remote_address = IP_socket::ip_lookup(address);
- if (!remote_address) return false;
- return initialise(remote_address, remote_port, timeout);
- }
-
- unsigned short TCP_client::port(void) const
- {
- return remote_port();
- }
-
- unsigned long TCP_client::address(void) const
- {
- return remote_address();
- }
-
- ////////////////////////////////////////////////////////////////////////////////
-
-} // end namespace stlplus
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+// Author: Andy Rushton\r
+// Copyright: (c) Southampton University 1999-2004\r
+// (c) Andy Rushton 2004 onwards\r
+// License: BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+#include "tcp_sockets.hpp"\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+ //////////////////////////////////////////////////////////////////////////////\r
+ // TCP Connection\r
+\r
+\r
+ TCP_connection::TCP_connection(const IP_socket& socket) : IP_socket(socket)\r
+ {\r
+ }\r
+\r
+ TCP_connection::TCP_connection(void) : IP_socket(TCP)\r
+ {\r
+ }\r
+\r
+ unsigned short TCP_connection::port(void) const\r
+ {\r
+ return remote_port();\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // Server\r
+\r
+ TCP_server::TCP_server(void) : IP_socket(TCP)\r
+ {\r
+ }\r
+\r
+ TCP_server::TCP_server(unsigned short port, unsigned short queue) : IP_socket(TCP)\r
+ {\r
+ initialise(port,queue);\r
+ }\r
+\r
+ bool TCP_server::initialise(unsigned short port, unsigned short queue)\r
+ {\r
+ if (!IP_socket::bind_any(port)) return false;\r
+ return IP_socket::listen(queue);\r
+ }\r
+\r
+ TCP_connection TCP_server::accept(void)\r
+ {\r
+ return TCP_connection(IP_socket::accept());\r
+ }\r
+\r
+ bool TCP_server::connection_ready(unsigned timeout)\r
+ {\r
+ return accept_ready(timeout);\r
+ }\r
+\r
+ TCP_connection TCP_server::connection(void)\r
+ {\r
+ return accept();\r
+ }\r
+\r
+ //////////////////////////////////////////////////////////////////////////////\r
+ // Client\r
+\r
+ TCP_client::TCP_client(void) : IP_socket(TCP)\r
+ {\r
+ }\r
+\r
+ TCP_client::TCP_client(const std::string& address, unsigned short port, unsigned int timeout) : IP_socket(TCP)\r
+ {\r
+ initialise(address,port,timeout);\r
+ }\r
+\r
+ TCP_client::TCP_client(unsigned long address, unsigned short port, unsigned int timeout) : IP_socket(TCP)\r
+ {\r
+ initialise(address,port,timeout);\r
+ }\r
+\r
+ bool TCP_client::initialise(unsigned long remote_address, unsigned short remote_port, unsigned int timeout)\r
+ {\r
+ if (!IP_socket::connect(remote_address, remote_port))\r
+ {\r
+ close();\r
+ return false;\r
+ }\r
+ if (timeout && !IP_socket::connected(timeout))\r
+ {\r
+ close();\r
+ return false;\r
+ }\r
+ return true;\r
+ }\r
+\r
+ bool TCP_client::initialise(const std::string& address, unsigned short remote_port, unsigned int timeout)\r
+ {\r
+ // lookup the address and convert it into an IP number\r
+ unsigned long remote_address = IP_socket::ip_lookup(address);\r
+ if (!remote_address) return false;\r
+ return initialise(remote_address, remote_port, timeout);\r
+ }\r
+\r
+ unsigned short TCP_client::port(void) const\r
+ {\r
+ return remote_port();\r
+ }\r
+\r
+ unsigned long TCP_client::address(void) const\r
+ {\r
+ return remote_address();\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
-#ifndef STLPLUS_TCP_SOCKET
-#define STLPLUS_TCP_SOCKET
-////////////////////////////////////////////////////////////////////////////////
-
-// Author: Andy Rushton
-// Copyright: (c) Southampton University 1999-2004
-// (c) Andy Rushton 2004-2009
-// License: BSD License, see ../docs/license.html
-
-// A platform-independent (Unix and Windows anyway) interface to TCP sockets
-
-////////////////////////////////////////////////////////////////////////////////
-
-#include "portability_fixes.hpp"
-#include "ip_sockets.hpp"
-#include <string>
-
-namespace stlplus
-{
-
- //////////////////////////////////////////////////////////////////////////////
- // Server Classes: A server creates a listening port which waits for incoming
- // connections. This is placed on the port number appropriate for the service
- // - for example, a Telnet server would typically use port 23. For a new
- // service you should of course use any port not allocated to a standard
- // service. I believe that RFC 1700 defines the standard service port numbers.
- // When an incoming connection is made, the server accepts it and in the
- // process creates a new connection on a different port. This leaves the
- // standard port listening for further connections. In effect, the server
- // farms out the handling of the connections themselves and only takes
- // responsibility for accepting the connection. This is reflected in the class
- // structure. A TCP_server performs the listening and accepting roles, but
- // creates a TCP_connection to handle the accepted connection.
- //////////////////////////////////////////////////////////////////////////////
-
- //////////////////////////////////////////////////////////////////////////////
- // connection class created by TCP_server when a connection is accepted
- // this is then used to perform any communications with the remote client
-
- class TCP_connection : protected IP_socket
- {
- private:
- // constructor to actually initialise the class - can only be constructed by TCP_server
- friend class TCP_server;
- TCP_connection(const IP_socket& socket);
-
- public:
-
- ////////////////////////////////////////////////////////////////////////////
- // constructors/destructors
-
- // create an uninitialised connection
- TCP_connection(void);
-
- ////////////////////////////////////////////////////////////////////////////
- // initialisation, connection control
- // Note: TCP connections can only be initialised by a TCP server
-
- // test whether this is an initialised socket
- // - returns whether this is initialised
- // bool initialised(void) const;
- using IP_socket::initialised;
-
- // close, i.e. disconnect the socket
- // - returns a success flag
- // bool close(void);
- using IP_socket::close;
-
- ////////////////////////////////////////////////////////////////////////////
- // sending/receiving
-
- // test whether a socket is connected and ready to send data, returns if ready or on timeout
- // - timeout: how long to wait in microseconds if not connected yet (blocking)
- // - returns status
- // bool send_ready(unsigned timeout = 0);
- using IP_socket::send_ready;
-
- // send data through the socket - if the data is long only part of it may
- // be sent. The sent part is removed from the data, so the same string can
- // be sent again and again until it is empty.
- // - data: string containing data to be sent - any data successfully sent is removed
- // - returns success flag
- // bool send (std::string& data);
- using IP_socket::send;
-
- // test whether a socket is connected and ready to receive data, returns if ready or on timeout
- // - timeout: how long to wait in microseconds if not connected yet (blocking)
- // - returns status
- // bool receive_ready(unsigned timeout = 0);
- using IP_socket::receive_ready;
-
- // receive data through the socket - if the data is long only part of it
- // may be received. The received data is appended to the string, building
- // it up in stages, so the same string can be received again and again
- // until all information has been received.
- // - data: string receiving data from socket - any data successfully received is appended
- // - returns success flag
- // bool receive (std::string& data);
- using IP_socket::receive;
-
- ////////////////////////////////////////////////////////////////////////////
- // informational
-
- // the local port number of the connection
- // - returns the port number, 0 if not bound to a port
- // unsigned short local_port(void) const;
- using IP_socket::local_port;
-
- // the remote address of the connection
- // - returns the address, 0 if not connected
- // unsigned long remote_address(void) const;
- using IP_socket::remote_address;
-
- // the remote port number of the connection
- // - returns the port number, 0 if not connected to a port
- // unsigned short remote_port(void) const;
- using IP_socket::remote_port;
-
- ////////////////////////////////////////////////////////////////////////////
- // error handling
- // errors are set internally
- // an error code of 0 is the test for no error, don't rely on the message being an empty string
- // an error code != 0 means an error, then there will be a message explaining the error
-
- // if an error is set it stays set - so you must clear it before further operations
- // void clear_error(void) const;
- using IP_socket::clear_error;
-
- // get the error code of the last error
- // int error(void) const;
- using IP_socket::error;
-
- // get the explanatory message of the last error
- // std::string message(void) const;
- using IP_socket::message;
-
- ////////////////////////////////////////////////////////////////////////////
-
- // deprecated version of remote_port
- unsigned short port(void) const;
-
- ////////////////////////////////////////////////////////////////////////////
- };
-
- //////////////////////////////////////////////////////////////////////////////
- // server class that does the listening on the designated port
- // incoming connections can be queued up to a maximum queue length specified
- // in the constructor/initialise
-
- class TCP_server : protected IP_socket
- {
- public:
-
- // create an uninitialised server
- TCP_server(void);
-
- // initialise a socket and set it up to be a listening port
- // - port: port to listen on
- // - queue: length of backlog queue to manage - may be zero
- // - returns success status
- TCP_server(unsigned short port, unsigned short queue = 0);
-
- ////////////////////////////////////////////////////////////////////////////
- // initialisation
-
- // initialise a socket and set it up to be a listening port
- // - port: port to listen on
- // - queue: length of backlog queue to manage - may be zero
- // - returns success status
- bool initialise(unsigned short port, unsigned short queue = 0);
-
- // test whether this is an initialised socket
- // - returns whether this is initialised
- // bool initialised(void) const;
- using IP_socket::initialised;
-
- // close, i.e. disconnect the socket
- // - returns a success flag
- // bool close(void);
- using IP_socket::close;
-
- //////////////////////////////////////////////////////////////////////////////
- // server operation - accepting a connection
-
- // test for a connection on the object's socket - only applicable if it has been set up as a listening port
- // - timeout: how long to wait in microseconds if not connected yet
- // - returns true if a connection is ready to be accepted
- // bool accept_ready(unsigned timeout = 0);
- using IP_socket::accept_ready;
-
- // accept a connection on the object's socket - only applicable if it has been set up as a listening port
- // - returns the connection as a new socket
- TCP_connection accept(void);
-
- ////////////////////////////////////////////////////////////////////////////
- // error handling
- // errors are set internally
- // an error code of 0 is the test for no error, don't rely on the message being an empty string
- // an error code != 0 means an error, then there will be a message explaining the error
-
- // if an error is set it stays set - so you must clear it before further operations
- // void clear_error (void) const;
- using IP_socket::clear_error;
-
- // get the error code of the last error
- // int error(void) const;
- using IP_socket::error;
-
- // get the explanatory message of the last error
- // std::string message(void) const;
- using IP_socket::message;
-
- //////////////////////////////////////////////////////////////////////////////
-
- // deprecated versions of accept_ready and accept
- bool connection_ready(unsigned timeout = 0);
- TCP_connection connection(void);
-
- //////////////////////////////////////////////////////////////////////////////
- };
-
- ////////////////////////////////////////////////////////////////////////////////
- // Client Class: a client is simpler in that there is no listening port - you
- // just create a connection and get on with it. Thus the client class does the
- // whole job - create the connection and handle communications to/from it.
- //
- // Blocking mode: To use the client in blocking mode, use non-zero timeout for
- // the initialisation method. In this mode, the connection operation must
- // complete before the call will return or an error is indicated if the
- // timeout is reached without completion. This usage was designed for
- // applications which either just to TCP and nothing else or which do TCP
- // operations in a separate thread.
- //
- // Non-blocking mode: To use the client in non-blocking mode, use a zero
- // timeout for the initialisation method. Instead, you can ask it if it has
- // connected once you've initialised it. It is not an error for it to be
- // initialised but not connected. This usage was designed so that you can poll
- // the connection periodically to implement a timeout for as long as you like for
- // the connection to occur without blocking the thread that uses the client.
- //
- // In both modes, the send_ready/receive_ready methods can be called with any
- // timeout including zero.
-
- class TCP_client : protected IP_socket
- {
- public:
-
- // create an uninitialised client
- TCP_client(void);
-
- // client connect to a server
- // - remote_address: IP name (stlplus.sourceforge.net) or dotted number (216.34.181.96)
- // - remote_port: port number of remote host
- // - timeout: if 0, don't wait; if >0 wait for that microseconds for connection to be confirmed
- TCP_client(const std::string& remote_address, unsigned short remote_port, unsigned timeout = 0);
-
- // client connect to a server
- // - remote_address: IP address as a long integer - generated by stlplus::ip_lookup
- // - remote_port: port number of remote host
- // - timeout: if 0, don't wait; if >0 wait for that microseconds for connection to be confirmed
- TCP_client(unsigned long remote_address, unsigned short remote_port, unsigned timeout = 0);
-
- ////////////////////////////////////////////////////////////////////////////
- // initialisation, connection
-
- // function for performing IP lookup (i.e. gethostbyname)
- // could be standalone but making it a member means that it can use the socket's error handler
- // i.e. if this fails, the sockets error code will be set - clear it to use the socket again
- // - remote_address: IP name (stlplus.sourceforge.net) or dotted number (216.34.181.96)
- // - returns the IP address as a long integer - zero if there's an error
- // unsigned long ip_lookup(const std::string& remote_address);
- using IP_socket::ip_lookup;
-
- // client connect to a server
- // - remote_address: IP name (stlplus.sourceforge.net) or dotted number (216.34.181.96)
- // - remote_port: port number of remote host
- // - timeout: if 0, don't wait; if >0 wait for that microseconds for connection to be confirmed
- // - returns a success flag
- bool initialise(const std::string& remote_address, unsigned short remote_port, unsigned timeout = 0);
-
- // client connect to a server
- // - remote_address: IP address as a long integer - generated by stlplus::ip_lookup
- // - remote_port: port number of remote host
- // - timeout: if 0, don't wait; if >0 wait for that microseconds for connection to be confirmed
- // - returns a success flag
- bool initialise(unsigned long remote_address, unsigned short remote_port, unsigned timeout = 0);
-
- // test whether this is an initialised socket
- // - returns whether this is initialised
- // bool initialised(void) const;
- using IP_socket::initialised;
-
- // test whether a socket is connected and ready to communicate, returns on successful connect or timeout
- // - timeout: how long to wait in microseconds if not connected yet
- // - returns success flag
- // bool connected(unsigned timeout = 0);
- using IP_socket::connected;
-
- // close, i.e. disconnect the socket
- // - returns a success flag
- // bool close(void);
- using IP_socket::close;
-
- ////////////////////////////////////////////////////////////////////////////
- // sending/receiving
-
- // test whether a socket is connected and ready to send data, returns if ready or on timeout
- // - timeout: how long to wait in microseconds if not connected yet (blocking)
- // - returns status
- // bool send_ready(unsigned timeout = 0);
- using IP_socket::send_ready;
-
- // send data through the socket - if the data is long only part of it may
- // be sent. The sent part is removed from the data, so the same string can
- // be sent again and again until it is empty.
- // - data: string containing data to be sent - any data successfully sent is removed
- // - returns success flag
- // bool send (std::string& data);
- using IP_socket::send;
-
- // test whether a socket is connected and ready to receive data, returns if ready or on timeout
- // - timeout: how long to wait in microseconds if not connected yet (blocking)
- // - returns status
- // bool receive_ready(unsigned timeout = 0);
- using IP_socket::receive_ready;
-
- // receive data through the socket - if the data is long only part of it
- // may be received. The received data is appended to the string, building
- // it up in stages, so the same string can be received again and again
- // until all information has been received.
- // - data: string receiving data from socket - any data successfully received is appended
- // - returns success flag
- // bool receive (std::string& data);
- using IP_socket::receive;
-
- ////////////////////////////////////////////////////////////////////////////
- // informational
-
- // the local port number of the connection
- // - returns the port number, 0 if not bound to a port
- // unsigned short local_port(void) const;
- using IP_socket::local_port;
-
- // the remote address of the connection
- // - returns the address, 0 if not connected
- // unsigned long remote_address(void) const;
- using IP_socket::remote_address;
-
- // the remote port number of the connection
- // - returns the port number, 0 if not connected to a port
- // unsigned short remote_port(void) const;
- using IP_socket::remote_port;
-
- ////////////////////////////////////////////////////////////////////////////
- // error handling
- // errors are set internally
- // an error code of 0 is the test for no error, don't rely on the message being an empty string
- // an error code != 0 means an error, then there will be a message explaining the error
-
- // if an error is set it stays set - so you must clear it before further operations
- // void clear_error (void) const;
- using IP_socket::clear_error;
-
- // get the error code of the last error
- // int error(void) const;
- using IP_socket::error;
-
- // get the explanatory message of the last error
- // std::string message(void) const;
- using IP_socket::message;
-
- //////////////////////////////////////////////////////////////////////////////
-
- // deprecated versions
- unsigned long address(void) const;
- unsigned short port(void) const;
-
- //////////////////////////////////////////////////////////////////////////////
- };
-
- ////////////////////////////////////////////////////////////////////////////////
-
-} // end namespace stlplus
-
-#endif
+#ifndef STLPLUS_TCP_SOCKET\r
+#define STLPLUS_TCP_SOCKET\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+// Author: Andy Rushton\r
+// Copyright: (c) Southampton University 1999-2004\r
+// (c) Andy Rushton 2004 onwards\r
+// License: BSD License, see ../docs/license.html\r
+\r
+// A platform-independent (Unix and Windows anyway) interface to TCP sockets\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+#include "portability_fixes.hpp"\r
+#include "ip_sockets.hpp"\r
+#include <string>\r
+\r
+namespace stlplus\r
+{\r
+\r
+ //////////////////////////////////////////////////////////////////////////////\r
+ // Server Classes: A server creates a listening port which waits for incoming\r
+ // connections. This is placed on the port number appropriate for the service\r
+ // - for example, a Telnet server would typically use port 23. For a new\r
+ // service you should of course use any port not allocated to a standard\r
+ // service. I believe that RFC 1700 defines the standard service port numbers.\r
+ // When an incoming connection is made, the server accepts it and in the\r
+ // process creates a new connection on a different port. This leaves the\r
+ // standard port listening for further connections. In effect, the server\r
+ // farms out the handling of the connections themselves and only takes\r
+ // responsibility for accepting the connection. This is reflected in the class\r
+ // structure. A TCP_server performs the listening and accepting roles, but\r
+ // creates a TCP_connection to handle the accepted connection.\r
+ //////////////////////////////////////////////////////////////////////////////\r
+\r
+ //////////////////////////////////////////////////////////////////////////////\r
+ // connection class created by TCP_server when a connection is accepted\r
+ // this is then used to perform any communications with the remote client\r
+\r
+ class TCP_connection : protected IP_socket\r
+ {\r
+ private:\r
+ // constructor to actually initialise the class - can only be constructed by TCP_server\r
+ friend class TCP_server;\r
+ TCP_connection(const IP_socket& socket);\r
+\r
+ public:\r
+\r
+ ////////////////////////////////////////////////////////////////////////////\r
+ // constructors/destructors\r
+\r
+ // create an uninitialised connection\r
+ TCP_connection(void);\r
+\r
+ ////////////////////////////////////////////////////////////////////////////\r
+ // initialisation, connection control\r
+ // Note: TCP connections can only be initialised by a TCP server\r
+\r
+ // test whether this is an initialised socket\r
+ // - returns whether this is initialised\r
+ // bool initialised(void) const;\r
+ using IP_socket::initialised;\r
+\r
+ // close, i.e. disconnect the socket\r
+ // - returns a success flag\r
+ // bool close(void);\r
+ using IP_socket::close;\r
+\r
+ ////////////////////////////////////////////////////////////////////////////\r
+ // sending/receiving\r
+\r
+ // test whether a socket is connected and ready to send data, returns if ready or on timeout\r
+ // - timeout: how long to wait in microseconds if not connected yet (blocking)\r
+ // - returns status\r
+ // bool send_ready(unsigned timeout = 0);\r
+ using IP_socket::send_ready;\r
+\r
+ // send data through the socket - if the data is long only part of it may\r
+ // be sent. The sent part is removed from the data, so the same string can\r
+ // be sent again and again until it is empty.\r
+ // - data: string containing data to be sent - any data successfully sent is removed\r
+ // - returns success flag\r
+ // bool send (std::string& data);\r
+ using IP_socket::send;\r
+\r
+ // test whether a socket is connected and ready to receive data, returns if ready or on timeout\r
+ // - timeout: how long to wait in microseconds if not connected yet (blocking)\r
+ // - returns status\r
+ // bool receive_ready(unsigned timeout = 0);\r
+ using IP_socket::receive_ready;\r
+\r
+ // receive data through the socket - if the data is long only part of it\r
+ // may be received. The received data is appended to the string, building\r
+ // it up in stages, so the same string can be received again and again\r
+ // until all information has been received.\r
+ // - data: string receiving data from socket - any data successfully received is appended\r
+ // - returns success flag\r
+ // bool receive (std::string& data);\r
+ using IP_socket::receive;\r
+\r
+ ////////////////////////////////////////////////////////////////////////////\r
+ // informational\r
+\r
+ // the local port number of the connection\r
+ // - returns the port number, 0 if not bound to a port\r
+ // unsigned short local_port(void) const;\r
+ using IP_socket::local_port;\r
+\r
+ // the remote address of the connection\r
+ // - returns the address, 0 if not connected\r
+ // unsigned long remote_address(void) const;\r
+ using IP_socket::remote_address;\r
+\r
+ // the remote port number of the connection\r
+ // - returns the port number, 0 if not connected to a port\r
+ // unsigned short remote_port(void) const;\r
+ using IP_socket::remote_port;\r
+\r
+ ////////////////////////////////////////////////////////////////////////////\r
+ // error handling\r
+ // errors are set internally\r
+ // an error code of 0 is the test for no error, don't rely on the message being an empty string\r
+ // an error code != 0 means an error, then there will be a message explaining the error\r
+\r
+ // if an error is set it stays set - so you must clear it before further operations\r
+ // void clear_error(void) const;\r
+ using IP_socket::clear_error;\r
+\r
+ // get the error code of the last error\r
+ // int error(void) const;\r
+ using IP_socket::error;\r
+\r
+ // get the explanatory message of the last error\r
+ // std::string message(void) const;\r
+ using IP_socket::message;\r
+\r
+ ////////////////////////////////////////////////////////////////////////////\r
+\r
+ // deprecated version of remote_port\r
+ unsigned short port(void) const;\r
+\r
+ ////////////////////////////////////////////////////////////////////////////\r
+ };\r
+\r
+ //////////////////////////////////////////////////////////////////////////////\r
+ // server class that does the listening on the designated port\r
+ // incoming connections can be queued up to a maximum queue length specified\r
+ // in the constructor/initialise\r
+\r
+ class TCP_server : protected IP_socket\r
+ {\r
+ public:\r
+\r
+ // create an uninitialised server\r
+ TCP_server(void);\r
+\r
+ // initialise a socket and set it up to be a listening port\r
+ // - port: port to listen on\r
+ // - queue: length of backlog queue to manage - may be zero\r
+ // - returns success status\r
+ TCP_server(unsigned short port, unsigned short queue = 0);\r
+\r
+ ////////////////////////////////////////////////////////////////////////////\r
+ // initialisation\r
+\r
+ // initialise a socket and set it up to be a listening port\r
+ // - port: port to listen on\r
+ // - queue: length of backlog queue to manage - may be zero\r
+ // - returns success status\r
+ bool initialise(unsigned short port, unsigned short queue = 0);\r
+\r
+ // test whether this is an initialised socket\r
+ // - returns whether this is initialised\r
+ // bool initialised(void) const;\r
+ using IP_socket::initialised;\r
+\r
+ // close, i.e. disconnect the socket\r
+ // - returns a success flag\r
+ // bool close(void);\r
+ using IP_socket::close;\r
+\r
+ //////////////////////////////////////////////////////////////////////////////\r
+ // server operation - accepting a connection\r
+\r
+ // test for a connection on the object's socket - only applicable if it has been set up as a listening port\r
+ // - timeout: how long to wait in microseconds if not connected yet\r
+ // - returns true if a connection is ready to be accepted\r
+ // bool accept_ready(unsigned timeout = 0);\r
+ using IP_socket::accept_ready;\r
+\r
+ // accept a connection on the object's socket - only applicable if it has been set up as a listening port\r
+ // - returns the connection as a new socket\r
+ TCP_connection accept(void);\r
+\r
+ ////////////////////////////////////////////////////////////////////////////\r
+ // error handling\r
+ // errors are set internally\r
+ // an error code of 0 is the test for no error, don't rely on the message being an empty string\r
+ // an error code != 0 means an error, then there will be a message explaining the error\r
+\r
+ // if an error is set it stays set - so you must clear it before further operations\r
+ // void clear_error (void) const;\r
+ using IP_socket::clear_error;\r
+\r
+ // get the error code of the last error\r
+ // int error(void) const;\r
+ using IP_socket::error;\r
+\r
+ // get the explanatory message of the last error\r
+ // std::string message(void) const;\r
+ using IP_socket::message;\r
+\r
+ //////////////////////////////////////////////////////////////////////////////\r
+\r
+ // deprecated versions of accept_ready and accept\r
+ bool connection_ready(unsigned timeout = 0);\r
+ TCP_connection connection(void);\r
+\r
+ //////////////////////////////////////////////////////////////////////////////\r
+ };\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // Client Class: a client is simpler in that there is no listening port - you\r
+ // just create a connection and get on with it. Thus the client class does the\r
+ // whole job - create the connection and handle communications to/from it.\r
+ //\r
+ // Blocking mode: To use the client in blocking mode, use non-zero timeout for\r
+ // the initialisation method. In this mode, the connection operation must\r
+ // complete before the call will return or an error is indicated if the\r
+ // timeout is reached without completion. This usage was designed for\r
+ // applications which either just to TCP and nothing else or which do TCP\r
+ // operations in a separate thread.\r
+ //\r
+ // Non-blocking mode: To use the client in non-blocking mode, use a zero\r
+ // timeout for the initialisation method. Instead, you can ask it if it has\r
+ // connected once you've initialised it. It is not an error for it to be\r
+ // initialised but not connected. This usage was designed so that you can poll\r
+ // the connection periodically to implement a timeout for as long as you like for\r
+ // the connection to occur without blocking the thread that uses the client.\r
+ //\r
+ // In both modes, the send_ready/receive_ready methods can be called with any\r
+ // timeout including zero.\r
+\r
+ class TCP_client : protected IP_socket\r
+ {\r
+ public:\r
+\r
+ // create an uninitialised client\r
+ TCP_client(void);\r
+\r
+ // client connect to a server\r
+ // - remote_address: IP name (stlplus.sourceforge.net) or dotted number (216.34.181.96)\r
+ // - remote_port: port number of remote host\r
+ // - timeout: if 0, don't wait; if >0 wait for that microseconds for connection to be confirmed\r
+ TCP_client(const std::string& remote_address, unsigned short remote_port, unsigned timeout = 0);\r
+\r
+ // client connect to a server\r
+ // - remote_address: IP address as a long integer - generated by stlplus::ip_lookup\r
+ // - remote_port: port number of remote host\r
+ // - timeout: if 0, don't wait; if >0 wait for that microseconds for connection to be confirmed\r
+ TCP_client(unsigned long remote_address, unsigned short remote_port, unsigned timeout = 0);\r
+\r
+ ////////////////////////////////////////////////////////////////////////////\r
+ // initialisation, connection\r
+\r
+ // function for performing IP lookup (i.e. gethostbyname)\r
+ // could be standalone but making it a member means that it can use the socket's error handler\r
+ // i.e. if this fails, the sockets error code will be set - clear it to use the socket again\r
+ // - remote_address: IP name (stlplus.sourceforge.net) or dotted number (216.34.181.96)\r
+ // - returns the IP address as a long integer - zero if there's an error\r
+ // unsigned long ip_lookup(const std::string& remote_address);\r
+ using IP_socket::ip_lookup;\r
+\r
+ // client connect to a server\r
+ // - remote_address: IP name (stlplus.sourceforge.net) or dotted number (216.34.181.96)\r
+ // - remote_port: port number of remote host\r
+ // - timeout: if 0, don't wait; if >0 wait for that microseconds for connection to be confirmed\r
+ // - returns a success flag\r
+ bool initialise(const std::string& remote_address, unsigned short remote_port, unsigned timeout = 0);\r
+\r
+ // client connect to a server\r
+ // - remote_address: IP address as a long integer - generated by stlplus::ip_lookup\r
+ // - remote_port: port number of remote host\r
+ // - timeout: if 0, don't wait; if >0 wait for that microseconds for connection to be confirmed\r
+ // - returns a success flag\r
+ bool initialise(unsigned long remote_address, unsigned short remote_port, unsigned timeout = 0);\r
+\r
+ // test whether this is an initialised socket\r
+ // - returns whether this is initialised\r
+ // bool initialised(void) const;\r
+ using IP_socket::initialised;\r
+\r
+ // test whether a socket is connected and ready to communicate, returns on successful connect or timeout\r
+ // - timeout: how long to wait in microseconds if not connected yet\r
+ // - returns success flag\r
+ // bool connected(unsigned timeout = 0);\r
+ using IP_socket::connected;\r
+\r
+ // close, i.e. disconnect the socket\r
+ // - returns a success flag\r
+ // bool close(void);\r
+ using IP_socket::close;\r
+\r
+ ////////////////////////////////////////////////////////////////////////////\r
+ // sending/receiving\r
+\r
+ // test whether a socket is connected and ready to send data, returns if ready or on timeout\r
+ // - timeout: how long to wait in microseconds if not connected yet (blocking)\r
+ // - returns status\r
+ // bool send_ready(unsigned timeout = 0);\r
+ using IP_socket::send_ready;\r
+\r
+ // send data through the socket - if the data is long only part of it may\r
+ // be sent. The sent part is removed from the data, so the same string can\r
+ // be sent again and again until it is empty.\r
+ // - data: string containing data to be sent - any data successfully sent is removed\r
+ // - returns success flag\r
+ // bool send (std::string& data);\r
+ using IP_socket::send;\r
+\r
+ // test whether a socket is connected and ready to receive data, returns if ready or on timeout\r
+ // - timeout: how long to wait in microseconds if not connected yet (blocking)\r
+ // - returns status\r
+ // bool receive_ready(unsigned timeout = 0);\r
+ using IP_socket::receive_ready;\r
+\r
+ // receive data through the socket - if the data is long only part of it\r
+ // may be received. The received data is appended to the string, building\r
+ // it up in stages, so the same string can be received again and again\r
+ // until all information has been received.\r
+ // - data: string receiving data from socket - any data successfully received is appended\r
+ // - returns success flag\r
+ // bool receive (std::string& data);\r
+ using IP_socket::receive;\r
+\r
+ ////////////////////////////////////////////////////////////////////////////\r
+ // informational\r
+\r
+ // the local port number of the connection\r
+ // - returns the port number, 0 if not bound to a port\r
+ // unsigned short local_port(void) const;\r
+ using IP_socket::local_port;\r
+\r
+ // the remote address of the connection\r
+ // - returns the address, 0 if not connected\r
+ // unsigned long remote_address(void) const;\r
+ using IP_socket::remote_address;\r
+\r
+ // the remote port number of the connection\r
+ // - returns the port number, 0 if not connected to a port\r
+ // unsigned short remote_port(void) const;\r
+ using IP_socket::remote_port;\r
+\r
+ ////////////////////////////////////////////////////////////////////////////\r
+ // error handling\r
+ // errors are set internally\r
+ // an error code of 0 is the test for no error, don't rely on the message being an empty string\r
+ // an error code != 0 means an error, then there will be a message explaining the error\r
+\r
+ // if an error is set it stays set - so you must clear it before further operations\r
+ // void clear_error (void) const;\r
+ using IP_socket::clear_error;\r
+\r
+ // get the error code of the last error\r
+ // int error(void) const;\r
+ using IP_socket::error;\r
+\r
+ // get the explanatory message of the last error\r
+ // std::string message(void) const;\r
+ using IP_socket::message;\r
+\r
+ //////////////////////////////////////////////////////////////////////////////\r
+\r
+ // deprecated versions\r
+ unsigned long address(void) const;\r
+ unsigned short port(void) const;\r
+\r
+ //////////////////////////////////////////////////////////////////////////////\r
+ };\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
+\r
+#endif\r
-////////////////////////////////////////////////////////////////////////////////
-
-// Author: Andy Rushton
-// Copyright: (c) Southampton University 1999-2004
-// (c) Andy Rushton 2004-2009
-// License: BSD License, see ../docs/license.html
-
-////////////////////////////////////////////////////////////////////////////////
-#include "time.hpp"
-#include "dprintf.hpp"
-#include <ctype.h>
-////////////////////////////////////////////////////////////////////////////////
-
-namespace stlplus
-{
-
- ////////////////////////////////////////////////////////////////////////////////
-
- time_t time_now(void)
- {
- return time(0);
- }
-
- time_t localtime_create(int year, int month, int day, int hour, int minute, int second)
- {
- tm tm_time;
- tm_time.tm_year = year-1900; // years are represented as an offset from 1900, for reasons unknown
- tm_time.tm_mon = month-1; // internal format represents month as 0-11, but it is friendlier to take an input 1-12
- tm_time.tm_mday = day;
- tm_time.tm_hour = hour;
- tm_time.tm_min = minute;
- tm_time.tm_sec = second;
- tm_time.tm_isdst = -1; // specify that the function should work out daylight savings
- time_t result = mktime(&tm_time);
- return result;
- }
-
- int localtime_year(time_t t)
- {
- tm* tm_time = localtime(&t);
- return tm_time->tm_year + 1900;
- }
-
- int localtime_month(time_t t)
- {
- tm* tm_time = localtime(&t);
- return tm_time->tm_mon + 1;
- }
-
- int localtime_day(time_t t)
- {
- tm* tm_time = localtime(&t);
- return tm_time->tm_mday;
- }
-
- int localtime_hour(time_t t)
- {
- tm* tm_time = localtime(&t);
- return tm_time->tm_hour;
- }
-
- int localtime_minute(time_t t)
- {
- tm* tm_time = localtime(&t);
- return tm_time->tm_min;
- }
-
- int localtime_second(time_t t)
- {
- tm* tm_time = localtime(&t);
- return tm_time->tm_sec;
- }
-
- int localtime_weekday(time_t t)
- {
- tm* tm_time = localtime(&t);
- return tm_time->tm_wday;
- }
-
- int localtime_yearday(time_t t)
- {
- tm* tm_time = localtime(&t);
- return tm_time->tm_yday;
- }
-
- std::string localtime_string(time_t t)
- {
- tm* local = localtime(&t);
- std::string result = local ? asctime(local) : "*time not available*";
- // ctime appends a newline for no apparent reason - clean up
- while (!result.empty() && isspace(result[result.size()-1]))
- result.erase(result.size()-1,1);
- return result;
- }
-
- std::string delaytime_string(time_t seconds)
- {
- unsigned minutes = (unsigned)seconds / 60;
- seconds %= 60;
- unsigned hours = minutes / 60;
- minutes %= 60;
- unsigned days = hours / 24;
- hours %= 24;
- unsigned weeks = days / 7;
- days %= 7;
- std::string result;
- if (weeks > 0)
- result += dformat("%dw ",weeks);
- if (!result.empty() || days > 0)
- result += dformat("%dd ", days);
- if (!result.empty() || hours > 0)
- result += dformat("%d:", hours);
- if (!result.empty() || minutes > 0)
- {
- if (!result.empty())
- result += dformat("%02d:", minutes);
- else
- result += dformat("%d:", minutes);
- }
- if (!result.empty())
- result += dformat("%02d:", seconds);
- else
- result += dformat("%ds", seconds);
- return result;
- }
-
- ////////////////////////////////////////////////////////////////////////////////
-
-} // end namespace stlplus
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+// Author: Andy Rushton\r
+// Copyright: (c) Southampton University 1999-2004\r
+// (c) Andy Rushton 2004 onwards\r
+// License: BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "time.hpp"\r
+#include "dprintf.hpp"\r
+#include <ctype.h>\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+\r
+ time_t time_now(void)\r
+ {\r
+ return time(0);\r
+ }\r
+\r
+ time_t localtime_create(int year, int month, int day, int hour, int minute, int second)\r
+ {\r
+ tm tm_time;\r
+ tm_time.tm_year = year-1900; // years are represented as an offset from 1900, for reasons unknown\r
+ tm_time.tm_mon = month-1; // internal format represents month as 0-11, but it is friendlier to take an input 1-12\r
+ tm_time.tm_mday = day;\r
+ tm_time.tm_hour = hour;\r
+ tm_time.tm_min = minute;\r
+ tm_time.tm_sec = second;\r
+ tm_time.tm_isdst = -1; // specify that the function should work out daylight savings\r
+ time_t result = mktime(&tm_time);\r
+ return result;\r
+ }\r
+\r
+ int localtime_year(time_t t)\r
+ {\r
+ tm* tm_time = localtime(&t);\r
+ return tm_time->tm_year + 1900;\r
+ }\r
+\r
+ int localtime_month(time_t t)\r
+ {\r
+ tm* tm_time = localtime(&t);\r
+ return tm_time->tm_mon + 1;\r
+ }\r
+\r
+ int localtime_day(time_t t)\r
+ {\r
+ tm* tm_time = localtime(&t);\r
+ return tm_time->tm_mday;\r
+ }\r
+\r
+ int localtime_hour(time_t t)\r
+ {\r
+ tm* tm_time = localtime(&t);\r
+ return tm_time->tm_hour;\r
+ }\r
+\r
+ int localtime_minute(time_t t)\r
+ {\r
+ tm* tm_time = localtime(&t);\r
+ return tm_time->tm_min;\r
+ }\r
+\r
+ int localtime_second(time_t t)\r
+ {\r
+ tm* tm_time = localtime(&t);\r
+ return tm_time->tm_sec;\r
+ }\r
+\r
+ int localtime_weekday(time_t t)\r
+ {\r
+ tm* tm_time = localtime(&t);\r
+ return tm_time->tm_wday;\r
+ }\r
+\r
+ int localtime_yearday(time_t t)\r
+ {\r
+ tm* tm_time = localtime(&t);\r
+ return tm_time->tm_yday;\r
+ }\r
+\r
+ std::string localtime_string(time_t t)\r
+ {\r
+ tm* local = localtime(&t);\r
+ std::string result = local ? asctime(local) : "*time not available*";\r
+ // ctime appends a newline for no apparent reason - clean up\r
+ while (!result.empty() && isspace(result[result.size()-1]))\r
+ result.erase(result.size()-1,1);\r
+ return result;\r
+ }\r
+\r
+ std::string delaytime_string(time_t seconds)\r
+ {\r
+ unsigned minutes = (unsigned)seconds / 60;\r
+ seconds %= 60;\r
+ unsigned hours = minutes / 60;\r
+ minutes %= 60;\r
+ unsigned days = hours / 24;\r
+ hours %= 24;\r
+ unsigned weeks = days / 7;\r
+ days %= 7;\r
+ std::string result;\r
+ if (weeks > 0)\r
+ result += dformat("%dw ",weeks);\r
+ if (!result.empty() || days > 0)\r
+ result += dformat("%dd ", days);\r
+ if (!result.empty() || hours > 0)\r
+ result += dformat("%d:", hours);\r
+ if (!result.empty() || minutes > 0)\r
+ {\r
+ if (!result.empty())\r
+ result += dformat("%02d:", minutes);\r
+ else\r
+ result += dformat("%d:", minutes);\r
+ }\r
+ if (!result.empty())\r
+ result += dformat("%02d:", seconds);\r
+ else\r
+ result += dformat("%ds", seconds);\r
+ return result;\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
-#ifndef STLPLUS_TIME
-#define STLPLUS_TIME
-////////////////////////////////////////////////////////////////////////////////
-
-// Author: Andy Rushton
-// Copyright: (c) Southampton University 1999-2004
-// (c) Andy Rushton 2004-2009
-// License: BSD License, see ../docs/license.html
-
-// Simplified access to representations of time and conversions between them.
-// The motivation for this package is that the low-level system calls for
-// accessing time are ugly and therefore potentially error-prone. I hope that
-// this interface is much simpler and therefore easier to use and more likely
-// to yield first-time right programs.
-
-// time is represented as the built-in integer type time_t - this is the
-// standard representation of system time in computerland and represents the
-// number of seconds since midnight 1 Jan 1970, believe it or not.
-
-// Functions are provided here for converting to and from more
-// human-comprehendable forms.
-
-////////////////////////////////////////////////////////////////////////////////
-#include "portability_fixes.hpp"
-#include <string>
-#include <time.h>
-
-namespace stlplus
-{
-
- // get the integer representing the time now
- time_t time_now(void);
-
- // get the integer representing the requested time - the local time is expressed in the local timezone
- time_t localtime_create(int year, int month, int day, int hour, int minute, int second);
-
- // extract human-centric form of the machine representation time_t
- int localtime_year(time_t); // the year e.g. 1962
- int localtime_month(time_t); // the month, numbered 1-12 e.g. August = 8
- int localtime_day(time_t); // the day of the month numbered 1-31 e.g. 29
- int localtime_hour(time_t); // the hour of day numbered 0-23
- int localtime_minute(time_t); // minute past the hour numbered 0-59
- int localtime_second(time_t); // second past the minute numbered 0-59
- int localtime_weekday(time_t); // the day of the week numbered 0-6 with 0=Sunday
- int localtime_yearday(time_t); // the number of days into the year
-
- // convert the integer representation of time to a human-readable form
- std::string localtime_string(time_t);
-
- // convert a time delay in seconds to human-readable form
- std::string delaytime_string(time_t);
-
-} // end namespace stlplus
-
-#endif
+#ifndef STLPLUS_TIME\r
+#define STLPLUS_TIME\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+// Author: Andy Rushton\r
+// Copyright: (c) Southampton University 1999-2004\r
+// (c) Andy Rushton 2004 onwards\r
+// License: BSD License, see ../docs/license.html\r
+\r
+// Simplified access to representations of time and conversions between them.\r
+// The motivation for this package is that the low-level system calls for\r
+// accessing time are ugly and therefore potentially error-prone. I hope that\r
+// this interface is much simpler and therefore easier to use and more likely\r
+// to yield first-time right programs.\r
+\r
+// time is represented as the built-in integer type time_t - this is the\r
+// standard representation of system time in computerland and represents the\r
+// number of seconds since midnight 1 Jan 1970, believe it or not.\r
+\r
+// Functions are provided here for converting to and from more\r
+// human-comprehendable forms.\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "portability_fixes.hpp"\r
+#include <string>\r
+#include <time.h>\r
+\r
+namespace stlplus\r
+{\r
+\r
+ // get the integer representing the time now\r
+ time_t time_now(void);\r
+\r
+ // get the integer representing the requested time - the local time is expressed in the local timezone\r
+ time_t localtime_create(int year, int month, int day, int hour, int minute, int second);\r
+\r
+ // extract human-centric form of the machine representation time_t\r
+ int localtime_year(time_t); // the year e.g. 1962\r
+ int localtime_month(time_t); // the month, numbered 1-12 e.g. August = 8\r
+ int localtime_day(time_t); // the day of the month numbered 1-31 e.g. 29\r
+ int localtime_hour(time_t); // the hour of day numbered 0-23\r
+ int localtime_minute(time_t); // minute past the hour numbered 0-59\r
+ int localtime_second(time_t); // second past the minute numbered 0-59\r
+ int localtime_weekday(time_t); // the day of the week numbered 0-6 with 0=Sunday\r
+ int localtime_yearday(time_t); // the number of days into the year\r
+\r
+ // convert the integer representation of time to a human-readable form\r
+ std::string localtime_string(time_t);\r
+\r
+ // convert a time delay in seconds to human-readable form\r
+ std::string delaytime_string(time_t);\r
+\r
+} // end namespace stlplus\r
+\r
+#endif\r
-////////////////////////////////////////////////////////////////////////////////
-
-// Author: Daniel Milton adapted by Andy Rushton
-// Copyright: (c) Daniel Milton, Andy Rushton 2009
-// License: BSD License, see ../docs/license.html
-
-////////////////////////////////////////////////////////////////////////////////
-
-#include "udp_sockets.hpp"
-
-////////////////////////////////////////////////////////////////////////////////
-
-namespace stlplus
-{
-
- ////////////////////////////////////////////////////////////////////////////////
- // UDP client
- ////////////////////////////////////////////////////////////////////////////////
-
- // create an uninitialised socket
- UDP_client::UDP_client(void) : IP_socket(UDP)
- {
- }
-
- // Send/Receive datagram packets to/from the given address/remote port on the local port.
- // Enables default send to remote address/port
- // - remote_address: IP name or number of remote host
- // - remote_port: port number of remote host
- // - local_port: port number to receive on - 0 to get an ephemeral port.
- UDP_client::UDP_client(const std::string& remote_address, unsigned short remote_port, unsigned short local_port) :
- IP_socket(UDP)
- {
- initialise(remote_address, remote_port, local_port);
- }
-
- // Send/Receive datagram packets to/from the given address/remote port on the given local port
- // Enables default send to remote address/port
- // - remote_address: IP address of remote host - pre-looked-up using ip_lookup
- // - remote_port: port number of remote host
- // - local_port: port number to receive on - 0 to get an ephemeral port.
- UDP_client::UDP_client(unsigned long remote_address, unsigned short remote_port, unsigned short local_port) :
- IP_socket(UDP)
- {
- initialise(remote_address, remote_port, local_port);
- }
-
- // Send/Receive datagram packets to/from the given address/remote port on the local port.
- // Enables default send to remote address/port
- // - remote_address: IP name or number of remote host
- // - remote_port: port number of remote host
- // - local_port: port number to receive on - 0 to get an ephemeral port.
- // - returns a success flag
- bool UDP_client::initialise(const std::string& address, unsigned short remote_port, unsigned short local_port)
- {
- // lookup the address and convert it into an IP number
- unsigned long remote_address = IP_socket::ip_lookup(address);
- if (!remote_address) return false;
- return initialise(remote_address, remote_port, local_port);
- }
-
- // Send/Receive datagram packets to/from the given address/remote port on the given local port
- // Enables default send to remote address/port
- // - remote_address: IP address of remote host - pre-looked-up using ip_lookup
- // - remote_port: port number of remote host
- // - local_port: port number to receive on - 0 to get an ephemeral port.
- // - returns a success flag
- bool UDP_client::initialise(unsigned long remote_address, unsigned short remote_port, unsigned short local_port)
- {
- if (!IP_socket::bind(remote_address, local_port)) return false;
- return IP_socket::connect(remote_address, remote_port);
- }
-
- // send to the remote address/port setup in initialise, from the local port also setup in initialise.
- // send data through the socket as a single datagram
- // - packet: string containing data to be sent - if data is successfully sent it is removed
- // - returns success flag
- bool UDP_client::send(std::string& packet)
- {
- return IP_socket::send_packet(packet);
- }
-
- // datagram receive
- // - packet: string to receive data from datagram - if data is successfully sent it is appended
- // - returns success flag - i.e. packet successfully received
- bool UDP_client::receive(std::string& packet)
- {
- return IP_socket::receive_packet(packet);
- }
-
- ////////////////////////////////////////////////////////////////////////////////
- // UDP Server
- ////////////////////////////////////////////////////////////////////////////////
-
- // create an uninitialised socket
- UDP_server::UDP_server(void) : IP_socket(UDP)
- {
- }
-
- // Initialise socket.
- // Receive datagram packets from any address on provided local receiving port.
- // No default send possible.
- // - local_port: port number to receive on - 0 to get an ephemeral port.
- UDP_server::UDP_server(unsigned short local_port) : IP_socket(UDP)
- {
- initialise(local_port);
- }
-
- // Initialise socket.
- // Receive datagram packets from any address on provided local receiving port.
- // No default send possible.
- // - local_port: port number to receive on - 0 to get an ephemeral port.
- // - returns a success flag
- bool UDP_server::initialise(unsigned short local_port)
- {
- return IP_socket::bind_any(local_port);
- }
-
- // send to the address/port given here, from the local port setup in initialise.
- // send data through the socket as a single datagram
- // - packet: string containing data to be sent - if data is successfully sent it is removed
- // - remote_address: IP name (stlplus.sourceforge.net) or dotted number (216.34.181.96)
- // - remote_port: port number of remote host
- // - returns success flag
- bool UDP_server::send(std::string& packet, const std::string& remote_address, unsigned short remote_port)
- {
- unsigned long ip_address = ip_lookup(remote_address);
- if (ip_address == 0) return false;
- return send(packet, ip_address, remote_port);
- }
-
- // send to the address/port given here, from the local port setup in initialise.
- // send data through the socket as a single datagram
- // - packet: string containing data to be sent - if data is successfully sent it is removed
- // - remote_address: pre-looked-up IP address of remote host
- // - remote_port: port number of remote host
- // - returns success flag
- bool UDP_server::send(std::string& packet, unsigned long remote_address, unsigned short remote_port)
- {
- return IP_socket::send_packet(packet, remote_address, remote_port);
- }
-
- // datagram receive
- // - packet: string to receive data from datagram - if data is successfully sent it is appended
- // - remote_address: the address of the client that sent the packet, can then be used to reply
- // - remote_port: the port of the client that sent the packet, can then be used to reply
- // - returns success flag - i.e. packet successfully received
- bool UDP_server::receive(std::string& packet, unsigned long& remote_address, unsigned short& remote_port)
- {
- return IP_socket::receive_packet(packet, remote_address, remote_port);
- }
-
- /////////////////////////////////////////////////////////////////////////////
- // fire and forget UDP client packet send function
- ////////////////////////////////////////////////////////////////////////////////
-
- bool UDP_send(const std::string& packet,
- const std::string& remote_address, unsigned short remote_port, unsigned short local_port)
- {
- UDP_client client(remote_address, remote_port, local_port);
- if (!client.initialised()) return false;
- std::string packet_copy = packet;
- return client.send(packet_copy);
- }
-
- /////////////////////////////////////////////////////////////////////////////
-
-} // end namespace stlplus
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+// Author: Daniel Milton adapted by Andy Rushton\r
+// Copyright: (c) Daniel Milton, Andy Rushton onwards\r
+// License: BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+#include "udp_sockets.hpp"\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // UDP client\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+\r
+ // create an uninitialised socket\r
+ UDP_client::UDP_client(void) : IP_socket(UDP)\r
+ {\r
+ }\r
+\r
+ // Send/Receive datagram packets to/from the given address/remote port on the local port.\r
+ // Enables default send to remote address/port\r
+ // - remote_address: IP name or number of remote host\r
+ // - remote_port: port number of remote host\r
+ // - local_port: port number to receive on - 0 to get an ephemeral port.\r
+ UDP_client::UDP_client(const std::string& remote_address, unsigned short remote_port, unsigned short local_port) :\r
+ IP_socket(UDP)\r
+ {\r
+ initialise(remote_address, remote_port, local_port);\r
+ }\r
+\r
+ // Send/Receive datagram packets to/from the given address/remote port on the given local port\r
+ // Enables default send to remote address/port\r
+ // - remote_address: IP address of remote host - pre-looked-up using ip_lookup\r
+ // - remote_port: port number of remote host\r
+ // - local_port: port number to receive on - 0 to get an ephemeral port.\r
+ UDP_client::UDP_client(unsigned long remote_address, unsigned short remote_port, unsigned short local_port) :\r
+ IP_socket(UDP)\r
+ {\r
+ initialise(remote_address, remote_port, local_port);\r
+ }\r
+\r
+ // Send/Receive datagram packets to/from the given address/remote port on the local port.\r
+ // Enables default send to remote address/port\r
+ // - remote_address: IP name or number of remote host\r
+ // - remote_port: port number of remote host\r
+ // - local_port: port number to receive on - 0 to get an ephemeral port.\r
+ // - returns a success flag\r
+ bool UDP_client::initialise(const std::string& address, unsigned short remote_port, unsigned short local_port)\r
+ {\r
+ // lookup the address and convert it into an IP number\r
+ unsigned long remote_address = IP_socket::ip_lookup(address);\r
+ if (!remote_address) return false;\r
+ return initialise(remote_address, remote_port, local_port);\r
+ }\r
+\r
+ // Send/Receive datagram packets to/from the given address/remote port on the given local port\r
+ // Enables default send to remote address/port\r
+ // - remote_address: IP address of remote host - pre-looked-up using ip_lookup\r
+ // - remote_port: port number of remote host\r
+ // - local_port: port number to receive on - 0 to get an ephemeral port.\r
+ // - returns a success flag\r
+ bool UDP_client::initialise(unsigned long remote_address, unsigned short remote_port, unsigned short local_port)\r
+ {\r
+ if (!IP_socket::bind(remote_address, local_port)) return false;\r
+ return IP_socket::connect(remote_address, remote_port);\r
+ }\r
+\r
+ // send to the remote address/port setup in initialise, from the local port also setup in initialise.\r
+ // send data through the socket as a single datagram\r
+ // - packet: string containing data to be sent - if data is successfully sent it is removed\r
+ // - returns success flag\r
+ bool UDP_client::send(std::string& packet)\r
+ {\r
+ return IP_socket::send_packet(packet);\r
+ }\r
+\r
+ // datagram receive\r
+ // - packet: string to receive data from datagram - if data is successfully sent it is appended\r
+ // - returns success flag - i.e. packet successfully received\r
+ bool UDP_client::receive(std::string& packet)\r
+ {\r
+ return IP_socket::receive_packet(packet);\r
+ }\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // UDP Server\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+\r
+ // create an uninitialised socket\r
+ UDP_server::UDP_server(void) : IP_socket(UDP)\r
+ {\r
+ }\r
+\r
+ // Initialise socket.\r
+ // Receive datagram packets from any address on provided local receiving port.\r
+ // No default send possible.\r
+ // - local_port: port number to receive on - 0 to get an ephemeral port.\r
+ UDP_server::UDP_server(unsigned short local_port) : IP_socket(UDP)\r
+ {\r
+ initialise(local_port);\r
+ }\r
+\r
+ // Initialise socket.\r
+ // Receive datagram packets from any address on provided local receiving port.\r
+ // No default send possible.\r
+ // - local_port: port number to receive on - 0 to get an ephemeral port.\r
+ // - returns a success flag\r
+ bool UDP_server::initialise(unsigned short local_port)\r
+ {\r
+ return IP_socket::bind_any(local_port);\r
+ }\r
+\r
+ // send to the address/port given here, from the local port setup in initialise.\r
+ // send data through the socket as a single datagram\r
+ // - packet: string containing data to be sent - if data is successfully sent it is removed\r
+ // - remote_address: IP name (stlplus.sourceforge.net) or dotted number (216.34.181.96)\r
+ // - remote_port: port number of remote host\r
+ // - returns success flag\r
+ bool UDP_server::send(std::string& packet, const std::string& remote_address, unsigned short remote_port)\r
+ {\r
+ unsigned long ip_address = ip_lookup(remote_address);\r
+ if (ip_address == 0) return false;\r
+ return send(packet, ip_address, remote_port);\r
+ }\r
+\r
+ // send to the address/port given here, from the local port setup in initialise.\r
+ // send data through the socket as a single datagram\r
+ // - packet: string containing data to be sent - if data is successfully sent it is removed\r
+ // - remote_address: pre-looked-up IP address of remote host\r
+ // - remote_port: port number of remote host\r
+ // - returns success flag\r
+ bool UDP_server::send(std::string& packet, unsigned long remote_address, unsigned short remote_port)\r
+ {\r
+ return IP_socket::send_packet(packet, remote_address, remote_port);\r
+ }\r
+\r
+ // datagram receive\r
+ // - packet: string to receive data from datagram - if data is successfully sent it is appended\r
+ // - remote_address: the address of the client that sent the packet, can then be used to reply\r
+ // - remote_port: the port of the client that sent the packet, can then be used to reply\r
+ // - returns success flag - i.e. packet successfully received\r
+ bool UDP_server::receive(std::string& packet, unsigned long& remote_address, unsigned short& remote_port)\r
+ {\r
+ return IP_socket::receive_packet(packet, remote_address, remote_port);\r
+ }\r
+\r
+ /////////////////////////////////////////////////////////////////////////////\r
+ // fire and forget UDP client packet send function\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+\r
+ bool UDP_send(const std::string& packet,\r
+ const std::string& remote_address, unsigned short remote_port, unsigned short local_port)\r
+ {\r
+ UDP_client client(remote_address, remote_port, local_port);\r
+ if (!client.initialised()) return false;\r
+ std::string packet_copy = packet;\r
+ return client.send(packet_copy);\r
+ }\r
+\r
+ /////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
-#ifndef STLPLUS_UDP_SOCKET
-#define STLPLUS_UDP_SOCKET
-////////////////////////////////////////////////////////////////////////////////
-
-// Author: Andy Rushton
-// Copyright: (c) Southampton University 1999-2004
-// (c) Andy Rushton 2004-2009
-// License: BSD License, see ../docs/license.html
-
-// A platform-independent (Unix and Windows anyway) interface to UDP sockets
-
-////////////////////////////////////////////////////////////////////////////////
-
-#include "portability_fixes.hpp"
-#include "ip_sockets.hpp"
-#include <string>
-
-namespace stlplus
-{
-
- //////////////////////////////////////////////////////////////////////////////
- // UDP client - creates a connectioned socket
-
- class UDP_client : protected IP_socket
- {
- public:
-
- // create an uninitialised socket
- UDP_client(void);
-
- // Send/Receive datagram packets to/from the given address/remote port on the local port.
- // - remote_address: IP name or number of remote host
- // - remote_port: port number of remote host
- // - local_port: port number to receive on - 0 to get an ephemeral port.
- UDP_client(const std::string& remote_address, unsigned short remote_port, unsigned short local_port=0);
-
- // Send/Receive datagram packets to/from the given address/remote port on the given local port
- // Enables default send to remote address/port
- // - remote_address: IP address of remote host - pre-looked-up using ip_lookup
- // - remote_port: port number of remote host
- // - local_port: port number to receive on - 0 to get an ephemeral port.
- UDP_client(unsigned long remote_address, unsigned short remote_port, unsigned short local_port=0);
-
- ////////////////////////////////////////////////////////////////////////////
- // initialisation, connection
-
- // function for performing IP lookup (i.e. gethostbyname)
- // could be standalone but making it a member means that it can use the socket's error handler
- // i.e. if this fails, the sockets error code will be set - clear it to use the socket again
- // - remote_address: IP name (stlplus.sourceforge.net) or dotted number (216.34.181.96)
- // - returns the IP address as a long integer - zero if there's an error
- // unsigned long ip_lookup(const std::string& remote_address);
- using IP_socket::ip_lookup;
-
- // Send/Receive datagram packets to/from the given address/remote port on the local port.
- // Enables default send to remote address/port
- // - remote_address: IP name or number of remote host
- // - remote_port: port number of remote host
- // - local_port: port number to receive on - 0 to get an ephemeral port.
- // - returns a success flag
- bool initialise(const std::string& remote_address, unsigned short remote_port, unsigned short local_port=0);
-
- // Send/Receive datagram packets to/from the given address/remote port on the given local port
- // Enables default send to remote address/port
- // - remote_address: IP address of remote host - pre-looked-up using ip_lookup
- // - remote_port: port number of remote host
- // - local_port: port number to receive on - 0 to get an ephemeral port.
- // - returns a success flag
- bool initialise(unsigned long remote_address, unsigned short remote_port, unsigned short local_port=0);
-
- // test whether this is an initialised socket
- // - returns whether this is initialised
- // bool initialised(void) const;
- using IP_socket::initialised;
-
- // close, i.e. disconnect the socket
- // - returns a success flag
- // bool close(void);
- using IP_socket::close;
-
- ////////////////////////////////////////////////////////////////////////////
- // sending/receiving
-
- // test whether a socket is connected and ready to send data, returns if ready or on timeout
- // - timeout: how long to wait in microseconds if not connected yet (blocking)
- // - returns status
- // bool send_ready(unsigned timeout = 0);
- using IP_socket::send_ready;
-
- // send to the remote address/port setup in initialise, from the local port also setup in initialise.
- // send data through the socket as a single datagram
- // - packet: string containing data to be sent - if data is successfully sent it is removed
- // - returns success flag
- bool send(std::string& packet);
-
- // test whether a socket is connected and ready to receive data, returns if ready or on timeout
- // - timeout: how long to wait in microseconds if not connected yet (blocking)
- // - returns status
- // bool receive_ready(unsigned timeout = 0);
- using IP_socket::receive_ready;
-
- // datagram receive
- // - packet: string to receive data from datagram - if data is successfully sent it is appended
- // - returns success flag - i.e. packet successfully received
- bool receive(std::string& packet);
-
- ////////////////////////////////////////////////////////////////////////////
- // informational
-
- // the local port number of the connection
- // returns the port number, 0 if not bound to a port
- // unsigned short local_port(void) const;
- using IP_socket::local_port;
-
- // the remote address of the connection
- // returns the address, 0 if ANY address
- // unsigned long remote_address(void) const;
- using IP_socket::remote_address;
-
- // the remote port number of the connection
- // returns the port number, 0 if not bound to a port
- // unsigned short remote_port(void) const;
- using IP_socket::remote_port;
-
- ////////////////////////////////////////////////////////////////////////////
- // error handling
- // errors are set internally
- // an error code of 0 is the test for no error, don't rely on the message being an empty string
- // an error code != 0 means an error, then there will be a message explaining the error
-
- // if an error is set it stays set - so you must clear it before further operations
- // void clear_error (void);
- using IP_socket::clear_error ;
-
- // get the error code of the last error
- // int error(void) const;
- using IP_socket::error;
-
- // get the explanatory message of the last error
- // std::string message(void) const;
- using IP_socket::message;
-
- ////////////////////////////////////////////////////////////////////////////
-
- private:
- IP_socket m_socket;
- };
-
- ////////////////////////////////////////////////////////////////////////////////
- // UDP server - creates a connectionless (multicast) listener socket
-
- class UDP_server : protected IP_socket
- {
- public:
-
- // create an uninitialised socket
- UDP_server(void);
-
- // Initialise socket.
- // Receive datagram packets from any address on provided local receiving port.
- // - local_port: port number to receive on - 0 to get an ephemeral port.
- UDP_server(unsigned short local_port);
-
- ////////////////////////////////////////////////////////////////////////////
- // initialisation, connection
-
- // function for performing IP lookup (i.e. gethostbyname)
- // could be standalone but making it a member means that it can use the socket's error handler
- // i.e. if this fails, the sockets error code will be set - clear it to use the socket again
- // - remote_address: IP name (stlplus.sourceforge.net) or dotted number (216.34.181.96)
- // - returns the IP address as a long integer - zero if there's an error
- // unsigned long ip_lookup(const std::string& remote_address);
- using IP_socket::ip_lookup;
-
- // Initialise socket.
- // Receive datagram packets from any address on provided local receiving port.
- // - local_port: port number to receive on - 0 to get an ephemeral port.
- // - returns a success flag
- bool initialise(unsigned short local_port);
-
- // test whether this is an initialised socket
- // - returns whether this is initialised
- // bool initialised(void) const;
- using IP_socket::initialised;
-
- // close, i.e. disconnect the socket
- // - returns a success flag
- // bool close(void);
- using IP_socket::close;
-
- ////////////////////////////////////////////////////////////////////////////
- // sending/receiving
-
- // test whether a socket is connected and ready to send data, returns if ready or on timeout
- // - timeout: how long to wait in microseconds if not connected yet (blocking)
- // - returns status
- // bool send_ready(unsigned timeout = 0);
- using IP_socket::send_ready;
-
- // send to the address/port given here, from the local port setup in initialise.
- // send data through the socket as a single datagram
- // - packet: string containing data to be sent - if data is successfully sent it is removed
- // - remote_address: IP name (stlplus.sourceforge.net) or dotted number (216.34.181.96)
- // - remote_port: port number of remote host
- // - returns success flag
- bool send(std::string& packet, const std::string& remote_address, unsigned short remote_port);
-
- // send to the address/port given here, from the local port setup in initialise.
- // send data through the socket as a single datagram
- // - packet: string containing data to be sent - if data is successfully sent it is removed
- // - remote_address: pre-looked-up IP address of remote host
- // - remote_port: port number of remote host
- // - returns success flag
- bool send(std::string& packet, unsigned long remote_address, unsigned short remote_port);
-
- // test whether a socket is connected and ready to receive data, returns if ready or on timeout
- // - timeout: how long to wait in microseconds if not connected yet (blocking)
- // - returns status
- // bool receive_ready(unsigned timeout = 0);
- using IP_socket::receive_ready;
-
- // datagram receive
- // - packet: string to receive data from datagram - if data is successfully sent it is appended
- // - remote_address: the address of the client that sent the packet, can then be used to reply
- // - remote_port: the port of the client that sent the packet, can then be used to reply
- // - returns success flag - i.e. packet successfully received
- bool receive(std::string& packet, unsigned long& remote_address, unsigned short& remote_port);
-
- ////////////////////////////////////////////////////////////////////////////
- // informational
-
- // the local port number of the connection
- // returns the port number, 0 if not bound to a port
- // unsigned short local_port(void) const;
- using IP_socket::local_port;
-
- ////////////////////////////////////////////////////////////////////////////
- // error handling
- // errors are set internally
- // an error code of 0 is the test for no error, don't rely on the message being an empty string
- // an error code != 0 means an error, then there will be a message explaining the error
-
- // if an error is set it stays set - so you must clear it before further operations
- // void clear_error(void);
- using IP_socket::clear_error;
-
- // get the error code of the last error
- // int error(void) const;
- using IP_socket::error;
-
- // get the explanatory message of the last error
- // std::string message(void) const;
- using IP_socket::message;
-
- ////////////////////////////////////////////////////////////////////////////
- };
-
- /////////////////////////////////////////////////////////////////////////////
- // fire and forget UDP client packet send function
-
- bool UDP_send(const std::string& packet,
- const std::string& remote_address, unsigned short remote_port, unsigned short local_port = 0);
-
- ////////////////////////////////////////////////////////////////////////////////
-
-} // end namespace stlplus
-
-#endif
+#ifndef STLPLUS_UDP_SOCKET\r
+#define STLPLUS_UDP_SOCKET\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+// Author: Andy Rushton\r
+// Copyright: (c) Southampton University 1999-2004\r
+// (c) Andy Rushton 2004 onwards\r
+// License: BSD License, see ../docs/license.html\r
+\r
+// A platform-independent (Unix and Windows anyway) interface to UDP sockets\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+#include "portability_fixes.hpp"\r
+#include "ip_sockets.hpp"\r
+#include <string>\r
+\r
+namespace stlplus\r
+{\r
+\r
+ //////////////////////////////////////////////////////////////////////////////\r
+ // UDP client - creates a connectioned socket\r
+\r
+ class UDP_client : protected IP_socket\r
+ {\r
+ public:\r
+\r
+ // create an uninitialised socket\r
+ UDP_client(void);\r
+\r
+ // Send/Receive datagram packets to/from the given address/remote port on the local port.\r
+ // - remote_address: IP name or number of remote host\r
+ // - remote_port: port number of remote host\r
+ // - local_port: port number to receive on - 0 to get an ephemeral port.\r
+ UDP_client(const std::string& remote_address, unsigned short remote_port, unsigned short local_port=0);\r
+\r
+ // Send/Receive datagram packets to/from the given address/remote port on the given local port\r
+ // Enables default send to remote address/port\r
+ // - remote_address: IP address of remote host - pre-looked-up using ip_lookup\r
+ // - remote_port: port number of remote host\r
+ // - local_port: port number to receive on - 0 to get an ephemeral port.\r
+ UDP_client(unsigned long remote_address, unsigned short remote_port, unsigned short local_port=0);\r
+\r
+ ////////////////////////////////////////////////////////////////////////////\r
+ // initialisation, connection\r
+\r
+ // function for performing IP lookup (i.e. gethostbyname)\r
+ // could be standalone but making it a member means that it can use the socket's error handler\r
+ // i.e. if this fails, the sockets error code will be set - clear it to use the socket again\r
+ // - remote_address: IP name (stlplus.sourceforge.net) or dotted number (216.34.181.96)\r
+ // - returns the IP address as a long integer - zero if there's an error\r
+ // unsigned long ip_lookup(const std::string& remote_address);\r
+ using IP_socket::ip_lookup;\r
+\r
+ // Send/Receive datagram packets to/from the given address/remote port on the local port.\r
+ // Enables default send to remote address/port\r
+ // - remote_address: IP name or number of remote host\r
+ // - remote_port: port number of remote host\r
+ // - local_port: port number to receive on - 0 to get an ephemeral port.\r
+ // - returns a success flag\r
+ bool initialise(const std::string& remote_address, unsigned short remote_port, unsigned short local_port=0);\r
+\r
+ // Send/Receive datagram packets to/from the given address/remote port on the given local port\r
+ // Enables default send to remote address/port\r
+ // - remote_address: IP address of remote host - pre-looked-up using ip_lookup\r
+ // - remote_port: port number of remote host\r
+ // - local_port: port number to receive on - 0 to get an ephemeral port.\r
+ // - returns a success flag\r
+ bool initialise(unsigned long remote_address, unsigned short remote_port, unsigned short local_port=0);\r
+\r
+ // test whether this is an initialised socket\r
+ // - returns whether this is initialised\r
+ // bool initialised(void) const;\r
+ using IP_socket::initialised;\r
+\r
+ // close, i.e. disconnect the socket\r
+ // - returns a success flag\r
+ // bool close(void);\r
+ using IP_socket::close;\r
+\r
+ ////////////////////////////////////////////////////////////////////////////\r
+ // sending/receiving\r
+\r
+ // test whether a socket is connected and ready to send data, returns if ready or on timeout\r
+ // - timeout: how long to wait in microseconds if not connected yet (blocking)\r
+ // - returns status\r
+ // bool send_ready(unsigned timeout = 0);\r
+ using IP_socket::send_ready;\r
+\r
+ // send to the remote address/port setup in initialise, from the local port also setup in initialise.\r
+ // send data through the socket as a single datagram\r
+ // - packet: string containing data to be sent - if data is successfully sent it is removed\r
+ // - returns success flag\r
+ bool send(std::string& packet);\r
+\r
+ // test whether a socket is connected and ready to receive data, returns if ready or on timeout\r
+ // - timeout: how long to wait in microseconds if not connected yet (blocking)\r
+ // - returns status\r
+ // bool receive_ready(unsigned timeout = 0);\r
+ using IP_socket::receive_ready;\r
+\r
+ // datagram receive\r
+ // - packet: string to receive data from datagram - if data is successfully sent it is appended\r
+ // - returns success flag - i.e. packet successfully received\r
+ bool receive(std::string& packet);\r
+\r
+ ////////////////////////////////////////////////////////////////////////////\r
+ // informational\r
+\r
+ // the local port number of the connection\r
+ // returns the port number, 0 if not bound to a port\r
+ // unsigned short local_port(void) const;\r
+ using IP_socket::local_port;\r
+\r
+ // the remote address of the connection\r
+ // returns the address, 0 if ANY address\r
+ // unsigned long remote_address(void) const;\r
+ using IP_socket::remote_address;\r
+\r
+ // the remote port number of the connection\r
+ // returns the port number, 0 if not bound to a port\r
+ // unsigned short remote_port(void) const;\r
+ using IP_socket::remote_port;\r
+\r
+ ////////////////////////////////////////////////////////////////////////////\r
+ // error handling\r
+ // errors are set internally\r
+ // an error code of 0 is the test for no error, don't rely on the message being an empty string\r
+ // an error code != 0 means an error, then there will be a message explaining the error\r
+\r
+ // if an error is set it stays set - so you must clear it before further operations\r
+ // void clear_error (void);\r
+ using IP_socket::clear_error ;\r
+\r
+ // get the error code of the last error\r
+ // int error(void) const;\r
+ using IP_socket::error;\r
+\r
+ // get the explanatory message of the last error\r
+ // std::string message(void) const;\r
+ using IP_socket::message;\r
+\r
+ ////////////////////////////////////////////////////////////////////////////\r
+\r
+ private:\r
+ IP_socket m_socket;\r
+ };\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+ // UDP server - creates a connectionless (multicast) listener socket\r
+\r
+ class UDP_server : protected IP_socket\r
+ {\r
+ public:\r
+\r
+ // create an uninitialised socket\r
+ UDP_server(void);\r
+\r
+ // Initialise socket.\r
+ // Receive datagram packets from any address on provided local receiving port.\r
+ // - local_port: port number to receive on - 0 to get an ephemeral port.\r
+ UDP_server(unsigned short local_port);\r
+\r
+ ////////////////////////////////////////////////////////////////////////////\r
+ // initialisation, connection\r
+\r
+ // function for performing IP lookup (i.e. gethostbyname)\r
+ // could be standalone but making it a member means that it can use the socket's error handler\r
+ // i.e. if this fails, the sockets error code will be set - clear it to use the socket again\r
+ // - remote_address: IP name (stlplus.sourceforge.net) or dotted number (216.34.181.96)\r
+ // - returns the IP address as a long integer - zero if there's an error\r
+ // unsigned long ip_lookup(const std::string& remote_address);\r
+ using IP_socket::ip_lookup;\r
+\r
+ // Initialise socket.\r
+ // Receive datagram packets from any address on provided local receiving port.\r
+ // - local_port: port number to receive on - 0 to get an ephemeral port.\r
+ // - returns a success flag\r
+ bool initialise(unsigned short local_port);\r
+\r
+ // test whether this is an initialised socket\r
+ // - returns whether this is initialised\r
+ // bool initialised(void) const;\r
+ using IP_socket::initialised;\r
+\r
+ // close, i.e. disconnect the socket\r
+ // - returns a success flag\r
+ // bool close(void);\r
+ using IP_socket::close;\r
+\r
+ ////////////////////////////////////////////////////////////////////////////\r
+ // sending/receiving\r
+\r
+ // test whether a socket is connected and ready to send data, returns if ready or on timeout\r
+ // - timeout: how long to wait in microseconds if not connected yet (blocking)\r
+ // - returns status\r
+ // bool send_ready(unsigned timeout = 0);\r
+ using IP_socket::send_ready;\r
+\r
+ // send to the address/port given here, from the local port setup in initialise.\r
+ // send data through the socket as a single datagram\r
+ // - packet: string containing data to be sent - if data is successfully sent it is removed\r
+ // - remote_address: IP name (stlplus.sourceforge.net) or dotted number (216.34.181.96)\r
+ // - remote_port: port number of remote host\r
+ // - returns success flag\r
+ bool send(std::string& packet, const std::string& remote_address, unsigned short remote_port);\r
+\r
+ // send to the address/port given here, from the local port setup in initialise.\r
+ // send data through the socket as a single datagram\r
+ // - packet: string containing data to be sent - if data is successfully sent it is removed\r
+ // - remote_address: pre-looked-up IP address of remote host\r
+ // - remote_port: port number of remote host\r
+ // - returns success flag\r
+ bool send(std::string& packet, unsigned long remote_address, unsigned short remote_port);\r
+\r
+ // test whether a socket is connected and ready to receive data, returns if ready or on timeout\r
+ // - timeout: how long to wait in microseconds if not connected yet (blocking)\r
+ // - returns status\r
+ // bool receive_ready(unsigned timeout = 0);\r
+ using IP_socket::receive_ready;\r
+\r
+ // datagram receive\r
+ // - packet: string to receive data from datagram - if data is successfully sent it is appended\r
+ // - remote_address: the address of the client that sent the packet, can then be used to reply\r
+ // - remote_port: the port of the client that sent the packet, can then be used to reply\r
+ // - returns success flag - i.e. packet successfully received\r
+ bool receive(std::string& packet, unsigned long& remote_address, unsigned short& remote_port);\r
+\r
+ ////////////////////////////////////////////////////////////////////////////\r
+ // informational\r
+\r
+ // the local port number of the connection\r
+ // returns the port number, 0 if not bound to a port\r
+ // unsigned short local_port(void) const;\r
+ using IP_socket::local_port;\r
+\r
+ ////////////////////////////////////////////////////////////////////////////\r
+ // error handling\r
+ // errors are set internally\r
+ // an error code of 0 is the test for no error, don't rely on the message being an empty string\r
+ // an error code != 0 means an error, then there will be a message explaining the error\r
+\r
+ // if an error is set it stays set - so you must clear it before further operations\r
+ // void clear_error(void);\r
+ using IP_socket::clear_error;\r
+\r
+ // get the error code of the last error\r
+ // int error(void) const;\r
+ using IP_socket::error;\r
+\r
+ // get the explanatory message of the last error\r
+ // std::string message(void) const;\r
+ using IP_socket::message;\r
+\r
+ ////////////////////////////////////////////////////////////////////////////\r
+ };\r
+\r
+ /////////////////////////////////////////////////////////////////////////////\r
+ // fire and forget UDP client packet send function\r
+\r
+ bool UDP_send(const std::string& packet,\r
+ const std::string& remote_address, unsigned short remote_port, unsigned short local_port = 0);\r
+\r
+ ////////////////////////////////////////////////////////////////////////////////\r
+\r
+} // end namespace stlplus\r
+\r
+#endif\r
-////////////////////////////////////////////////////////////////////////////////
-
-// Author: Andy Rushton
-// Copyright: (c) Southampton University 1999-2004
-// (c) Andy Rushton 2004-2009
-// License: BSD License, see ../docs/license.html
-
-////////////////////////////////////////////////////////////////////////////////
-#include "version.hpp"
-////////////////////////////////////////////////////////////////////////////////
-
-namespace stlplus
-{
-
- std::string version(void)
- {
- return STLPLUS_VERSION;
- }
-
-} // end namespace stlplus
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+// Author: Andy Rushton\r
+// Copyright: (c) Southampton University 1999-2004\r
+// (c) Andy Rushton 2004 onwards\r
+// License: BSD License, see ../docs/license.html\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "version.hpp"\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+namespace stlplus\r
+{\r
+\r
+ std::string version(void)\r
+ {\r
+ return STLPLUS_VERSION;\r
+ }\r
+\r
+} // end namespace stlplus\r
-#ifndef STLPLUS_VERSION
-////////////////////////////////////////////////////////////////////////////////
-
-// Author: Andy Rushton
-// Copyright: (c) Southampton University 1999-2004
-// (c) Andy Rushton 2004-2009
-// License: BSD License, see ../docs/license.html
-
-// Contains just the STLplus version number
-
-////////////////////////////////////////////////////////////////////////////////
-#include "portability_fixes.hpp"
-#include <string>
-
-#define STLPLUS_VERSION "3.5"
-
-namespace stlplus
-{
-
- std::string version(void);
-
-}
-////////////////////////////////////////////////////////////////////////////////
-#endif
+#ifndef STLPLUS_VERSION\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+// Author: Andy Rushton\r
+// Copyright: (c) Southampton University 1999-2004\r
+// (c) Andy Rushton 2004 onwards\r
+// License: BSD License, see ../docs/license.html\r
+\r
+// Contains just the STLplus version number\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "portability_fixes.hpp"\r
+#include <string>\r
+\r
+#define STLPLUS_VERSION "3.7"\r
+\r
+namespace stlplus\r
+{\r
+\r
+ std::string version(void);\r
+\r
+}\r
+////////////////////////////////////////////////////////////////////////////////\r
+#endif\r
-////////////////////////////////////////////////////////////////////////////////
-
-// Author: Andy Rushton
-// Copyright: (c) Southampton University 1999-2004
-// (c) Andy Rushton 2004-2009
-// License: BSD License, see ../docs/license.html
-
-// Simple wildcard matching function.
-
-// WARNING: wheel re-invention follows
-// Given that all shells perform wildcard matching, why don't the library writers put it in the C run-time????????
-
-////////////////////////////////////////////////////////////////////////////////
-#include "wildcard.hpp"
-
-namespace stlplus
-{
-
- // function for testing whether a character matches a set
- // I can't remember the exact rules and I have no definitive references but:
- // a set contains characters, escaped characters (I think) and ranges in the form a-z
- // The character '-' can only appear at the start of the set where it is not interpreted as a range
- // This is a horrible mess - blame the Unix folks for making a hash of wildcards
- // first expand any ranges and remove escape characters to make life more palatable
-
- static bool match_set (const std::string& set, char match)
- {
- std::string simple_set;
- for (std::string::const_iterator i = set.begin(); i != set.end(); ++i)
- {
- switch(*i)
- {
- case '-':
- {
- if (i == set.begin())
- {
- simple_set += *i;
- }
- else if (i+1 == set.end())
- {
- return false;
- }
- else
- {
- // found a set. The first character is already in the result, so first remove it (the set might be empty)
- simple_set.erase(simple_set.end()-1);
- char last = *++i;
- for (char ch = *(i-2); ch <= last; ch++)
- {
- simple_set += ch;
- }
- }
- break;
- }
- case '\\':
- if (i+1 == set.end()) {return false;}
- simple_set += *++i;
- break;
- default:
- simple_set += *i;
- break;
- }
- }
- std::string::size_type result = simple_set.find(match);
- return result != std::string::npos;
- }
-
- // the recursive bit - basically whenever a * is found you recursively call this for each candidate substring match
- // until either it succeeds or you run out of string to match
- // for each * in the wildcard another level of recursion is created
-
- static bool match_remainder (const std::string& wild, std::string::const_iterator wildi, const std::string& match, std::string::const_iterator matchi)
- {
- //cerr << "match_remainder called at " << *matchi << " with wildcard " << *wildi << endl;
- while (wildi != wild.end() && matchi != match.end())
- {
- //cerr << "trying to match " << *matchi << " with wildcard " << *wildi << endl;
- switch(*wildi)
- {
- case '*':
- {
- ++wildi;
- ++matchi;
- for (std::string::const_iterator i = matchi; i != match.end(); ++i)
- {
- // deal with * at the end of the wildcard - there is no remainder then
- if (wildi == wild.end())
- {
- if (i == match.end()-1)
- return true;
- }
- else if (match_remainder(wild, wildi, match, i))
- {
- return true;
- }
- }
- return false;
- }
- case '[':
- {
- // scan for the end of the set using a similar method for avoiding escaped characters
- bool found = false;
- std::string::const_iterator end = wildi + 1;
- for (; !found && end != wild.end(); ++end)
- {
- switch(*end)
- {
- case ']':
- {
- // found the set, now match with its contents excluding the brackets
- if (!match_set(wild.substr(wildi - wild.begin() + 1, end - wildi - 1), *matchi))
- return false;
- found = true;
- break;
- }
- case '\\':
- if (end == wild.end()-1)
- return false;
- ++end;
- break;
- default:
- break;
- }
- }
- if (!found)
- return false;
- ++matchi;
- wildi = end;
- break;
- }
- case '?':
- ++wildi;
- ++matchi;
- break;
- case '\\':
- if (wildi == wild.end()-1)
- return false;
- ++wildi;
- if (*wildi != *matchi)
- return false;
- ++wildi;
- ++matchi;
- break;
- default:
- if (*wildi != *matchi)
- return false;
- ++wildi;
- ++matchi;
- break;
- }
- }
- bool result = wildi == wild.end() && matchi == match.end();
- return result;
- }
-
- // like all recursions the exported function has a simpler interface than the
- // recursive function and is just a 'seed' to the recursion itself
-
- bool wildcard(const std::string& wild, const std::string& match)
- {
- return match_remainder(wild, wild.begin(), match, match.begin());
- }
-
-} // end namespace stlplus
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+// Author: Andy Rushton\r
+// Copyright: (c) Southampton University 1999-2004\r
+// (c) Andy Rushton 2004 onwards\r
+// License: BSD License, see ../docs/license.html\r
+\r
+// Simple wildcard matching function.\r
+\r
+// WARNING: wheel re-invention follows\r
+// Given that all shells perform wildcard matching, why don't the library writers put it in the C run-time????????\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "wildcard.hpp"\r
+\r
+namespace stlplus\r
+{\r
+\r
+ // function for testing whether a character matches a set\r
+ // I can't remember the exact rules and I have no definitive references but:\r
+ // a set contains characters, escaped characters (I think) and ranges in the form a-z\r
+ // The character '-' can only appear at the start of the set where it is not interpreted as a range\r
+ // This is a horrible mess - blame the Unix folks for making a hash of wildcards\r
+ // first expand any ranges and remove escape characters to make life more palatable\r
+\r
+ static bool match_set (const std::string& set, char match)\r
+ {\r
+ std::string simple_set;\r
+ for (std::string::const_iterator i = set.begin(); i != set.end(); ++i)\r
+ {\r
+ switch(*i)\r
+ {\r
+ case '-':\r
+ {\r
+ if (i == set.begin())\r
+ {\r
+ simple_set += *i;\r
+ }\r
+ else if (i+1 == set.end())\r
+ {\r
+ return false;\r
+ }\r
+ else\r
+ {\r
+ // found a set. The first character is already in the result, so first remove it (the set might be empty)\r
+ simple_set.erase(simple_set.end()-1);\r
+ char last = *++i;\r
+ for (char ch = *(i-2); ch <= last; ch++)\r
+ {\r
+ simple_set += ch;\r
+ }\r
+ }\r
+ break;\r
+ }\r
+ case '\\':\r
+ if (i+1 == set.end()) {return false;}\r
+ simple_set += *++i;\r
+ break;\r
+ default:\r
+ simple_set += *i;\r
+ break;\r
+ }\r
+ }\r
+ std::string::size_type result = simple_set.find(match);\r
+ return result != std::string::npos;\r
+ }\r
+\r
+ // the recursive bit - basically whenever a * is found you recursively call this for each candidate substring match\r
+ // until either it succeeds or you run out of string to match\r
+ // for each * in the wildcard another level of recursion is created\r
+\r
+ static bool match_remainder (const std::string& wild, std::string::const_iterator wildi, const std::string& match, std::string::const_iterator matchi)\r
+ {\r
+ //cerr << "match_remainder called at " << *matchi << " with wildcard " << *wildi << endl;\r
+ while (wildi != wild.end() && matchi != match.end())\r
+ {\r
+ //cerr << "trying to match " << *matchi << " with wildcard " << *wildi << endl;\r
+ switch(*wildi)\r
+ {\r
+ case '*':\r
+ {\r
+ ++wildi;\r
+ ++matchi;\r
+ for (std::string::const_iterator i = matchi; i != match.end(); ++i)\r
+ {\r
+ // deal with * at the end of the wildcard - there is no remainder then\r
+ if (wildi == wild.end())\r
+ {\r
+ if (i == match.end()-1)\r
+ return true;\r
+ }\r
+ else if (match_remainder(wild, wildi, match, i))\r
+ {\r
+ return true;\r
+ }\r
+ }\r
+ return false;\r
+ }\r
+ case '[':\r
+ {\r
+ // scan for the end of the set using a similar method for avoiding escaped characters\r
+ bool found = false;\r
+ std::string::const_iterator end = wildi + 1;\r
+ for (; !found && end != wild.end(); ++end)\r
+ {\r
+ switch(*end)\r
+ {\r
+ case ']':\r
+ {\r
+ // found the set, now match with its contents excluding the brackets\r
+ if (!match_set(wild.substr(wildi - wild.begin() + 1, end - wildi - 1), *matchi))\r
+ return false;\r
+ found = true;\r
+ break;\r
+ }\r
+ case '\\':\r
+ if (end == wild.end()-1)\r
+ return false;\r
+ ++end;\r
+ break;\r
+ default:\r
+ break;\r
+ }\r
+ }\r
+ if (!found)\r
+ return false;\r
+ ++matchi;\r
+ wildi = end;\r
+ break;\r
+ }\r
+ case '?':\r
+ ++wildi;\r
+ ++matchi;\r
+ break;\r
+ case '\\':\r
+ if (wildi == wild.end()-1)\r
+ return false;\r
+ ++wildi;\r
+ if (*wildi != *matchi)\r
+ return false;\r
+ ++wildi;\r
+ ++matchi;\r
+ break;\r
+ default:\r
+ if (*wildi != *matchi)\r
+ return false;\r
+ ++wildi;\r
+ ++matchi;\r
+ break;\r
+ }\r
+ }\r
+ bool result = wildi == wild.end() && matchi == match.end();\r
+ return result;\r
+ }\r
+\r
+ // like all recursions the exported function has a simpler interface than the\r
+ // recursive function and is just a 'seed' to the recursion itself\r
+\r
+ bool wildcard(const std::string& wild, const std::string& match)\r
+ {\r
+ return match_remainder(wild, wild.begin(), match, match.begin());\r
+ }\r
+\r
+} // end namespace stlplus\r
-#ifndef STLPLUS_WILDCARD
-#define STLPLUS_WILDCARD
-////////////////////////////////////////////////////////////////////////////////
-
-// Author: Andy Rushton
-// Copyright: (c) Southampton University 1999-2004
-// (c) Andy Rushton 2004-2009
-// License: BSD License, see ../docs/license.html
-
-// This is a portable interface to wildcard matching.
-
-// The problem:
-// * matches any number of characters - this is achieved by matching 1 and seeing if the remainder matches
-// if not, try 2 characters and see if the remainder matches etc.
-// this must be recursive, not iterative, so that multiple *s can appear in the same wildcard expression
-// ? matches exactly one character so doesn't need the what-if approach
-// \ escapes special characters such as *, ? and [
-// [] matches exactly one character in the set - the difficulty is the set can contain ranges, e.g [a-zA-Z0-9]
-// a set cannot be empty and the ] character can be included by making it the first character
-
-////////////////////////////////////////////////////////////////////////////////
-#include "portability_fixes.hpp"
-#include <string>
-
-namespace stlplus
-{
-
- // wild = the wildcard expression
- // match = the string to test against that expression
- // e.g. wildcard("[a-f]*", "fred") returns true
- bool wildcard(const std::string& wild, const std::string& match);
-
-}
-
-#endif
+#ifndef STLPLUS_WILDCARD\r
+#define STLPLUS_WILDCARD\r
+////////////////////////////////////////////////////////////////////////////////\r
+\r
+// Author: Andy Rushton\r
+// Copyright: (c) Southampton University 1999-2004\r
+// (c) Andy Rushton 2004 onwards\r
+// License: BSD License, see ../docs/license.html\r
+\r
+// This is a portable interface to wildcard matching.\r
+\r
+// The problem:\r
+// * matches any number of characters - this is achieved by matching 1 and seeing if the remainder matches\r
+// if not, try 2 characters and see if the remainder matches etc.\r
+// this must be recursive, not iterative, so that multiple *s can appear in the same wildcard expression\r
+// ? matches exactly one character so doesn't need the what-if approach\r
+// \ escapes special characters such as *, ? and [\r
+// [] matches exactly one character in the set - the difficulty is the set can contain ranges, e.g [a-zA-Z0-9]\r
+// a set cannot be empty and the ] character can be included by making it the first character\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+#include "portability_fixes.hpp"\r
+#include <string>\r
+\r
+namespace stlplus\r
+{\r
+\r
+ // wild = the wildcard expression\r
+ // match = the string to test against that expression\r
+ // e.g. wildcard("[a-f]*", "fred") returns true\r
+ bool wildcard(const std::string& wild, const std::string& match);\r
+\r
+}\r
+\r
+#endif\r