From: Charles McGarvey Date: Mon, 14 Jun 2010 22:13:37 +0000 (-0600) Subject: cleanup stlplus files X-Git-Url: https://git.brokenzipper.com/gitweb?a=commitdiff_plain;h=5846afb00833cc72fe72422ca896d2387c712cb4;p=chaz%2Fyoink cleanup stlplus files --- diff --git a/src/stlplus/README.txt b/src/stlplus/README.txt deleted file mode 100644 index dad518e..0000000 --- a/src/stlplus/README.txt +++ /dev/null @@ -1,19 +0,0 @@ -This directory is for implementing STLplus as a library collection. The -libraries are to be found in the directories: - - containers - persistence - portability - strings - subsystems - -The documentation is in the 'docs' directory and starts with index.html. - -To build the STLplus3 library collection, use the project files supplied at -this level - 'Makefile' for gcc, 'stlplus3.sln' for Visual Studio 9 (2008), -'stlplus3.dsw' for Visual Studio 6 (and 7 or 8). - -The 'source' directory is provided with script files that allow the library -collection to be merged into one large library - termed the monolithic build. -See source/README.txt for instructions. - diff --git a/src/stlplus/containers/containers.hpp b/src/stlplus/containers/containers.hpp index 5863758..e99d313 100644 --- a/src/stlplus/containers/containers.hpp +++ b/src/stlplus/containers/containers.hpp @@ -1,23 +1,23 @@ -#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 +#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 diff --git a/src/stlplus/containers/containers_fixes.hpp b/src/stlplus/containers/containers_fixes.hpp index ad29f99..f244323 100644 --- a/src/stlplus/containers/containers_fixes.hpp +++ b/src/stlplus/containers/containers_fixes.hpp @@ -1,132 +1,132 @@ -#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 fn () { -// typedef typename someclass::member_type local_type; -// ^^^^^^^^ -// 2) in a function parameter declaration, with similar rules to the above -// e.g. template fn (typename someclass::member_type) -// ^^^^^^^^ -// 3) in instantiating a template, the parameter to the template, with similar rules to the above -// e.g. template_class::member_type> -// ^^^^^^^^ -// 4) Return expressions -// e.g. return typename ntree::const_iterator(this,m_root); -// ^^^^^^^^ -// 5) Creating temporary objects when passing arguments to a function or constructor -// e.g. return typename ntree::const_prefix_iterator(typename ntree::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 +#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 fn () { +// typedef typename someclass::member_type local_type; +// ^^^^^^^^ +// 2) in a function parameter declaration, with similar rules to the above +// e.g. template fn (typename someclass::member_type) +// ^^^^^^^^ +// 3) in instantiating a template, the parameter to the template, with similar rules to the above +// e.g. template_class::member_type> +// ^^^^^^^^ +// 4) Return expressions +// e.g. return typename ntree::const_iterator(this,m_root); +// ^^^^^^^^ +// 5) Creating temporary objects when passing arguments to a function or constructor +// e.g. return typename ntree::const_prefix_iterator(typename ntree::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 diff --git a/src/stlplus/containers/copy_functors.hpp b/src/stlplus/containers/copy_functors.hpp index c82ee9f..68f2b21 100644 --- a/src/stlplus/containers/copy_functors.hpp +++ b/src/stlplus/containers/copy_functors.hpp @@ -1,66 +1,66 @@ -#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 - 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 - 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 - 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 +#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 + 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 + 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 + 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 diff --git a/src/stlplus/containers/digraph.hpp b/src/stlplus/containers/digraph.hpp index 8281998..7b603c1 100644 --- a/src/stlplus/containers/digraph.hpp +++ b/src/stlplus/containers/digraph.hpp @@ -1,507 +1,507 @@ -#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 -#include -#include - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - // Internals - - template class digraph_node; - template class digraph_arc; - template 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::iterator - points to a non-const node - // digraph::const_iterator - points to a const node - // digraph::arc_iterator - points to a non-const arc - // digraph::const_arc_iterator - points to a const arc - // and this is the form in which they should be used - - template - class digraph_iterator : public safe_iterator, digraph_node > - { - public: - friend class digraph; - - // local type definitions - // an iterator points to an object whilst a const_iterator points to a const object - typedef digraph_iterator iterator; - typedef digraph_iterator const_iterator; - typedef digraph_iterator 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* node); - // constructor used by digraph to create an end iterator - explicit digraph_iterator(const digraph* owner); - // used to create an alias of an iterator - explicit digraph_iterator(const safe_iterator, digraph_node >& iterator); - }; - - //////////////////////////////////////////////////////////////////////////////// - - template - class digraph_arc_iterator : public safe_iterator, digraph_arc > - { - public: - friend class digraph; - - // local type definitions - // an iterator points to an object whilst a const_iterator points to a const object - typedef digraph_arc_iterator iterator; - typedef digraph_arc_iterator const_iterator; - typedef digraph_arc_iterator 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* arc); - // constructor used by digraph to create an end iterator - explicit digraph_arc_iterator(const digraph* owner); - // used to create an alias of an iterator - explicit digraph_arc_iterator(const safe_iterator, digraph_arc >& iterator); - }; - - //////////////////////////////////////////////////////////////////////////////// - // The Graph class - // NT is the Node type and AT is the Arc type - //////////////////////////////////////////////////////////////////////////////// - - template - class digraph - { - public: - // STL-like typedefs for the types and iterators - typedef NT node_type; - typedef AT arc_type; - typedef digraph_iterator iterator; - typedef digraph_iterator const_iterator; - typedef digraph_arc_iterator arc_iterator; - typedef digraph_arc_iterator 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_vector; - typedef std::vector 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 path_vector; - typedef std::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 node_vector; - typedef std::vector 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&, const_arc_iterator); - - // a value representing an unknown offset - // Note that it's static so use in the form digraph::npos() - static unsigned npos(void); - - ////////////////////////////////////////////////////////////////////////// - // Constructors, destructors and copies - - digraph(void); - ~digraph(void); - - // copy constructor and assignment both copy the graph - digraph(const digraph&); - digraph& operator=(const digraph&); - - ////////////////////////////////////////////////////////////////////////// - // 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::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 sort(arc_select_fn = 0) const; - std::pair 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; - friend class digraph_iterator; - friend class digraph_arc_iterator; - friend class digraph_arc_iterator; - - typedef std::set 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* m_nodes_begin; - digraph_node* m_nodes_end; - digraph_arc* m_arcs_begin; - digraph_arc* m_arcs_end; - }; - - //////////////////////////////////////////////////////////////////////////////// - -} // end namespace stlplus - -#include "digraph.tpp" -#endif +#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 +#include +#include + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // Internals + + template class digraph_node; + template class digraph_arc; + template 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::iterator - points to a non-const node + // digraph::const_iterator - points to a const node + // digraph::arc_iterator - points to a non-const arc + // digraph::const_arc_iterator - points to a const arc + // and this is the form in which they should be used + + template + class digraph_iterator : public safe_iterator, digraph_node > + { + public: + friend class digraph; + + // local type definitions + // an iterator points to an object whilst a const_iterator points to a const object + typedef digraph_iterator iterator; + typedef digraph_iterator const_iterator; + typedef digraph_iterator 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* node); + // constructor used by digraph to create an end iterator + explicit digraph_iterator(const digraph* owner); + // used to create an alias of an iterator + explicit digraph_iterator(const safe_iterator, digraph_node >& iterator); + }; + + //////////////////////////////////////////////////////////////////////////////// + + template + class digraph_arc_iterator : public safe_iterator, digraph_arc > + { + public: + friend class digraph; + + // local type definitions + // an iterator points to an object whilst a const_iterator points to a const object + typedef digraph_arc_iterator iterator; + typedef digraph_arc_iterator const_iterator; + typedef digraph_arc_iterator 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* arc); + // constructor used by digraph to create an end iterator + explicit digraph_arc_iterator(const digraph* owner); + // used to create an alias of an iterator + explicit digraph_arc_iterator(const safe_iterator, digraph_arc >& iterator); + }; + + //////////////////////////////////////////////////////////////////////////////// + // The Graph class + // NT is the Node type and AT is the Arc type + //////////////////////////////////////////////////////////////////////////////// + + template + class digraph + { + public: + // STL-like typedefs for the types and iterators + typedef NT node_type; + typedef AT arc_type; + typedef digraph_iterator iterator; + typedef digraph_iterator const_iterator; + typedef digraph_arc_iterator arc_iterator; + typedef digraph_arc_iterator 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_vector; + typedef std::vector 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 path_vector; + typedef std::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 node_vector; + typedef std::vector 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&, const_arc_iterator); + + // a value representing an unknown offset + // Note that it's static so use in the form digraph::npos() + static unsigned npos(void); + + ////////////////////////////////////////////////////////////////////////// + // Constructors, destructors and copies + + digraph(void); + ~digraph(void); + + // copy constructor and assignment both copy the graph + digraph(const digraph&); + digraph& operator=(const digraph&); + + ////////////////////////////////////////////////////////////////////////// + // 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::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 sort(arc_select_fn = 0) const; + std::pair 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; + friend class digraph_iterator; + friend class digraph_arc_iterator; + friend class digraph_arc_iterator; + + typedef std::set 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* m_nodes_begin; + digraph_node* m_nodes_end; + digraph_arc* m_arcs_begin; + digraph_arc* m_arcs_end; + }; + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus + +#include "digraph.tpp" +#endif diff --git a/src/stlplus/containers/digraph.tpp b/src/stlplus/containers/digraph.tpp index c10586b..804954a 100644 --- a/src/stlplus/containers/digraph.tpp +++ b/src/stlplus/containers/digraph.tpp @@ -1,1483 +1,1483 @@ -//////////////////////////////////////////////////////////////////////////////// - -// 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 -#include - -//////////////////////////////////////////////////////////////////////////////// -// Internals - -namespace stlplus -{ - - template - class digraph_node - { - public: - master_iterator, digraph_node > m_master; - NT m_data; - digraph_node* m_prev; - digraph_node* m_next; - std::vector*> m_inputs; - std::vector*> m_outputs; - public: - digraph_node(const digraph* owner, const NT& d = NT()) : - m_master(owner,this), m_data(d), m_prev(0), m_next(0) - { - } - ~digraph_node(void) - { - } - }; - - template - class digraph_arc - { - public: - master_iterator, digraph_arc > m_master; - AT m_data; - digraph_arc* m_prev; - digraph_arc* m_next; - digraph_node* m_from; - digraph_node* m_to; - digraph_arc(const digraph* owner, digraph_node* from = 0, digraph_node* 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 - digraph_iterator::digraph_iterator(void) - { - } - - // valid iterator - template - digraph_iterator::digraph_iterator(digraph_node* node) : - safe_iterator,digraph_node >(node->m_master) - { - } - - // end iterator - template - digraph_iterator::digraph_iterator(const digraph* owner) : - safe_iterator,digraph_node >(owner) - { - } - - // alias an iterator - template - digraph_iterator::digraph_iterator(const safe_iterator, digraph_node >& iterator) : - safe_iterator,digraph_node >(iterator) - { - } - - // destructor - template - digraph_iterator::~digraph_iterator(void) - { - } - - template - TYPENAME digraph_iterator::const_iterator digraph_iterator::constify (void) const - { - return digraph_iterator(*this); - } - - template - TYPENAME digraph_iterator::iterator digraph_iterator::deconstify (void) const - { - return digraph_iterator(*this); - } - - template - TYPENAME digraph_iterator::this_iterator& digraph_iterator::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 digraph_iterator::this_iterator digraph_iterator::operator ++ (int) - throw(null_dereference,end_dereference) - { - // post-increment is defined in terms of the pre-increment - digraph_iterator result(*this); - ++(*this); - return result; - } - - template - TYPENAME digraph_iterator::this_iterator& digraph_iterator::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 digraph_iterator::this_iterator digraph_iterator::operator -- (int) - throw(null_dereference,end_dereference) - { - // post-decrement is defined in terms of the pre-decrement - digraph_iterator result(*this); - --(*this); - return result; - } - - template - bool digraph_iterator::operator == (const TYPENAME digraph_iterator::this_iterator& r) const - { - return equal(r); - } - - template - bool digraph_iterator::operator != (const TYPENAME digraph_iterator::this_iterator& r) const - { - return !operator==(r); - } - - template - bool digraph_iterator::operator < (const TYPENAME digraph_iterator::this_iterator& r) const - { - return compare(r) < 0; - } - - template - TYPENAME digraph_iterator::reference digraph_iterator::operator*(void) const - throw(null_dereference,end_dereference) - { - this->assert_valid(); - return this->node()->m_data; - } - - template - TYPENAME digraph_iterator::pointer digraph_iterator::operator->(void) const - throw(null_dereference,end_dereference) - { - return &(operator*()); - } - - //////////////////////////////////////////////////////////////////////////////// - // Arc Iterator - - template - digraph_arc_iterator::digraph_arc_iterator(void) - { - } - - // valid iterator - template - digraph_arc_iterator::digraph_arc_iterator(digraph_arc* arc) : - safe_iterator,digraph_arc >(arc->m_master) - { - } - - // end iterator - template - digraph_arc_iterator::digraph_arc_iterator(const digraph* owner) : - safe_iterator,digraph_arc >(owner) - { - } - - // alias an iterator - template - digraph_arc_iterator::digraph_arc_iterator(const safe_iterator, digraph_arc >& iterator) : - safe_iterator,digraph_arc >(iterator) - { - } - - template - digraph_arc_iterator::~digraph_arc_iterator(void) - { - } - - template - TYPENAME digraph_arc_iterator::const_iterator digraph_arc_iterator::constify (void) const - { - return digraph_arc_iterator(*this); - } - - template - TYPENAME digraph_arc_iterator::iterator digraph_arc_iterator::deconstify (void) const - { - return digraph_arc_iterator(*this); - } - - template - TYPENAME digraph_arc_iterator::this_iterator& digraph_arc_iterator::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 digraph_arc_iterator::this_iterator digraph_arc_iterator::operator ++ (int) - throw(null_dereference,end_dereference) - { - // post-increment is defined in terms of the pre-increment - digraph_arc_iterator result(*this); - ++(*this); - return result; - } - - template - TYPENAME digraph_arc_iterator::this_iterator& digraph_arc_iterator::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 digraph_arc_iterator::this_iterator digraph_arc_iterator::operator -- (int) - throw(null_dereference,end_dereference) - { - // post-decrement is defined in terms of the pre-decrement - digraph_arc_iterator result(*this); - --(*this); - return result; - } - - template - bool digraph_arc_iterator::operator == (const TYPENAME digraph_arc_iterator::this_iterator& r) const - { - return equal(r); - } - - template - bool digraph_arc_iterator::operator != (const TYPENAME digraph_arc_iterator::this_iterator& r) const - { - return !operator==(r); - } - - template - bool digraph_arc_iterator::operator < (const TYPENAME digraph_arc_iterator::this_iterator& r) const - { - return compare(r) < 0; - } - - template - TYPENAME digraph_arc_iterator::reference digraph_arc_iterator::operator*(void) const - throw(null_dereference,end_dereference) - { - this->assert_valid(); - return this->node()->m_data; - } - - template - TYPENAME digraph_arc_iterator::pointer digraph_arc_iterator::operator->(void) const - throw(null_dereference,end_dereference) - { - return &(operator*()); - } - - //////////////////////////////////////////////////////////////////////////////// - // subtype utilities - - template - TYPENAME digraph::const_arc_vector digraph::constify_arcs(const TYPENAME digraph::arc_vector& arcs) const - throw(wrong_object,null_dereference,end_dereference) - { - std::vector > result; - for (unsigned i = 0; i < arcs.size(); i++) - { - arcs[i].assert_valid(this); - result.push_back(arcs[i].constify()); - } - return result; - } - - template - TYPENAME digraph::arc_vector digraph::deconstify_arcs(const TYPENAME digraph::const_arc_vector& arcs) const - throw(wrong_object,null_dereference,end_dereference) - { - std::vector > result; - for (unsigned i = 0; i < arcs.size(); i++) - { - arcs[i].assert_valid(this); - result.push_back(arcs[i].deconstify()); - } - return result; - } - - template - TYPENAME digraph::const_path_vector digraph::constify_paths(const TYPENAME digraph::path_vector& paths) const - throw(wrong_object,null_dereference,end_dereference) - { - std::vector > > result; - for (unsigned i = 0; i < paths.size(); i++) - result.push_back(constify_arcs(paths[i])); - return result; - } - - template - TYPENAME digraph::path_vector digraph::deconstify_paths(const TYPENAME digraph::const_path_vector& paths) const - throw(wrong_object,null_dereference,end_dereference) - { - std::vector > > result; - for (unsigned i = 0; i < paths.size(); i++) - result.push_back(deconstify_arcs(paths[i])); - return result; - } - - template - TYPENAME digraph::const_node_vector digraph::constify_nodes(const TYPENAME digraph::node_vector& nodes) const - throw(wrong_object,null_dereference,end_dereference) - { - std::vector > result; - for (unsigned i = 0; i < nodes.size(); i++) - { - nodes[i].assert_valid(this); - result.push_back(nodes[i].constify()); - } - return result; - } - - template - TYPENAME digraph::node_vector digraph::deconstify_nodes(const TYPENAME digraph::const_node_vector& nodes) const - throw(wrong_object,null_dereference,end_dereference) - { - std::vector > result; - for (unsigned i = 0; i < nodes.size(); i++) - { - nodes[i].assert_valid(this); - result.push_back(nodes[i].deconstify()); - } - return result; - } - - template - unsigned digraph::npos(void) - { - return(unsigned)-1; - } - - //////////////////////////////////////////////////////////////////////////////// - // Constructors etc. - - template - digraph::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 - digraph::~digraph(void) - { - clear(); - } - - template - digraph::digraph(const digraph& r) : - m_nodes_begin(0), m_nodes_end(0), m_arcs_begin(0), m_arcs_end(0) - { - *this = r; - } - - template - digraph& digraph::operator=(const digraph& 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 > xref; - for (digraph_iterator 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 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 - bool digraph::empty(void) const - { - return m_nodes_begin == 0; - } - - template - unsigned digraph::size(void) const - { - unsigned count = 0; - for (digraph_iterator i = begin(); i != end(); i++) - count++; - return count; - } - - template - TYPENAME digraph::iterator digraph::insert(const NT& node_data) - { - digraph_node* new_node = new digraph_node(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(new_node); - } - - template - TYPENAME digraph::iterator digraph::erase(TYPENAME digraph::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* next = iter.node()->m_next; - delete iter.node(); - // return the next node in the list - if (next) - return digraph_iterator(next); - else - return digraph_iterator(this); - } - - template - void digraph::clear(void) - { - // clearing the nodes also clears the arcs - for (digraph_iterator i = begin(); i != end(); ) - i = erase(i); - } - - template - TYPENAME digraph::const_iterator digraph::begin(void) const - { - if (m_nodes_begin) - return digraph_iterator(m_nodes_begin); - else - return digraph_iterator(this); - } - - template - TYPENAME digraph::iterator digraph::begin(void) - { - if (m_nodes_begin) - return digraph_iterator(m_nodes_begin); - else - return digraph_iterator(this); - } - - template - TYPENAME digraph::const_iterator digraph::end(void) const - { - return digraph_iterator(this); - } - - template - TYPENAME digraph::iterator digraph::end(void) - { - return digraph_iterator(this); - } - - template - unsigned digraph::fanin(TYPENAME digraph::const_iterator iter) const - throw(wrong_object,null_dereference,end_dereference) - { - iter.assert_valid(this); - return iter.node()->m_inputs.size(); - } - - template - unsigned digraph::fanin(TYPENAME digraph::iterator iter) - throw(wrong_object,null_dereference,end_dereference) - { - iter.assert_valid(this); - return iter.node()->m_inputs.size(); - } - - template - TYPENAME digraph::const_arc_iterator digraph::input(TYPENAME digraph::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(iter.node()->m_inputs[i]); - } - - template - TYPENAME digraph::arc_iterator digraph::input(TYPENAME digraph::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(iter.node()->m_inputs[i]); - } - - template - unsigned digraph::fanout(TYPENAME digraph::const_iterator iter) const - throw(wrong_object,null_dereference,end_dereference) - { - iter.assert_valid(this); - return iter.node()->m_outputs.size(); - } - - template - unsigned digraph::fanout(TYPENAME digraph::iterator iter) - throw(wrong_object,null_dereference,end_dereference) - { - iter.assert_valid(this); - return iter.node()->m_outputs.size(); - } - - template - TYPENAME digraph::const_arc_iterator digraph::output(TYPENAME digraph::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(iter.node()->m_outputs[i]); - } - - template - TYPENAME digraph::arc_iterator digraph::output(TYPENAME digraph::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(iter.node()->m_outputs[i]); - } - - template - TYPENAME digraph::const_arc_vector digraph::inputs(TYPENAME digraph::const_iterator node) const - throw(wrong_object,null_dereference,end_dereference) - { - node.assert_valid(this); - std::vector > result; - for (unsigned i = 0; i < fanin(node); i++) - result.push_back(input(node,i)); - return result; - } - - template - TYPENAME digraph::arc_vector digraph::inputs(TYPENAME digraph::iterator node) - throw(wrong_object,null_dereference,end_dereference) - { - node.assert_valid(this); - std::vector > result; - for (unsigned i = 0; i < fanin(node); i++) - result.push_back(input(node,i)); - return result; - } - - template - TYPENAME digraph::const_arc_vector digraph::outputs(TYPENAME digraph::const_iterator node) const - throw(wrong_object,null_dereference,end_dereference) - { - node.assert_valid(this); - std::vector > result; - for (unsigned i = 0; i < fanout(node); i++) - result.push_back(output(node,i)); - return result; - } - - template - TYPENAME digraph::arc_vector digraph::outputs(TYPENAME digraph::iterator node) - throw(wrong_object,null_dereference,end_dereference) - { - node.assert_valid(this); - std::vector > result; - for (unsigned i = 0; i < fanout(node); i++) - result.push_back(output(node,i)); - return result; - } - - template - unsigned digraph::output_offset(TYPENAME digraph::const_iterator from, - TYPENAME digraph::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::npos(); - } - - template - unsigned digraph::output_offset(TYPENAME digraph::iterator from, - TYPENAME digraph::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::npos(); - } - - template - unsigned digraph::input_offset(TYPENAME digraph::const_iterator to, - TYPENAME digraph::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::npos(); - } - - template - unsigned digraph::input_offset(TYPENAME digraph::iterator to, - TYPENAME digraph::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::npos(); - } - - //////////////////////////////////////////////////////////////////////////////// - // Basic Arc functions - - template - bool digraph::arc_empty(void) const - { - return m_arcs_end == 0; - } - - template - unsigned digraph::arc_size(void) const - { - unsigned count = 0; - for (digraph_arc_iterator i = arc_begin(); i != arc_end(); i++) - count++; - return count; - } - - template - TYPENAME digraph::arc_iterator digraph::arc_insert(TYPENAME digraph::iterator from, - TYPENAME digraph::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* new_arc = new digraph_arc(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(new_arc); - } - - template - TYPENAME digraph::arc_iterator digraph::arc_erase(TYPENAME digraph::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*>::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*>::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* next = iter.node()->m_next; - delete iter.node(); - if (next) - return digraph_arc_iterator(next); - else - return digraph_arc_iterator(this); - } - - template - void digraph::arc_clear(void) - { - for (digraph_arc_iterator a = arc_begin(); a != arc_end(); ) - a = arc_erase(a); - } - - template - TYPENAME digraph::const_arc_iterator digraph::arc_begin(void) const - { - if (m_arcs_begin) - return digraph_arc_iterator(m_arcs_begin); - else - return digraph_arc_iterator(this); - } - - template - TYPENAME digraph::arc_iterator digraph::arc_begin(void) - { - if (m_arcs_begin) - return digraph_arc_iterator(m_arcs_begin); - else - return digraph_arc_iterator(this); - } - - template - TYPENAME digraph::const_arc_iterator digraph::arc_end(void) const - { - return digraph_arc_iterator(this); - } - - template - TYPENAME digraph::arc_iterator digraph::arc_end(void) - { - return digraph_arc_iterator(this); - } - - template - TYPENAME digraph::const_iterator digraph::arc_from(TYPENAME digraph::const_arc_iterator iter) const - throw(wrong_object,null_dereference,end_dereference) - { - iter.assert_valid(this); - return digraph_iterator(iter.node()->m_from); - } - - template - TYPENAME digraph::iterator digraph::arc_from(TYPENAME digraph::arc_iterator iter) - throw(wrong_object,null_dereference,end_dereference) - { - iter.assert_valid(this); - return digraph_iterator(iter.node()->m_from); - } - - template - TYPENAME digraph::const_iterator digraph::arc_to(TYPENAME digraph::const_arc_iterator iter) const - throw(wrong_object,null_dereference,end_dereference) - { - iter.assert_valid(this); - return digraph_iterator(iter.node()->m_to); - } - - template - TYPENAME digraph::iterator digraph::arc_to(TYPENAME digraph::arc_iterator iter) - throw(wrong_object,null_dereference,end_dereference) - { - iter.assert_valid(this); - return digraph_iterator(iter.node()->m_to); - } - - template - void digraph::arc_move(TYPENAME digraph::arc_iterator arc, - TYPENAME digraph::iterator from, - TYPENAME digraph::iterator to) - throw(wrong_object,null_dereference,end_dereference) - { - arc_move_to(arc,to); - arc_move_from(arc,from); - } - - template - void digraph::arc_move_from(TYPENAME digraph::arc_iterator arc, - TYPENAME digraph::iterator from) - throw(wrong_object,null_dereference,end_dereference) - { - arc.assert_valid(this); - from.assert_valid(this); - for (TYPENAME std::vector*>::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 - void digraph::arc_move_to(TYPENAME digraph::arc_iterator arc, - TYPENAME digraph::iterator to) - throw(wrong_object,null_dereference,end_dereference) - { - arc.assert_valid(this); - to.assert_valid(this); - for (TYPENAME std::vector*>::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 - void digraph::arc_flip(TYPENAME digraph::arc_iterator arc) - throw(wrong_object,null_dereference,end_dereference) - { - arc_move(arc,arc_to(arc),arc_from(arc)); - } - - //////////////////////////////////////////////////////////////////////////////// - // Adjacency Algorithms - - template - bool digraph::adjacent(TYPENAME digraph::const_iterator from, - TYPENAME digraph::const_iterator to) const - throw(wrong_object,null_dereference,end_dereference) - { - return adjacent_arc(from,to) != arc_end(); - } - - template - bool digraph::adjacent(TYPENAME digraph::iterator from, - TYPENAME digraph::iterator to) - throw(wrong_object,null_dereference,end_dereference) - { - return adjacent_arc(from,to) != arc_end(); - } - - template - TYPENAME digraph::const_arc_iterator digraph::adjacent_arc(TYPENAME digraph::const_iterator from, - TYPENAME digraph::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 digraph::arc_iterator digraph::adjacent_arc(TYPENAME digraph::iterator from, - TYPENAME digraph::iterator to) - throw(wrong_object,null_dereference,end_dereference) - { - return adjacent_arc(from.constify(), to.constify()).deconstify(); - } - - template - TYPENAME digraph::const_arc_vector digraph::adjacent_arcs(TYPENAME digraph::const_iterator from, - TYPENAME digraph::const_iterator to) const - throw(wrong_object,null_dereference,end_dereference) - { - from.assert_valid(this); - to.assert_valid(this); - std::vector > 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 digraph::arc_vector digraph::adjacent_arcs(TYPENAME digraph::iterator from, - TYPENAME digraph::iterator to) - throw(wrong_object,null_dereference,end_dereference) - { - return deconstify_arcs(adjacent_arcs(from.constify(), to.constify())); - } - - template - TYPENAME digraph::const_node_vector digraph::input_adjacencies(TYPENAME digraph::const_iterator to) const - throw(wrong_object,null_dereference,end_dereference) - { - std::vector > result; - for (unsigned arc = 0; arc < fanin(to); arc++) - { - digraph_iterator from = arc_from(input(to, arc)); - if (std::find(result.begin(), result.end(), from) == result.end()) - result.push_back(from); - } - return result; - } - - template - TYPENAME digraph::node_vector digraph::input_adjacencies(TYPENAME digraph::iterator to) - throw(wrong_object,null_dereference,end_dereference) - { - return deconstify_nodes(input_adjacencies(to.constify())); - } - - template - TYPENAME digraph::const_node_vector digraph::output_adjacencies(TYPENAME digraph::const_iterator from) const - throw(wrong_object,null_dereference,end_dereference) - { - std::vector > result; - for (unsigned arc = 0; arc < fanout(from); arc++) - { - digraph_iterator to = arc_to(output(from, arc)); - if (find(result.begin(), result.end(), to) == result.end()) - result.push_back(to); - } - return result; - } - - template - TYPENAME digraph::node_vector digraph::output_adjacencies(TYPENAME digraph::iterator from) - throw(wrong_object,null_dereference,end_dereference) - { - return deconstify_nodes(output_adjacencies(from.constify())); - } - - //////////////////////////////////////////////////////////////////////////////// - // Topographical Sort Algorithms - - template - std::pair::const_node_vector, TYPENAME digraph::const_arc_vector> - digraph::sort(TYPENAME digraph::arc_select_fn select) const - { - std::vector > result; - std::vector > errors; - // build a map containing the number of fanins to each node that must be visited before this one - std::map,unsigned> fanin_map; - for (digraph_iterator 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 input_arc = input(n,f); - digraph_iterator 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 current = result[i]; - for (unsigned f = 0; f < fanout(current); f++) - { - // only consider successors connected by selected arcs - digraph_arc_iterator output_arc = output(current, f); - digraph_iterator 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 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 input_arc = input(stuck_node, f); - if (!select || select(*this,input_arc)) - { - digraph_iterator 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 - std::pair::node_vector, TYPENAME digraph::arc_vector> - digraph::sort(TYPENAME digraph::arc_select_fn select) - { - std::pair >, - std::vector > > const_result = - const_cast*>(this)->sort(select); - - std::pair >, - std::vector > > result = - std::make_pair(deconstify_nodes(const_result.first),deconstify_arcs(const_result.second)); - return result; - } - - template - TYPENAME digraph::const_node_vector digraph::dag_sort(TYPENAME digraph::arc_select_fn select) const - { - std::pair >, - std::vector > > result = sort(select); - if (result.second.empty()) return result.first; - return std::vector >(); - } - - template - TYPENAME digraph::node_vector digraph::dag_sort(TYPENAME digraph::arc_select_fn select) - { - return deconstify_nodes(const_cast*>(this)->dag_sort(select)); - } - //////////////////////////////////////////////////////////////////////////////// - // Path Algorithms - - template - bool digraph::path_exists_r(TYPENAME digraph::const_iterator from, - TYPENAME digraph::const_iterator to, - TYPENAME digraph::const_iterator_set& visited, - TYPENAME digraph::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 arc = output(from,i); - if (!select || select(*this, arc)) - { - digraph_iterator 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 - bool digraph::path_exists(TYPENAME digraph::const_iterator from, - TYPENAME digraph::const_iterator to, - TYPENAME digraph::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 > visited; - visited.insert(from); - return path_exists_r(from, to, visited, select); - } - - template - bool digraph::path_exists(TYPENAME digraph::iterator from, - TYPENAME digraph::iterator to, - TYPENAME digraph::arc_select_fn select) - throw(wrong_object,null_dereference,end_dereference) - { - return path_exists(from.constify(), to.constify(), select); - } - - template - void digraph::all_paths_r(TYPENAME digraph::const_iterator from, - TYPENAME digraph::const_iterator to, - TYPENAME digraph::const_arc_vector& so_far, - TYPENAME digraph::const_path_vector& result, - TYPENAME digraph::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 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 digraph::const_path_vector - digraph::all_paths(TYPENAME digraph::const_iterator from, - TYPENAME digraph::const_iterator to, - TYPENAME digraph::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 > > result; - std::vector > so_far; - all_paths_r(from, to, so_far, result, select); - return result; - } - - template - TYPENAME digraph::path_vector - digraph::all_paths(TYPENAME digraph::iterator from, - TYPENAME digraph::iterator to, - TYPENAME digraph::arc_select_fn select) - throw(wrong_object,null_dereference,end_dereference) - { - return deconstify_paths(all_paths(from.constify(), to.constify(), select)); - } - - template - void digraph::reachable_nodes_r(TYPENAME digraph::const_iterator from, - TYPENAME digraph::const_iterator_set& visited, - TYPENAME digraph::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 arc = output(from,i); - if (!select || select(*this,arc)) - { - digraph_iterator candidate = arc_to(arc); - if (visited.insert(candidate).second) - reachable_nodes_r(candidate,visited,select); - } - } - } - - template - TYPENAME digraph::const_node_vector - digraph::reachable_nodes(TYPENAME digraph::const_iterator from, - TYPENAME digraph::arc_select_fn select) const - throw(wrong_object,null_dereference,end_dereference) - { - // seed the recursion, marking the starting node as already visited - std::set > 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 > result; - for (TYPENAME std::set >::iterator i = visited.begin(); i != visited.end(); i++) - if (*i != from) - result.push_back(*i); - return result; - } - - template - TYPENAME digraph::node_vector - digraph::reachable_nodes(TYPENAME digraph::iterator from, - TYPENAME digraph::arc_select_fn select) - throw(wrong_object,null_dereference,end_dereference) - { - return deconstify_nodes(reachable_nodes(from.constify(), select)); - } - - template - void digraph::reaching_nodes_r(TYPENAME digraph::const_iterator to, - TYPENAME digraph::const_iterator_set& visited, - TYPENAME digraph::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 arc = input(to,i); - if (!select || select(*this,arc)) - { - digraph_iterator candidate = arc_from(input(to,i)); - if (visited.insert(candidate).second) - reaching_nodes_r(candidate,visited,select); - } - } - } - - template - TYPENAME digraph::const_node_vector - digraph::reaching_nodes(TYPENAME digraph::const_iterator to, - TYPENAME digraph::arc_select_fn select) const - throw(wrong_object,null_dereference,end_dereference) - { - // seed the recursion, marking the starting node as already visited - std::set > 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 > result; - for (TYPENAME std::set >::iterator i = visited.begin(); i != visited.end(); i++) - if (*i != to) - result.push_back(*i); - return result; - } - - template - TYPENAME digraph::node_vector - digraph::reaching_nodes(TYPENAME digraph::iterator to, - TYPENAME digraph::arc_select_fn select) - throw(wrong_object,null_dereference,end_dereference) - { - return deconstify_nodes(reaching_nodes(to.constify(),select)); - } - - //////////////////////////////////////////////////////////////////////////////// - // Shortest Path Algorithms - - template - TYPENAME digraph::const_arc_vector - digraph::shortest_path(TYPENAME digraph::const_iterator from, - TYPENAME digraph::const_iterator to, - TYPENAME digraph::arc_select_fn select) const - throw(wrong_object,null_dereference,end_dereference) - { - std::vector > > paths = all_paths(from,to,select); - std::vector > shortest; - for (TYPENAME std::vector > >::iterator i = paths.begin(); i != paths.end(); i++) - if (shortest.empty() || i->size() < shortest.size()) - shortest = *i; - return shortest; - } - - template - TYPENAME digraph::arc_vector - digraph::shortest_path(TYPENAME digraph::iterator from, - TYPENAME digraph::iterator to, - TYPENAME digraph::arc_select_fn select) - throw(wrong_object,null_dereference,end_dereference) - { - return deconstify_arcs(shortest_path(from.constify(),to.constify(),select)); - } - - template - TYPENAME digraph::const_path_vector - digraph::shortest_paths(TYPENAME digraph::const_iterator from, - TYPENAME digraph::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 > > result; - // initialise the iteration by creating a queue and adding the start node - std::deque > 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_arc_iterator > known_map; - known_map known; - known.insert(std::make_pair(from,digraph_arc_iterator())); - // 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 current = nodes.front(); - nodes.pop_front(); - // now visit all the successors - for (unsigned i = 0; i < fanout(current); i++) - { - digraph_arc_iterator 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 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 digraph::path_vector - digraph::shortest_paths(TYPENAME digraph::iterator from, - TYPENAME digraph::arc_select_fn select) - throw(wrong_object,null_dereference,end_dereference) - { - return deconstify_paths(shortest_paths(from.constify(),select)); - } - - //////////////////////////////////////////////////////////////////////////////// - -} // end namespace stlplus +//////////////////////////////////////////////////////////////////////////////// + +// 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 +#include + +//////////////////////////////////////////////////////////////////////////////// +// Internals + +namespace stlplus +{ + + template + class digraph_node + { + public: + master_iterator, digraph_node > m_master; + NT m_data; + digraph_node* m_prev; + digraph_node* m_next; + std::vector*> m_inputs; + std::vector*> m_outputs; + public: + digraph_node(const digraph* owner, const NT& d = NT()) : + m_master(owner,this), m_data(d), m_prev(0), m_next(0) + { + } + ~digraph_node(void) + { + } + }; + + template + class digraph_arc + { + public: + master_iterator, digraph_arc > m_master; + AT m_data; + digraph_arc* m_prev; + digraph_arc* m_next; + digraph_node* m_from; + digraph_node* m_to; + digraph_arc(const digraph* owner, digraph_node* from = 0, digraph_node* 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 + digraph_iterator::digraph_iterator(void) + { + } + + // valid iterator + template + digraph_iterator::digraph_iterator(digraph_node* node) : + safe_iterator,digraph_node >(node->m_master) + { + } + + // end iterator + template + digraph_iterator::digraph_iterator(const digraph* owner) : + safe_iterator,digraph_node >(owner) + { + } + + // alias an iterator + template + digraph_iterator::digraph_iterator(const safe_iterator, digraph_node >& iterator) : + safe_iterator,digraph_node >(iterator) + { + } + + // destructor + template + digraph_iterator::~digraph_iterator(void) + { + } + + template + TYPENAME digraph_iterator::const_iterator digraph_iterator::constify (void) const + { + return digraph_iterator(*this); + } + + template + TYPENAME digraph_iterator::iterator digraph_iterator::deconstify (void) const + { + return digraph_iterator(*this); + } + + template + TYPENAME digraph_iterator::this_iterator& digraph_iterator::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 digraph_iterator::this_iterator digraph_iterator::operator ++ (int) + throw(null_dereference,end_dereference) + { + // post-increment is defined in terms of the pre-increment + digraph_iterator result(*this); + ++(*this); + return result; + } + + template + TYPENAME digraph_iterator::this_iterator& digraph_iterator::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 digraph_iterator::this_iterator digraph_iterator::operator -- (int) + throw(null_dereference,end_dereference) + { + // post-decrement is defined in terms of the pre-decrement + digraph_iterator result(*this); + --(*this); + return result; + } + + template + bool digraph_iterator::operator == (const TYPENAME digraph_iterator::this_iterator& r) const + { + return equal(r); + } + + template + bool digraph_iterator::operator != (const TYPENAME digraph_iterator::this_iterator& r) const + { + return !operator==(r); + } + + template + bool digraph_iterator::operator < (const TYPENAME digraph_iterator::this_iterator& r) const + { + return compare(r) < 0; + } + + template + TYPENAME digraph_iterator::reference digraph_iterator::operator*(void) const + throw(null_dereference,end_dereference) + { + this->assert_valid(); + return this->node()->m_data; + } + + template + TYPENAME digraph_iterator::pointer digraph_iterator::operator->(void) const + throw(null_dereference,end_dereference) + { + return &(operator*()); + } + + //////////////////////////////////////////////////////////////////////////////// + // Arc Iterator + + template + digraph_arc_iterator::digraph_arc_iterator(void) + { + } + + // valid iterator + template + digraph_arc_iterator::digraph_arc_iterator(digraph_arc* arc) : + safe_iterator,digraph_arc >(arc->m_master) + { + } + + // end iterator + template + digraph_arc_iterator::digraph_arc_iterator(const digraph* owner) : + safe_iterator,digraph_arc >(owner) + { + } + + // alias an iterator + template + digraph_arc_iterator::digraph_arc_iterator(const safe_iterator, digraph_arc >& iterator) : + safe_iterator,digraph_arc >(iterator) + { + } + + template + digraph_arc_iterator::~digraph_arc_iterator(void) + { + } + + template + TYPENAME digraph_arc_iterator::const_iterator digraph_arc_iterator::constify (void) const + { + return digraph_arc_iterator(*this); + } + + template + TYPENAME digraph_arc_iterator::iterator digraph_arc_iterator::deconstify (void) const + { + return digraph_arc_iterator(*this); + } + + template + TYPENAME digraph_arc_iterator::this_iterator& digraph_arc_iterator::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 digraph_arc_iterator::this_iterator digraph_arc_iterator::operator ++ (int) + throw(null_dereference,end_dereference) + { + // post-increment is defined in terms of the pre-increment + digraph_arc_iterator result(*this); + ++(*this); + return result; + } + + template + TYPENAME digraph_arc_iterator::this_iterator& digraph_arc_iterator::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 digraph_arc_iterator::this_iterator digraph_arc_iterator::operator -- (int) + throw(null_dereference,end_dereference) + { + // post-decrement is defined in terms of the pre-decrement + digraph_arc_iterator result(*this); + --(*this); + return result; + } + + template + bool digraph_arc_iterator::operator == (const TYPENAME digraph_arc_iterator::this_iterator& r) const + { + return equal(r); + } + + template + bool digraph_arc_iterator::operator != (const TYPENAME digraph_arc_iterator::this_iterator& r) const + { + return !operator==(r); + } + + template + bool digraph_arc_iterator::operator < (const TYPENAME digraph_arc_iterator::this_iterator& r) const + { + return compare(r) < 0; + } + + template + TYPENAME digraph_arc_iterator::reference digraph_arc_iterator::operator*(void) const + throw(null_dereference,end_dereference) + { + this->assert_valid(); + return this->node()->m_data; + } + + template + TYPENAME digraph_arc_iterator::pointer digraph_arc_iterator::operator->(void) const + throw(null_dereference,end_dereference) + { + return &(operator*()); + } + + //////////////////////////////////////////////////////////////////////////////// + // subtype utilities + + template + TYPENAME digraph::const_arc_vector digraph::constify_arcs(const TYPENAME digraph::arc_vector& arcs) const + throw(wrong_object,null_dereference,end_dereference) + { + std::vector > result; + for (unsigned i = 0; i < arcs.size(); i++) + { + arcs[i].assert_valid(this); + result.push_back(arcs[i].constify()); + } + return result; + } + + template + TYPENAME digraph::arc_vector digraph::deconstify_arcs(const TYPENAME digraph::const_arc_vector& arcs) const + throw(wrong_object,null_dereference,end_dereference) + { + std::vector > result; + for (unsigned i = 0; i < arcs.size(); i++) + { + arcs[i].assert_valid(this); + result.push_back(arcs[i].deconstify()); + } + return result; + } + + template + TYPENAME digraph::const_path_vector digraph::constify_paths(const TYPENAME digraph::path_vector& paths) const + throw(wrong_object,null_dereference,end_dereference) + { + std::vector > > result; + for (unsigned i = 0; i < paths.size(); i++) + result.push_back(constify_arcs(paths[i])); + return result; + } + + template + TYPENAME digraph::path_vector digraph::deconstify_paths(const TYPENAME digraph::const_path_vector& paths) const + throw(wrong_object,null_dereference,end_dereference) + { + std::vector > > result; + for (unsigned i = 0; i < paths.size(); i++) + result.push_back(deconstify_arcs(paths[i])); + return result; + } + + template + TYPENAME digraph::const_node_vector digraph::constify_nodes(const TYPENAME digraph::node_vector& nodes) const + throw(wrong_object,null_dereference,end_dereference) + { + std::vector > result; + for (unsigned i = 0; i < nodes.size(); i++) + { + nodes[i].assert_valid(this); + result.push_back(nodes[i].constify()); + } + return result; + } + + template + TYPENAME digraph::node_vector digraph::deconstify_nodes(const TYPENAME digraph::const_node_vector& nodes) const + throw(wrong_object,null_dereference,end_dereference) + { + std::vector > result; + for (unsigned i = 0; i < nodes.size(); i++) + { + nodes[i].assert_valid(this); + result.push_back(nodes[i].deconstify()); + } + return result; + } + + template + unsigned digraph::npos(void) + { + return(unsigned)-1; + } + + //////////////////////////////////////////////////////////////////////////////// + // Constructors etc. + + template + digraph::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 + digraph::~digraph(void) + { + clear(); + } + + template + digraph::digraph(const digraph& r) : + m_nodes_begin(0), m_nodes_end(0), m_arcs_begin(0), m_arcs_end(0) + { + *this = r; + } + + template + digraph& digraph::operator=(const digraph& 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 > xref; + for (digraph_iterator 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 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 + bool digraph::empty(void) const + { + return m_nodes_begin == 0; + } + + template + unsigned digraph::size(void) const + { + unsigned count = 0; + for (digraph_iterator i = begin(); i != end(); i++) + count++; + return count; + } + + template + TYPENAME digraph::iterator digraph::insert(const NT& node_data) + { + digraph_node* new_node = new digraph_node(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(new_node); + } + + template + TYPENAME digraph::iterator digraph::erase(TYPENAME digraph::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* next = iter.node()->m_next; + delete iter.node(); + // return the next node in the list + if (next) + return digraph_iterator(next); + else + return digraph_iterator(this); + } + + template + void digraph::clear(void) + { + // clearing the nodes also clears the arcs + for (digraph_iterator i = begin(); i != end(); ) + i = erase(i); + } + + template + TYPENAME digraph::const_iterator digraph::begin(void) const + { + if (m_nodes_begin) + return digraph_iterator(m_nodes_begin); + else + return digraph_iterator(this); + } + + template + TYPENAME digraph::iterator digraph::begin(void) + { + if (m_nodes_begin) + return digraph_iterator(m_nodes_begin); + else + return digraph_iterator(this); + } + + template + TYPENAME digraph::const_iterator digraph::end(void) const + { + return digraph_iterator(this); + } + + template + TYPENAME digraph::iterator digraph::end(void) + { + return digraph_iterator(this); + } + + template + unsigned digraph::fanin(TYPENAME digraph::const_iterator iter) const + throw(wrong_object,null_dereference,end_dereference) + { + iter.assert_valid(this); + return iter.node()->m_inputs.size(); + } + + template + unsigned digraph::fanin(TYPENAME digraph::iterator iter) + throw(wrong_object,null_dereference,end_dereference) + { + iter.assert_valid(this); + return iter.node()->m_inputs.size(); + } + + template + TYPENAME digraph::const_arc_iterator digraph::input(TYPENAME digraph::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(iter.node()->m_inputs[i]); + } + + template + TYPENAME digraph::arc_iterator digraph::input(TYPENAME digraph::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(iter.node()->m_inputs[i]); + } + + template + unsigned digraph::fanout(TYPENAME digraph::const_iterator iter) const + throw(wrong_object,null_dereference,end_dereference) + { + iter.assert_valid(this); + return iter.node()->m_outputs.size(); + } + + template + unsigned digraph::fanout(TYPENAME digraph::iterator iter) + throw(wrong_object,null_dereference,end_dereference) + { + iter.assert_valid(this); + return iter.node()->m_outputs.size(); + } + + template + TYPENAME digraph::const_arc_iterator digraph::output(TYPENAME digraph::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(iter.node()->m_outputs[i]); + } + + template + TYPENAME digraph::arc_iterator digraph::output(TYPENAME digraph::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(iter.node()->m_outputs[i]); + } + + template + TYPENAME digraph::const_arc_vector digraph::inputs(TYPENAME digraph::const_iterator node) const + throw(wrong_object,null_dereference,end_dereference) + { + node.assert_valid(this); + std::vector > result; + for (unsigned i = 0; i < fanin(node); i++) + result.push_back(input(node,i)); + return result; + } + + template + TYPENAME digraph::arc_vector digraph::inputs(TYPENAME digraph::iterator node) + throw(wrong_object,null_dereference,end_dereference) + { + node.assert_valid(this); + std::vector > result; + for (unsigned i = 0; i < fanin(node); i++) + result.push_back(input(node,i)); + return result; + } + + template + TYPENAME digraph::const_arc_vector digraph::outputs(TYPENAME digraph::const_iterator node) const + throw(wrong_object,null_dereference,end_dereference) + { + node.assert_valid(this); + std::vector > result; + for (unsigned i = 0; i < fanout(node); i++) + result.push_back(output(node,i)); + return result; + } + + template + TYPENAME digraph::arc_vector digraph::outputs(TYPENAME digraph::iterator node) + throw(wrong_object,null_dereference,end_dereference) + { + node.assert_valid(this); + std::vector > result; + for (unsigned i = 0; i < fanout(node); i++) + result.push_back(output(node,i)); + return result; + } + + template + unsigned digraph::output_offset(TYPENAME digraph::const_iterator from, + TYPENAME digraph::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::npos(); + } + + template + unsigned digraph::output_offset(TYPENAME digraph::iterator from, + TYPENAME digraph::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::npos(); + } + + template + unsigned digraph::input_offset(TYPENAME digraph::const_iterator to, + TYPENAME digraph::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::npos(); + } + + template + unsigned digraph::input_offset(TYPENAME digraph::iterator to, + TYPENAME digraph::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::npos(); + } + + //////////////////////////////////////////////////////////////////////////////// + // Basic Arc functions + + template + bool digraph::arc_empty(void) const + { + return m_arcs_end == 0; + } + + template + unsigned digraph::arc_size(void) const + { + unsigned count = 0; + for (digraph_arc_iterator i = arc_begin(); i != arc_end(); i++) + count++; + return count; + } + + template + TYPENAME digraph::arc_iterator digraph::arc_insert(TYPENAME digraph::iterator from, + TYPENAME digraph::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* new_arc = new digraph_arc(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(new_arc); + } + + template + TYPENAME digraph::arc_iterator digraph::arc_erase(TYPENAME digraph::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*>::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*>::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* next = iter.node()->m_next; + delete iter.node(); + if (next) + return digraph_arc_iterator(next); + else + return digraph_arc_iterator(this); + } + + template + void digraph::arc_clear(void) + { + for (digraph_arc_iterator a = arc_begin(); a != arc_end(); ) + a = arc_erase(a); + } + + template + TYPENAME digraph::const_arc_iterator digraph::arc_begin(void) const + { + if (m_arcs_begin) + return digraph_arc_iterator(m_arcs_begin); + else + return digraph_arc_iterator(this); + } + + template + TYPENAME digraph::arc_iterator digraph::arc_begin(void) + { + if (m_arcs_begin) + return digraph_arc_iterator(m_arcs_begin); + else + return digraph_arc_iterator(this); + } + + template + TYPENAME digraph::const_arc_iterator digraph::arc_end(void) const + { + return digraph_arc_iterator(this); + } + + template + TYPENAME digraph::arc_iterator digraph::arc_end(void) + { + return digraph_arc_iterator(this); + } + + template + TYPENAME digraph::const_iterator digraph::arc_from(TYPENAME digraph::const_arc_iterator iter) const + throw(wrong_object,null_dereference,end_dereference) + { + iter.assert_valid(this); + return digraph_iterator(iter.node()->m_from); + } + + template + TYPENAME digraph::iterator digraph::arc_from(TYPENAME digraph::arc_iterator iter) + throw(wrong_object,null_dereference,end_dereference) + { + iter.assert_valid(this); + return digraph_iterator(iter.node()->m_from); + } + + template + TYPENAME digraph::const_iterator digraph::arc_to(TYPENAME digraph::const_arc_iterator iter) const + throw(wrong_object,null_dereference,end_dereference) + { + iter.assert_valid(this); + return digraph_iterator(iter.node()->m_to); + } + + template + TYPENAME digraph::iterator digraph::arc_to(TYPENAME digraph::arc_iterator iter) + throw(wrong_object,null_dereference,end_dereference) + { + iter.assert_valid(this); + return digraph_iterator(iter.node()->m_to); + } + + template + void digraph::arc_move(TYPENAME digraph::arc_iterator arc, + TYPENAME digraph::iterator from, + TYPENAME digraph::iterator to) + throw(wrong_object,null_dereference,end_dereference) + { + arc_move_to(arc,to); + arc_move_from(arc,from); + } + + template + void digraph::arc_move_from(TYPENAME digraph::arc_iterator arc, + TYPENAME digraph::iterator from) + throw(wrong_object,null_dereference,end_dereference) + { + arc.assert_valid(this); + from.assert_valid(this); + for (TYPENAME std::vector*>::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 + void digraph::arc_move_to(TYPENAME digraph::arc_iterator arc, + TYPENAME digraph::iterator to) + throw(wrong_object,null_dereference,end_dereference) + { + arc.assert_valid(this); + to.assert_valid(this); + for (TYPENAME std::vector*>::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 + void digraph::arc_flip(TYPENAME digraph::arc_iterator arc) + throw(wrong_object,null_dereference,end_dereference) + { + arc_move(arc,arc_to(arc),arc_from(arc)); + } + + //////////////////////////////////////////////////////////////////////////////// + // Adjacency Algorithms + + template + bool digraph::adjacent(TYPENAME digraph::const_iterator from, + TYPENAME digraph::const_iterator to) const + throw(wrong_object,null_dereference,end_dereference) + { + return adjacent_arc(from,to) != arc_end(); + } + + template + bool digraph::adjacent(TYPENAME digraph::iterator from, + TYPENAME digraph::iterator to) + throw(wrong_object,null_dereference,end_dereference) + { + return adjacent_arc(from,to) != arc_end(); + } + + template + TYPENAME digraph::const_arc_iterator digraph::adjacent_arc(TYPENAME digraph::const_iterator from, + TYPENAME digraph::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 digraph::arc_iterator digraph::adjacent_arc(TYPENAME digraph::iterator from, + TYPENAME digraph::iterator to) + throw(wrong_object,null_dereference,end_dereference) + { + return adjacent_arc(from.constify(), to.constify()).deconstify(); + } + + template + TYPENAME digraph::const_arc_vector digraph::adjacent_arcs(TYPENAME digraph::const_iterator from, + TYPENAME digraph::const_iterator to) const + throw(wrong_object,null_dereference,end_dereference) + { + from.assert_valid(this); + to.assert_valid(this); + std::vector > 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 digraph::arc_vector digraph::adjacent_arcs(TYPENAME digraph::iterator from, + TYPENAME digraph::iterator to) + throw(wrong_object,null_dereference,end_dereference) + { + return deconstify_arcs(adjacent_arcs(from.constify(), to.constify())); + } + + template + TYPENAME digraph::const_node_vector digraph::input_adjacencies(TYPENAME digraph::const_iterator to) const + throw(wrong_object,null_dereference,end_dereference) + { + std::vector > result; + for (unsigned arc = 0; arc < fanin(to); arc++) + { + digraph_iterator from = arc_from(input(to, arc)); + if (std::find(result.begin(), result.end(), from) == result.end()) + result.push_back(from); + } + return result; + } + + template + TYPENAME digraph::node_vector digraph::input_adjacencies(TYPENAME digraph::iterator to) + throw(wrong_object,null_dereference,end_dereference) + { + return deconstify_nodes(input_adjacencies(to.constify())); + } + + template + TYPENAME digraph::const_node_vector digraph::output_adjacencies(TYPENAME digraph::const_iterator from) const + throw(wrong_object,null_dereference,end_dereference) + { + std::vector > result; + for (unsigned arc = 0; arc < fanout(from); arc++) + { + digraph_iterator to = arc_to(output(from, arc)); + if (find(result.begin(), result.end(), to) == result.end()) + result.push_back(to); + } + return result; + } + + template + TYPENAME digraph::node_vector digraph::output_adjacencies(TYPENAME digraph::iterator from) + throw(wrong_object,null_dereference,end_dereference) + { + return deconstify_nodes(output_adjacencies(from.constify())); + } + + //////////////////////////////////////////////////////////////////////////////// + // Topographical Sort Algorithms + + template + std::pair::const_node_vector, TYPENAME digraph::const_arc_vector> + digraph::sort(TYPENAME digraph::arc_select_fn select) const + { + std::vector > result; + std::vector > errors; + // build a map containing the number of fanins to each node that must be visited before this one + std::map,unsigned> fanin_map; + for (digraph_iterator 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 input_arc = input(n,f); + digraph_iterator 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 current = result[i]; + for (unsigned f = 0; f < fanout(current); f++) + { + // only consider successors connected by selected arcs + digraph_arc_iterator output_arc = output(current, f); + digraph_iterator 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 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 input_arc = input(stuck_node, f); + if (!select || select(*this,input_arc)) + { + digraph_iterator 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 + std::pair::node_vector, TYPENAME digraph::arc_vector> + digraph::sort(TYPENAME digraph::arc_select_fn select) + { + std::pair >, + std::vector > > const_result = + const_cast*>(this)->sort(select); + + std::pair >, + std::vector > > result = + std::make_pair(deconstify_nodes(const_result.first),deconstify_arcs(const_result.second)); + return result; + } + + template + TYPENAME digraph::const_node_vector digraph::dag_sort(TYPENAME digraph::arc_select_fn select) const + { + std::pair >, + std::vector > > result = sort(select); + if (result.second.empty()) return result.first; + return std::vector >(); + } + + template + TYPENAME digraph::node_vector digraph::dag_sort(TYPENAME digraph::arc_select_fn select) + { + return deconstify_nodes(const_cast*>(this)->dag_sort(select)); + } + //////////////////////////////////////////////////////////////////////////////// + // Path Algorithms + + template + bool digraph::path_exists_r(TYPENAME digraph::const_iterator from, + TYPENAME digraph::const_iterator to, + TYPENAME digraph::const_iterator_set& visited, + TYPENAME digraph::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 arc = output(from,i); + if (!select || select(*this, arc)) + { + digraph_iterator 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 + bool digraph::path_exists(TYPENAME digraph::const_iterator from, + TYPENAME digraph::const_iterator to, + TYPENAME digraph::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 > visited; + visited.insert(from); + return path_exists_r(from, to, visited, select); + } + + template + bool digraph::path_exists(TYPENAME digraph::iterator from, + TYPENAME digraph::iterator to, + TYPENAME digraph::arc_select_fn select) + throw(wrong_object,null_dereference,end_dereference) + { + return path_exists(from.constify(), to.constify(), select); + } + + template + void digraph::all_paths_r(TYPENAME digraph::const_iterator from, + TYPENAME digraph::const_iterator to, + TYPENAME digraph::const_arc_vector& so_far, + TYPENAME digraph::const_path_vector& result, + TYPENAME digraph::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 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 digraph::const_path_vector + digraph::all_paths(TYPENAME digraph::const_iterator from, + TYPENAME digraph::const_iterator to, + TYPENAME digraph::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 > > result; + std::vector > so_far; + all_paths_r(from, to, so_far, result, select); + return result; + } + + template + TYPENAME digraph::path_vector + digraph::all_paths(TYPENAME digraph::iterator from, + TYPENAME digraph::iterator to, + TYPENAME digraph::arc_select_fn select) + throw(wrong_object,null_dereference,end_dereference) + { + return deconstify_paths(all_paths(from.constify(), to.constify(), select)); + } + + template + void digraph::reachable_nodes_r(TYPENAME digraph::const_iterator from, + TYPENAME digraph::const_iterator_set& visited, + TYPENAME digraph::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 arc = output(from,i); + if (!select || select(*this,arc)) + { + digraph_iterator candidate = arc_to(arc); + if (visited.insert(candidate).second) + reachable_nodes_r(candidate,visited,select); + } + } + } + + template + TYPENAME digraph::const_node_vector + digraph::reachable_nodes(TYPENAME digraph::const_iterator from, + TYPENAME digraph::arc_select_fn select) const + throw(wrong_object,null_dereference,end_dereference) + { + // seed the recursion, marking the starting node as already visited + std::set > 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 > result; + for (TYPENAME std::set >::iterator i = visited.begin(); i != visited.end(); i++) + if (*i != from) + result.push_back(*i); + return result; + } + + template + TYPENAME digraph::node_vector + digraph::reachable_nodes(TYPENAME digraph::iterator from, + TYPENAME digraph::arc_select_fn select) + throw(wrong_object,null_dereference,end_dereference) + { + return deconstify_nodes(reachable_nodes(from.constify(), select)); + } + + template + void digraph::reaching_nodes_r(TYPENAME digraph::const_iterator to, + TYPENAME digraph::const_iterator_set& visited, + TYPENAME digraph::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 arc = input(to,i); + if (!select || select(*this,arc)) + { + digraph_iterator candidate = arc_from(input(to,i)); + if (visited.insert(candidate).second) + reaching_nodes_r(candidate,visited,select); + } + } + } + + template + TYPENAME digraph::const_node_vector + digraph::reaching_nodes(TYPENAME digraph::const_iterator to, + TYPENAME digraph::arc_select_fn select) const + throw(wrong_object,null_dereference,end_dereference) + { + // seed the recursion, marking the starting node as already visited + std::set > 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 > result; + for (TYPENAME std::set >::iterator i = visited.begin(); i != visited.end(); i++) + if (*i != to) + result.push_back(*i); + return result; + } + + template + TYPENAME digraph::node_vector + digraph::reaching_nodes(TYPENAME digraph::iterator to, + TYPENAME digraph::arc_select_fn select) + throw(wrong_object,null_dereference,end_dereference) + { + return deconstify_nodes(reaching_nodes(to.constify(),select)); + } + + //////////////////////////////////////////////////////////////////////////////// + // Shortest Path Algorithms + + template + TYPENAME digraph::const_arc_vector + digraph::shortest_path(TYPENAME digraph::const_iterator from, + TYPENAME digraph::const_iterator to, + TYPENAME digraph::arc_select_fn select) const + throw(wrong_object,null_dereference,end_dereference) + { + std::vector > > paths = all_paths(from,to,select); + std::vector > shortest; + for (TYPENAME std::vector > >::iterator i = paths.begin(); i != paths.end(); i++) + if (shortest.empty() || i->size() < shortest.size()) + shortest = *i; + return shortest; + } + + template + TYPENAME digraph::arc_vector + digraph::shortest_path(TYPENAME digraph::iterator from, + TYPENAME digraph::iterator to, + TYPENAME digraph::arc_select_fn select) + throw(wrong_object,null_dereference,end_dereference) + { + return deconstify_arcs(shortest_path(from.constify(),to.constify(),select)); + } + + template + TYPENAME digraph::const_path_vector + digraph::shortest_paths(TYPENAME digraph::const_iterator from, + TYPENAME digraph::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 > > result; + // initialise the iteration by creating a queue and adding the start node + std::deque > 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_arc_iterator > known_map; + known_map known; + known.insert(std::make_pair(from,digraph_arc_iterator())); + // 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 current = nodes.front(); + nodes.pop_front(); + // now visit all the successors + for (unsigned i = 0; i < fanout(current); i++) + { + digraph_arc_iterator 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 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 digraph::path_vector + digraph::shortest_paths(TYPENAME digraph::iterator from, + TYPENAME digraph::arc_select_fn select) + throw(wrong_object,null_dereference,end_dereference) + { + return deconstify_paths(shortest_paths(from.constify(),select)); + } + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus diff --git a/src/stlplus/containers/exceptions.hpp b/src/stlplus/containers/exceptions.hpp index 3549ff4..1911013 100644 --- a/src/stlplus/containers/exceptions.hpp +++ b/src/stlplus/containers/exceptions.hpp @@ -1,71 +1,71 @@ -#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 -#include - -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 +#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 +#include + +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 diff --git a/src/stlplus/containers/foursome.hpp b/src/stlplus/containers/foursome.hpp index 36437f1..46dc46d 100644 --- a/src/stlplus/containers/foursome.hpp +++ b/src/stlplus/containers/foursome.hpp @@ -1,59 +1,59 @@ -#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 - 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& t2); - }; - - //////////////////////////////////////////////////////////////////////////////// - // creation - - template - foursome make_foursome(const T1& first, const T2& second, const T3& third, const T4& fourth); - - //////////////////////////////////////////////////////////////////////////////// - // comparison - - template - bool operator == (const foursome& left, const foursome& right); - - //////////////////////////////////////////////////////////////////////////////// - -} // end namespace stlplus - -#include "foursome.tpp" -#endif +#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 + 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& t2); + }; + + //////////////////////////////////////////////////////////////////////////////// + // creation + + template + foursome make_foursome(const T1& first, const T2& second, const T3& third, const T4& fourth); + + //////////////////////////////////////////////////////////////////////////////// + // comparison + + template + bool operator == (const foursome& left, const foursome& right); + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus + +#include "foursome.tpp" +#endif diff --git a/src/stlplus/containers/foursome.tpp b/src/stlplus/containers/foursome.tpp index f1dd9b3..b2bfe08 100644 --- a/src/stlplus/containers/foursome.tpp +++ b/src/stlplus/containers/foursome.tpp @@ -1,59 +1,59 @@ -//////////////////////////////////////////////////////////////////////////////// - -// 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 - foursome::foursome(void) : - first(), second(), third(), fourth() - { - } - - template - foursome::foursome(const T1& p1, const T2& p2, const T3& p3, const T4& p4) : - first(p1), second(p2), third(p3), fourth(p4) - { - } - - template - foursome::foursome(const foursome& t2) : - first(t2.first), second(t2.second), third(t2.third), fourth(t2.fourth) - { - } - - //////////////////////////////////////////////////////////////////////////////// - // creation - - template - foursome make_foursome(const T1& first, const T2& second, const T3& third, const T4& fourth) - { - return foursome(first,second,third,fourth); - } - - //////////////////////////////////////////////////////////////////////////////// - // comparison - - template - bool operator == (const foursome& left, const foursome& 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 +//////////////////////////////////////////////////////////////////////////////// + +// 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 + foursome::foursome(void) : + first(), second(), third(), fourth() + { + } + + template + foursome::foursome(const T1& p1, const T2& p2, const T3& p3, const T4& p4) : + first(p1), second(p2), third(p3), fourth(p4) + { + } + + template + foursome::foursome(const foursome& t2) : + first(t2.first), second(t2.second), third(t2.third), fourth(t2.fourth) + { + } + + //////////////////////////////////////////////////////////////////////////////// + // creation + + template + foursome make_foursome(const T1& first, const T2& second, const T3& third, const T4& fourth) + { + return foursome(first,second,third,fourth); + } + + //////////////////////////////////////////////////////////////////////////////// + // comparison + + template + bool operator == (const foursome& left, const foursome& 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 diff --git a/src/stlplus/containers/hash.hpp b/src/stlplus/containers/hash.hpp index 1962481..13e9541 100644 --- a/src/stlplus/containers/hash.hpp +++ b/src/stlplus/containers/hash.hpp @@ -1,205 +1,205 @@ -#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 -#include - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - // internals - - template class hash; - template class hash_element; - - //////////////////////////////////////////////////////////////////////////////// - // iterator class - - template - class hash_iterator : public safe_iterator,hash_element > - { - public: - friend class hash; - - // 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 > iterator; - typedef hash_iterator > const_iterator; - typedef hash_iterator 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; - - // 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* element); - // constructor used to create an end iterator - explicit hash_iterator(const hash* owner); - // used to create an alias of an iterator - explicit hash_iterator(const safe_iterator, hash_element >& 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 > - class hash - { - public: - typedef unsigned size_type; - typedef K key_type; - typedef T data_type; - typedef T mapped_type; - typedef std::pair value_type; - typedef hash_iterator iterator; - typedef hash_iterator 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 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 - // 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; - friend class hash_iterator >; - friend class hash_iterator >; - - unsigned m_rehash; - unsigned m_bins; - unsigned m_size; - hash_element** m_values; - }; - - //////////////////////////////////////////////////////////////////////////////// - -} // end namespace stlplus - -#include "hash.tpp" -#endif +#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 +#include + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // internals + + template class hash; + template class hash_element; + + //////////////////////////////////////////////////////////////////////////////// + // iterator class + + template + class hash_iterator : public safe_iterator,hash_element > + { + public: + friend class hash; + + // 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 > iterator; + typedef hash_iterator > const_iterator; + typedef hash_iterator 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; + + // 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* element); + // constructor used to create an end iterator + explicit hash_iterator(const hash* owner); + // used to create an alias of an iterator + explicit hash_iterator(const safe_iterator, hash_element >& 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 > + class hash + { + public: + typedef unsigned size_type; + typedef K key_type; + typedef T data_type; + typedef T mapped_type; + typedef std::pair value_type; + typedef hash_iterator iterator; + typedef hash_iterator 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 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 + // 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; + friend class hash_iterator >; + friend class hash_iterator >; + + unsigned m_rehash; + unsigned m_bins; + unsigned m_size; + hash_element** m_values; + }; + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus + +#include "hash.tpp" +#endif diff --git a/src/stlplus/containers/hash.tpp b/src/stlplus/containers/hash.tpp index 9a1e4e9..da2e39d 100644 --- a/src/stlplus/containers/hash.tpp +++ b/src/stlplus/containers/hash.tpp @@ -1,658 +1,658 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// -#include - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - // the element stored in the hash - - template - class hash_element - { - public: - master_iterator, hash_element > m_master; - std::pair m_value; - hash_element* m_next; - unsigned m_hash; - - hash_element(const hash* 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* owner, const std::pair& 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* 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 - hash_iterator::hash_iterator(void) - { - } - - // non-null constructor used from within the hash to construct a valid iterator - template - hash_iterator::hash_iterator(hash_element* element) : - safe_iterator,hash_element >(element->m_master) - { - } - - // constructor used to create an end iterator - template - hash_iterator::hash_iterator(const hash* owner) : - safe_iterator,hash_element >(owner) - { - } - - template - hash_iterator::hash_iterator(const safe_iterator, hash_element >& iterator) : - safe_iterator,hash_element >(iterator) - { - } - - // destructor - - template - hash_iterator::~hash_iterator(void) - { - } - - // mode conversions - - template - TYPENAME hash_iterator::const_iterator hash_iterator::constify(void) const - { - return hash_iterator >(*this); - } - - template - TYPENAME hash_iterator::iterator hash_iterator::deconstify(void) const - { - return hash_iterator >(*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 hash_iterator::this_iterator& hash_iterator::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* 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 hash_iterator::this_iterator hash_iterator::operator ++ (int) - throw(null_dereference,end_dereference) - { - hash_iterator 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 - bool hash_iterator::operator == (const hash_iterator& r) const - { - return equal(r); - } - - template - bool hash_iterator::operator != (const hash_iterator& r) const - { - return !operator==(r); - } - - template - bool hash_iterator::operator < (const hash_iterator& r) const - { - return compare(r) < 0; - } - - // iterator dereferencing is only legal on a non-null iterator - template - V& hash_iterator::operator*(void) const - throw(null_dereference,end_dereference) - { - this->assert_valid(); - return this->node()->m_value; - } - - template - V* hash_iterator::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 - hash::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*[m_bins]; - for (unsigned i = 0; i < m_bins; i++) - m_values[i] = 0; - } - - template - hash::~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 - hash::hash(const hash& right) : - m_rehash(right.m_rehash), m_bins(right.m_bins), m_size(0), m_values(0) - { - m_values = new hash_element*[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 - hash& hash::operator = (const hash& 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 > i = r.begin(); i != r.end(); ++i) - insert(i->first, i->second); - return *this; - } - - // number of values in the hash - template - bool hash::empty(void) const - { - return m_size == 0; - } - - template - unsigned hash::size(void) const - { - return m_size; - } - - // equality - template - bool hash::operator == (const hash& 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 > i = begin(); i != end(); i++) - { - hash_iterator > 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 - void hash::auto_rehash(void) - { - m_rehash = m_bins; - } - - template - void hash::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 - void hash::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** old_values = m_values; - unsigned old_bins = m_bins; - // create a replacement structure - m_values = new hash_element*[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* 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 - float hash::loading(void) const - { - return (float)m_size / (float)m_bins; - } - - // remove all elements from the table - - template - void hash::erase(void) - { - // unhook the list elements and destroy them - for (unsigned i = 0; i < m_bins; i++) - { - hash_element* current = m_values[i]; - while(current) - { - hash_element* 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 - bool hash::present(const K& key) const - { - return find(key) != end(); - } - - template - TYPENAME hash::size_type hash::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 hash::iterator hash::insert(const K& key, const T& data) - { - return insert(std::pair(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 - std::pair::iterator, bool> hash::insert(const std::pair& 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* previous = 0; - for (hash_element* 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* new_item = new hash_element(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 >(new_item), inserted); - } - - // insert a key with an empty data field ready to be filled in later - - template - TYPENAME hash::iterator hash::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 - unsigned hash::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* previous = 0; - for (hash_element* 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 hash::iterator hash::erase(TYPENAME hash::iterator it) - { - // work out what the next iterator is in order to return it later - TYPENAME hash::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* previous = 0; - for (hash_element* 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 - void hash::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 hash::const_iterator hash::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* 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 >(current); - } - return end(); - } - - template - TYPENAME hash::iterator hash::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* 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 >(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 - const T& hash::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 > found = find(key); - if (found == end()) - throw std::out_of_range("key not found in stlplus::hash::operator[]"); - return found->second; - } - - template - T& hash::operator[] (const K& key) - { - // this non-const version can change the hash, so creates a new element if the key is missing - hash_iterator > found = find(key); - if (found == end()) - found = insert(key); - return found->second; - } - - // iterators - - template - TYPENAME hash::const_iterator hash::begin(void) const - { - // find the first element - for (unsigned bin = 0; bin < m_bins; bin++) - if (m_values[bin]) - return hash_iterator >(m_values[bin]); - // if the hash is empty, return the end iterator - return end(); - } - - template - TYPENAME hash::iterator hash::begin(void) - { - // find the first element - for (unsigned bin = 0; bin < m_bins; bin++) - if (m_values[bin]) - return hash_iterator >(m_values[bin]); - // if the hash is empty, return the end iterator - return end(); - } - - template - TYPENAME hash::const_iterator hash::end(void) const - { - return hash_iterator >(this); - } - - template - TYPENAME hash::iterator hash::end(void) - { - return hash_iterator >(this); - } - - template - void hash::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* 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* 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 - +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // the element stored in the hash + + template + class hash_element + { + public: + master_iterator, hash_element > m_master; + std::pair m_value; + hash_element* m_next; + unsigned m_hash; + + hash_element(const hash* 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* owner, const std::pair& 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* 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 + hash_iterator::hash_iterator(void) + { + } + + // non-null constructor used from within the hash to construct a valid iterator + template + hash_iterator::hash_iterator(hash_element* element) : + safe_iterator,hash_element >(element->m_master) + { + } + + // constructor used to create an end iterator + template + hash_iterator::hash_iterator(const hash* owner) : + safe_iterator,hash_element >(owner) + { + } + + template + hash_iterator::hash_iterator(const safe_iterator, hash_element >& iterator) : + safe_iterator,hash_element >(iterator) + { + } + + // destructor + + template + hash_iterator::~hash_iterator(void) + { + } + + // mode conversions + + template + TYPENAME hash_iterator::const_iterator hash_iterator::constify(void) const + { + return hash_iterator >(*this); + } + + template + TYPENAME hash_iterator::iterator hash_iterator::deconstify(void) const + { + return hash_iterator >(*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 hash_iterator::this_iterator& hash_iterator::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* 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 hash_iterator::this_iterator hash_iterator::operator ++ (int) + throw(null_dereference,end_dereference) + { + hash_iterator 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 + bool hash_iterator::operator == (const hash_iterator& r) const + { + return equal(r); + } + + template + bool hash_iterator::operator != (const hash_iterator& r) const + { + return !operator==(r); + } + + template + bool hash_iterator::operator < (const hash_iterator& r) const + { + return compare(r) < 0; + } + + // iterator dereferencing is only legal on a non-null iterator + template + V& hash_iterator::operator*(void) const + throw(null_dereference,end_dereference) + { + this->assert_valid(); + return this->node()->m_value; + } + + template + V* hash_iterator::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 + hash::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*[m_bins]; + for (unsigned i = 0; i < m_bins; i++) + m_values[i] = 0; + } + + template + hash::~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 + hash::hash(const hash& right) : + m_rehash(right.m_rehash), m_bins(right.m_bins), m_size(0), m_values(0) + { + m_values = new hash_element*[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 + hash& hash::operator = (const hash& 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 > i = r.begin(); i != r.end(); ++i) + insert(i->first, i->second); + return *this; + } + + // number of values in the hash + template + bool hash::empty(void) const + { + return m_size == 0; + } + + template + unsigned hash::size(void) const + { + return m_size; + } + + // equality + template + bool hash::operator == (const hash& 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 > i = begin(); i != end(); i++) + { + hash_iterator > 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 + void hash::auto_rehash(void) + { + m_rehash = m_bins; + } + + template + void hash::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 + void hash::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** old_values = m_values; + unsigned old_bins = m_bins; + // create a replacement structure + m_values = new hash_element*[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* 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 + float hash::loading(void) const + { + return (float)m_size / (float)m_bins; + } + + // remove all elements from the table + + template + void hash::erase(void) + { + // unhook the list elements and destroy them + for (unsigned i = 0; i < m_bins; i++) + { + hash_element* current = m_values[i]; + while(current) + { + hash_element* 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 + bool hash::present(const K& key) const + { + return find(key) != end(); + } + + template + TYPENAME hash::size_type hash::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 hash::iterator hash::insert(const K& key, const T& data) + { + return insert(std::pair(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 + std::pair::iterator, bool> hash::insert(const std::pair& 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* previous = 0; + for (hash_element* 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* new_item = new hash_element(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 >(new_item), inserted); + } + + // insert a key with an empty data field ready to be filled in later + + template + TYPENAME hash::iterator hash::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 + unsigned hash::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* previous = 0; + for (hash_element* 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 hash::iterator hash::erase(TYPENAME hash::iterator it) + { + // work out what the next iterator is in order to return it later + TYPENAME hash::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* previous = 0; + for (hash_element* 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 + void hash::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 hash::const_iterator hash::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* 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 >(current); + } + return end(); + } + + template + TYPENAME hash::iterator hash::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* 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 >(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 + const T& hash::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 > found = find(key); + if (found == end()) + throw std::out_of_range("key not found in stlplus::hash::operator[]"); + return found->second; + } + + template + T& hash::operator[] (const K& key) + { + // this non-const version can change the hash, so creates a new element if the key is missing + hash_iterator > found = find(key); + if (found == end()) + found = insert(key); + return found->second; + } + + // iterators + + template + TYPENAME hash::const_iterator hash::begin(void) const + { + // find the first element + for (unsigned bin = 0; bin < m_bins; bin++) + if (m_values[bin]) + return hash_iterator >(m_values[bin]); + // if the hash is empty, return the end iterator + return end(); + } + + template + TYPENAME hash::iterator hash::begin(void) + { + // find the first element + for (unsigned bin = 0; bin < m_bins; bin++) + if (m_values[bin]) + return hash_iterator >(m_values[bin]); + // if the hash is empty, return the end iterator + return end(); + } + + template + TYPENAME hash::const_iterator hash::end(void) const + { + return hash_iterator >(this); + } + + template + TYPENAME hash::iterator hash::end(void) + { + return hash_iterator >(this); + } + + template + void hash::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* 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* 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 + diff --git a/src/stlplus/containers/matrix.hpp b/src/stlplus/containers/matrix.hpp index b76a998..9a0fa69 100644 --- a/src/stlplus/containers/matrix.hpp +++ b/src/stlplus/containers/matrix.hpp @@ -1,63 +1,63 @@ -#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 - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - - template 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 +#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 + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + + template 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 diff --git a/src/stlplus/containers/matrix.tpp b/src/stlplus/containers/matrix.tpp index 65ec779..1e2f785 100644 --- a/src/stlplus/containers/matrix.tpp +++ b/src/stlplus/containers/matrix.tpp @@ -1,215 +1,215 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - - template - matrix::matrix(unsigned rows, unsigned cols, const T& fill) throw() - { - m_rows = 0; - m_cols = 0; - m_data = 0; - resize(rows,cols,fill); - } - - template - matrix::~matrix(void) throw() - { - for (unsigned row = 0; row < m_rows; row++) - delete[] m_data[row]; - delete[] m_data; - } - - template - matrix::matrix(const matrix& r) throw() - { - m_rows = 0; - m_cols = 0; - m_data = 0; - *this = r; - } - - template - matrix& matrix::operator =(const matrix& 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 - void matrix::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 - unsigned matrix::rows(void) const throw() - { - return m_rows; - } - - template - unsigned matrix::columns(void) const throw() - { - return m_cols; - } - - template - void matrix::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 - void matrix::erase(unsigned row, unsigned col, const T& fill) throw(std::out_of_range) - { - insert(row,col,fill); - } - - template - void matrix::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 - const T& matrix::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 - T& matrix::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 - const T& matrix::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 - T& matrix::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 - void matrix::fill(const T& item) throw() - { - erase(item); - } - - template - void matrix::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 - void matrix::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 - void matrix::fill_leading_diagonal(const T& item) throw() - { - for (unsigned i = 0; i < m_cols && i < m_rows; i++) - insert(i, i, item); - } - - template - void matrix::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 - void matrix::make_identity(const T& one, const T& zero) throw() - { - fill(zero); - fill_leading_diagonal(one); - } - - template - void matrix::transpose(void) throw() - { - // no gain in manipulating this, since building a new matrix is no less efficient - matrix 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 - +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + + template + matrix::matrix(unsigned rows, unsigned cols, const T& fill) throw() + { + m_rows = 0; + m_cols = 0; + m_data = 0; + resize(rows,cols,fill); + } + + template + matrix::~matrix(void) throw() + { + for (unsigned row = 0; row < m_rows; row++) + delete[] m_data[row]; + delete[] m_data; + } + + template + matrix::matrix(const matrix& r) throw() + { + m_rows = 0; + m_cols = 0; + m_data = 0; + *this = r; + } + + template + matrix& matrix::operator =(const matrix& 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 + void matrix::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 + unsigned matrix::rows(void) const throw() + { + return m_rows; + } + + template + unsigned matrix::columns(void) const throw() + { + return m_cols; + } + + template + void matrix::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 + void matrix::erase(unsigned row, unsigned col, const T& fill) throw(std::out_of_range) + { + insert(row,col,fill); + } + + template + void matrix::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 + const T& matrix::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 + T& matrix::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 + const T& matrix::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 + T& matrix::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 + void matrix::fill(const T& item) throw() + { + erase(item); + } + + template + void matrix::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 + void matrix::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 + void matrix::fill_leading_diagonal(const T& item) throw() + { + for (unsigned i = 0; i < m_cols && i < m_rows; i++) + insert(i, i, item); + } + + template + void matrix::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 + void matrix::make_identity(const T& one, const T& zero) throw() + { + fill(zero); + fill_leading_diagonal(one); + } + + template + void matrix::transpose(void) throw() + { + // no gain in manipulating this, since building a new matrix is no less efficient + matrix 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 + diff --git a/src/stlplus/containers/ntree.hpp b/src/stlplus/containers/ntree.hpp index 33817e2..1dd41c0 100644 --- a/src/stlplus/containers/ntree.hpp +++ b/src/stlplus/containers/ntree.hpp @@ -1,364 +1,364 @@ -#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 class ntree_node; - template class ntree; - template class ntree_iterator; - template class ntree_prefix_iterator; - template 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 - class ntree_iterator : public safe_iterator,ntree_node > - { - public: - // local type definitions - // an iterator points to an object whilst a const_iterator points to a const object - typedef ntree_iterator iterator; - typedef ntree_iterator const_iterator; - typedef ntree_iterator 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; - friend class ntree_prefix_iterator; - friend class ntree_postfix_iterator; - - 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* node); - // constructor used by ntree to create an end iterator - explicit ntree_iterator(const ntree* owner); - // used to create an alias of an iterator - explicit ntree_iterator(const safe_iterator, ntree_node >& 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 - class ntree_prefix_iterator - { - public: - typedef ntree_prefix_iterator iterator; - typedef ntree_prefix_iterator const_iterator; - typedef ntree_prefix_iterator this_iterator; - typedef ntree_iterator 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 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; - friend class ntree_iterator; - - private: - ntree_iterator m_iterator; - - explicit ntree_prefix_iterator(const ntree_iterator& i); - const ntree_iterator& get_iterator(void) const; - ntree_iterator& get_iterator(void); - }; - - //////////////////////////////////////////////////////////////////////////////// - - template - class ntree_postfix_iterator - { - public: - typedef ntree_postfix_iterator iterator; - typedef ntree_postfix_iterator const_iterator; - typedef ntree_postfix_iterator this_iterator; - typedef ntree_iterator 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 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; - friend class ntree_iterator; - - private: - ntree_iterator m_iterator; - - explicit ntree_postfix_iterator(const ntree_iterator& i); - const ntree_iterator& get_iterator(void) const; - ntree_iterator& get_iterator(void); - }; - - //////////////////////////////////////////////////////////////////////////////// - // The Ntree class - //////////////////////////////////////////////////////////////////////////////// - - template - class ntree - { - public: - // STL-like typedefs for the types and iterators - typedef T value_type; - - typedef ntree_iterator iterator; - typedef ntree_iterator const_iterator; - - typedef ntree_prefix_iterator prefix_iterator; - typedef ntree_prefix_iterator const_prefix_iterator; - - typedef ntree_postfix_iterator postfix_iterator; - typedef ntree_postfix_iterator const_postfix_iterator; - - ////////////////////////////////////////////////////////////////////////////// - // Constructors, destructors and copies - - ntree(void); - ~ntree(void); - - // copy constructor and assignment both copy the tree - ntree(const ntree&); - ntree& operator=(const ntree&); - - ////////////////////////////////////////////////////////////////////////////// - // 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&) - throw(wrong_object,null_dereference,end_dereference,std::out_of_range); - iterator append(const iterator& node, const ntree&) - 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 subtree(void); - ntree subtree(const iterator& node) - throw(wrong_object,null_dereference,end_dereference); - ntree subtree(const iterator& node, unsigned child) - throw(wrong_object,null_dereference,end_dereference,std::out_of_range); - - ntree cut(void); - ntree cut(const iterator& node) - throw(wrong_object,null_dereference,end_dereference); - ntree cut(const iterator& node, unsigned child) - throw(wrong_object,null_dereference,end_dereference,std::out_of_range); - - ////////////////////////////////////////////////////////////////////////////// - - private: - ntree_node* m_root; - }; - - //////////////////////////////////////////////////////////////////////////////// - -} // end namespace stlplus - -#include "ntree.tpp" -#endif +#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 class ntree_node; + template class ntree; + template class ntree_iterator; + template class ntree_prefix_iterator; + template 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 + class ntree_iterator : public safe_iterator,ntree_node > + { + public: + // local type definitions + // an iterator points to an object whilst a const_iterator points to a const object + typedef ntree_iterator iterator; + typedef ntree_iterator const_iterator; + typedef ntree_iterator 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; + friend class ntree_prefix_iterator; + friend class ntree_postfix_iterator; + + 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* node); + // constructor used by ntree to create an end iterator + explicit ntree_iterator(const ntree* owner); + // used to create an alias of an iterator + explicit ntree_iterator(const safe_iterator, ntree_node >& 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 + class ntree_prefix_iterator + { + public: + typedef ntree_prefix_iterator iterator; + typedef ntree_prefix_iterator const_iterator; + typedef ntree_prefix_iterator this_iterator; + typedef ntree_iterator 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 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; + friend class ntree_iterator; + + private: + ntree_iterator m_iterator; + + explicit ntree_prefix_iterator(const ntree_iterator& i); + const ntree_iterator& get_iterator(void) const; + ntree_iterator& get_iterator(void); + }; + + //////////////////////////////////////////////////////////////////////////////// + + template + class ntree_postfix_iterator + { + public: + typedef ntree_postfix_iterator iterator; + typedef ntree_postfix_iterator const_iterator; + typedef ntree_postfix_iterator this_iterator; + typedef ntree_iterator 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 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; + friend class ntree_iterator; + + private: + ntree_iterator m_iterator; + + explicit ntree_postfix_iterator(const ntree_iterator& i); + const ntree_iterator& get_iterator(void) const; + ntree_iterator& get_iterator(void); + }; + + //////////////////////////////////////////////////////////////////////////////// + // The Ntree class + //////////////////////////////////////////////////////////////////////////////// + + template + class ntree + { + public: + // STL-like typedefs for the types and iterators + typedef T value_type; + + typedef ntree_iterator iterator; + typedef ntree_iterator const_iterator; + + typedef ntree_prefix_iterator prefix_iterator; + typedef ntree_prefix_iterator const_prefix_iterator; + + typedef ntree_postfix_iterator postfix_iterator; + typedef ntree_postfix_iterator const_postfix_iterator; + + ////////////////////////////////////////////////////////////////////////////// + // Constructors, destructors and copies + + ntree(void); + ~ntree(void); + + // copy constructor and assignment both copy the tree + ntree(const ntree&); + ntree& operator=(const ntree&); + + ////////////////////////////////////////////////////////////////////////////// + // 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&) + throw(wrong_object,null_dereference,end_dereference,std::out_of_range); + iterator append(const iterator& node, const ntree&) + 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 subtree(void); + ntree subtree(const iterator& node) + throw(wrong_object,null_dereference,end_dereference); + ntree subtree(const iterator& node, unsigned child) + throw(wrong_object,null_dereference,end_dereference,std::out_of_range); + + ntree cut(void); + ntree cut(const iterator& node) + throw(wrong_object,null_dereference,end_dereference); + ntree cut(const iterator& node, unsigned child) + throw(wrong_object,null_dereference,end_dereference,std::out_of_range); + + ////////////////////////////////////////////////////////////////////////////// + + private: + ntree_node* m_root; + }; + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus + +#include "ntree.tpp" +#endif diff --git a/src/stlplus/containers/ntree.tpp b/src/stlplus/containers/ntree.tpp index 6fd019d..1467aa8 100644 --- a/src/stlplus/containers/ntree.tpp +++ b/src/stlplus/containers/ntree.tpp @@ -1,913 +1,913 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// -#include -#include - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - // ntree_node - - template - class ntree_node - { - public: - master_iterator, ntree_node > m_master; - T m_data; - ntree_node* m_parent; - std::vector*> m_children; - - public: - ntree_node(const ntree* owner, const T& data = T()) : - m_master(owner,this), m_data(data), m_parent(0) - { - } - - void change_owner(const ntree* owner) - { - m_master.change_owner(owner); - for (TYPENAME std::vector*>::iterator i = m_children.begin(); i != m_children.end(); i++) - (*i)->change_owner(owner); - } - - ~ntree_node(void) - { - m_parent = 0; - for (TYPENAME std::vector*>::iterator i = m_children.begin(); i != m_children.end(); i++) - delete *i; - } - - }; - - template - static ntree_node* ntree_copy(const ntree* new_owner, ntree_node* root) - { - if (!root) return 0; - ntree_node* new_tree = new ntree_node(new_owner, root->m_data); - for (TYPENAME std::vector*>::iterator i = root->m_children.begin(); i != root->m_children.end(); i++) - { - ntree_node* 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 - static unsigned ntree_size(ntree_node* root) - { - if (!root) return 0; - unsigned result = 1; - for (TYPENAME std::vector*>::iterator i = root->m_children.begin(); i != root->m_children.end(); i++) - result += ntree_size(*i); - return result; - } - - template - static unsigned ntree_depth(ntree_node* root) - { - unsigned depth = 0; - for (ntree_node* 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 - ntree_iterator::ntree_iterator(void) - { - } - - // used to create an alias of an iterator - template - ntree_iterator::ntree_iterator(const safe_iterator, ntree_node >& iterator) : - safe_iterator,ntree_node >(iterator) - { - } - - // constructor used by ntree to create a non-null iterator - template - ntree_iterator::ntree_iterator(ntree_node* node) : - safe_iterator,ntree_node >(node->m_master) - { - } - - // constructor used by ntree to create an end iterator - template - ntree_iterator::ntree_iterator(const ntree* owner) : - safe_iterator,ntree_node >(owner) - { - } - - // destructor - template - ntree_iterator::~ntree_iterator(void) - { - } - - template - TYPENAME ntree_iterator::const_iterator ntree_iterator::constify(void) const - { - return ntree_iterator(*this); - } - - template - TYPENAME ntree_iterator::iterator ntree_iterator::deconstify(void) const - { - return ntree_iterator(*this); - } - - template - bool ntree_iterator::operator == (const TYPENAME ntree_iterator::this_iterator& r) const - { - return equal(r); - } - - template - bool ntree_iterator::operator != (const TYPENAME ntree_iterator::this_iterator& r) const - { - return !operator==(r); - } - - template - bool ntree_iterator::operator < (const TYPENAME ntree_iterator::this_iterator& r) const - { - return compare(r) < 0; - } - - template - TYPENAME ntree_iterator::reference ntree_iterator::operator*(void) const - throw(null_dereference,end_dereference) - { - this->assert_valid(); - return this->node()->m_data; - } - - template - TYPENAME ntree_iterator::pointer ntree_iterator::operator->(void) const - throw(null_dereference,end_dereference) - { - return &(operator*()); - } - - //////////////////////////////////////////////////////////////////////////////// - // ntree_prefix_iterator - - template - ntree_prefix_iterator::ntree_prefix_iterator(void) - { - } - - template - ntree_prefix_iterator::~ntree_prefix_iterator(void) - { - } - - template - ntree_prefix_iterator::ntree_prefix_iterator(const ntree_iterator& i) : - m_iterator(i) - { - // this is initialised with the root node - // which is also the first node in prefix traversal order - } - - template - bool ntree_prefix_iterator::null(void) const - { - return m_iterator.null(); - } - - template - bool ntree_prefix_iterator::end(void) const - { - return m_iterator.end(); - } - - template - bool ntree_prefix_iterator::valid(void) const - { - return m_iterator.valid(); - } - - template - TYPENAME ntree_prefix_iterator::const_iterator ntree_prefix_iterator::constify(void) const - { - return ntree_prefix_iterator(m_iterator); - } - - template - TYPENAME ntree_prefix_iterator::iterator ntree_prefix_iterator::deconstify(void) const - { - return ntree_prefix_iterator(m_iterator); - } - - template - ntree_iterator ntree_prefix_iterator::simplify(void) const - { - return m_iterator; - } - - template - bool ntree_prefix_iterator::operator == (const TYPENAME ntree_prefix_iterator::this_iterator& r) const - { - return m_iterator == r.m_iterator; - } - - template - bool ntree_prefix_iterator::operator != (const TYPENAME ntree_prefix_iterator::this_iterator& r) const - { - return m_iterator != r.m_iterator; - } - - template - bool ntree_prefix_iterator::operator < (const TYPENAME ntree_prefix_iterator::this_iterator& r) const - { - return m_iterator < r.m_iterator; - } - - template - TYPENAME ntree_prefix_iterator::this_iterator& ntree_prefix_iterator::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* 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* 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*>::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 ntree_prefix_iterator::this_iterator ntree_prefix_iterator::operator ++ (int) - throw(null_dereference,end_dereference) - { - // post-increment is defined in terms of the pre-increment - ntree_prefix_iterator result(*this); - ++(*this); - return result; - } - - template - TYPENAME ntree_prefix_iterator::reference ntree_prefix_iterator::operator*(void) const - throw(null_dereference,end_dereference) - { - return m_iterator.operator*(); - } - - template - TYPENAME ntree_prefix_iterator::pointer ntree_prefix_iterator::operator->(void) const - throw(null_dereference,end_dereference) - { - return m_iterator.operator->(); - } - - template - const ntree_iterator& ntree_prefix_iterator::get_iterator(void) const - { - return m_iterator; - } - - template - ntree_iterator& ntree_prefix_iterator::get_iterator(void) - { - return m_iterator; - } - - //////////////////////////////////////////////////////////////////////////////// - // ntree_postfix_iterator - - template - ntree_postfix_iterator::ntree_postfix_iterator(void) - { - } - - template - ntree_postfix_iterator::~ntree_postfix_iterator(void) - { - } - - template - ntree_postfix_iterator::ntree_postfix_iterator(const ntree_iterator& 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* node = m_iterator.node(); - while (!node->m_children.empty()) - node = node->m_children[0]; - m_iterator.set(node->m_master); - } - } - - template - bool ntree_postfix_iterator::null(void) const - { - return m_iterator.null(); - } - - template - bool ntree_postfix_iterator::end(void) const - { - return m_iterator.end(); - } - - template - bool ntree_postfix_iterator::valid(void) const - { - return m_iterator.valid(); - } - - template - TYPENAME ntree_postfix_iterator::const_iterator ntree_postfix_iterator::constify(void) const - { - return ntree_postfix_iterator(m_iterator); - } - - template - TYPENAME ntree_postfix_iterator::iterator ntree_postfix_iterator::deconstify(void) const - { - return ntree_postfix_iterator(m_iterator); - } - - template - ntree_iterator ntree_postfix_iterator::simplify(void) const - { - return m_iterator; - } - - template - bool ntree_postfix_iterator::operator == (const TYPENAME ntree_postfix_iterator::this_iterator& r) const - { - return m_iterator == r.m_iterator; - } - - template - bool ntree_postfix_iterator::operator != (const TYPENAME ntree_postfix_iterator::this_iterator& r) const - { - return m_iterator != r.m_iterator; - } - - template - bool ntree_postfix_iterator::operator < (const TYPENAME ntree_postfix_iterator::this_iterator& r) const - { - return m_iterator < r.m_iterator; - } - - template - TYPENAME ntree_postfix_iterator::this_iterator& ntree_postfix_iterator::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* old_node = m_iterator.node(); - ntree_node* 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*>::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* 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 ntree_postfix_iterator::this_iterator ntree_postfix_iterator::operator ++ (int) - throw(null_dereference,end_dereference) - { - // post-increment is defined in terms of the pre-increment - ntree_postfix_iterator result(*this); - ++(*this); - return result; - } - - template - TYPENAME ntree_postfix_iterator::reference ntree_postfix_iterator::operator*(void) const - throw(null_dereference,end_dereference) - { - return m_iterator.operator*(); - } - - template - TYPENAME ntree_postfix_iterator::pointer ntree_postfix_iterator::operator->(void) const - throw(null_dereference,end_dereference) - { - return m_iterator.operator->(); - } - - template - const ntree_iterator& ntree_postfix_iterator::get_iterator(void) const - { - return m_iterator; - } - - template - ntree_iterator& ntree_postfix_iterator::get_iterator(void) - { - return m_iterator; - } - - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - // ntree - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - - template - ntree::ntree(void) : m_root(0) - { - } - - template - ntree::~ntree(void) - { - if (m_root) delete m_root; - } - - template - ntree::ntree(const ntree& r) : m_root(0) - { - *this = r; - } - - template - ntree& ntree::operator=(const ntree& r) - { - if (m_root) delete m_root; - m_root = ntree_copy(this, r.m_root); - return *this; - } - - template - bool ntree::empty(void) const - { - return m_root == 0; - } - - template - unsigned ntree::size(void) const - { - return ntree_size(m_root); - } - - template - unsigned ntree::size(const TYPENAME ntree::const_iterator& i) const - throw(wrong_object,null_dereference,end_dereference) - { - i.assert_valid(this); - return ntree_size(i.node()); - } - - template - unsigned ntree::size(const TYPENAME ntree::iterator& i) - throw(wrong_object,null_dereference,end_dereference) - { - i.assert_valid(this); - return ntree_size(i.node()); - } - - template - unsigned ntree::depth(const TYPENAME ntree::const_iterator& i) const - throw(wrong_object,null_dereference,end_dereference) - { - i.assert_valid(this); - return ntree_depth(i.node()); - } - - template - unsigned ntree::depth(const TYPENAME ntree::iterator& i) - throw(wrong_object,null_dereference,end_dereference) - { - i.assert_valid(this); - return ntree_depth(i.node()); - } - - template - TYPENAME ntree::const_iterator ntree::root(void) const - { - if (!m_root) return ntree_iterator(this); - return ntree_iterator(m_root); - } - - template - TYPENAME ntree::iterator ntree::root(void) - { - if (!m_root) return ntree_iterator(this); - return ntree_iterator(m_root); - } - - template - unsigned ntree::children(const TYPENAME ntree::const_iterator& i) const - throw(wrong_object,null_dereference,end_dereference) - { - i.assert_valid(this); - return i.node()->m_children.size(); - } - - template - unsigned ntree::children(const ntree_iterator& i) - throw(wrong_object,null_dereference,end_dereference) - { - i.assert_valid(this); - return i.node()->m_children.size(); - } - - template - TYPENAME ntree::const_iterator ntree::child(const TYPENAME ntree::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(i.node()->m_children[child]); - } - - template - TYPENAME ntree::iterator ntree::child(const TYPENAME ntree::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(i.node()->m_children[child]); - } - - template - TYPENAME ntree::const_iterator ntree::parent(const TYPENAME ntree::const_iterator& i) const - throw(wrong_object,null_dereference,end_dereference) - { - i.assert_valid(this); - ntree_node* parent = i.node()->m_parent; - if (!parent) return ntree_iterator(this); - return ntree_iterator(parent); - } - - template - TYPENAME ntree::iterator ntree::parent(const TYPENAME ntree::iterator& i) - throw(wrong_object,null_dereference,end_dereference) - { - i.assert_valid(this); - ntree_node* parent = i.node()->m_parent; - if (!parent) return ntree_iterator(this); - return ntree_iterator(parent); - } - - template - TYPENAME ntree::const_prefix_iterator ntree::prefix_begin(void) const - { - return ntree_prefix_iterator(root()); - } - - template - TYPENAME ntree::prefix_iterator ntree::prefix_begin(void) - { - return ntree_prefix_iterator(root()); - } - - template - TYPENAME ntree::const_prefix_iterator ntree::prefix_end(void) const - { - return ntree_prefix_iterator(ntree_iterator(this)); - } - - template - TYPENAME ntree::prefix_iterator ntree::prefix_end(void) - { - return ntree_prefix_iterator(ntree_iterator(this)); - } - - template - TYPENAME ntree::const_postfix_iterator ntree::postfix_begin(void) const - { - return ntree_postfix_iterator(root()); - } - - template - TYPENAME ntree::postfix_iterator ntree::postfix_begin(void) - { - return ntree_postfix_iterator(root()); - } - - template - TYPENAME ntree::const_postfix_iterator ntree::postfix_end(void) const - { - return ntree_postfix_iterator(ntree_iterator(this)); - } - - template - TYPENAME ntree::postfix_iterator ntree::postfix_end(void) - { - return ntree_postfix_iterator(ntree_iterator(this)); - } - - template - TYPENAME ntree::iterator ntree::insert(const T& data) - { - // insert a new node as the root - return insert(ntree_iterator(this), 0, data); - } - - template - TYPENAME ntree::iterator ntree::insert(const TYPENAME ntree::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* new_node = new ntree_node(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(new_node); - } - - template - TYPENAME ntree::iterator ntree::append(const TYPENAME ntree::iterator& i, const T& data) - throw(wrong_object,null_dereference,end_dereference) - { - return insert(i, i.node()->m_children.size(), data); - } - - template - TYPENAME ntree::iterator ntree::insert(const TYPENAME ntree::iterator& i, unsigned offset, const ntree& 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* 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(new_node); - } - - template - TYPENAME ntree::iterator ntree::append(const TYPENAME ntree::iterator& i, const ntree& tree) - throw(wrong_object,null_dereference,end_dereference) - { - return insert(i, children(i), tree); - } - - template - TYPENAME ntree::iterator ntree::push(const TYPENAME ntree::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* new_node = new ntree_node(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(new_node); - } - - template - void ntree::pop(const TYPENAME ntree::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* node = parent.node(); - if (offset >= node->m_children.size()) throw std::out_of_range("stlplus::ntree"); - // move the grandchildren first - ntree_node* 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* 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 - void ntree::erase(void) - { - // erase the whole tree - erase(root()); - } - - template - void ntree::erase(const TYPENAME ntree::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* node = i.node(); - if (node == m_root) - { - delete m_root; - m_root = 0; - } - else - { - ntree_node* parent = node->m_parent; - // impossible for parent to be null - should assert this - TYPENAME std::vector*>::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 - void ntree::erase(const TYPENAME ntree::iterator& i, unsigned offset) - throw(wrong_object,null_dereference,end_dereference,std::out_of_range) - { - erase(child(i, offset)); - } - - template - ntree ntree::subtree(void) - { - return subtree(root()); - } - - template - ntree ntree::subtree(const TYPENAME ntree::iterator& i) - throw(wrong_object,null_dereference,end_dereference) - { - ntree result; - if (!i.end()) - { - i.assert_valid(this); - result.m_root = ntree_copy(&result, i.node()); - } - return result; - } - - template - ntree ntree::subtree(const TYPENAME ntree::iterator& i, unsigned offset) - throw(wrong_object,null_dereference,end_dereference,std::out_of_range) - { - return subtree(child(i, offset)); - } - - template - ntree ntree::cut(void) - { - return cut(root()); - } - - template - ntree ntree::cut(const TYPENAME ntree::iterator& i) - throw(wrong_object,null_dereference,end_dereference) - { - ntree result; - if (!i.end()) - { - i.assert_valid(this); - ntree_node* node = i.node(); - if (node == m_root) - { - result.m_root = m_root; - m_root = 0; - } - else - { - ntree_node* parent = node->m_parent; - // impossible for parent to be null - should assert this - TYPENAME std::vector*>::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 - ntree ntree::cut(const TYPENAME ntree::iterator& i, unsigned offset) - throw(wrong_object,null_dereference,end_dereference,std::out_of_range) - { - return cut(child(i, offset)); - } - - //////////////////////////////////////////////////////////////////////////////// - -} // end namespace stlplus - +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include +#include + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // ntree_node + + template + class ntree_node + { + public: + master_iterator, ntree_node > m_master; + T m_data; + ntree_node* m_parent; + std::vector*> m_children; + + public: + ntree_node(const ntree* owner, const T& data = T()) : + m_master(owner,this), m_data(data), m_parent(0) + { + } + + void change_owner(const ntree* owner) + { + m_master.change_owner(owner); + for (TYPENAME std::vector*>::iterator i = m_children.begin(); i != m_children.end(); i++) + (*i)->change_owner(owner); + } + + ~ntree_node(void) + { + m_parent = 0; + for (TYPENAME std::vector*>::iterator i = m_children.begin(); i != m_children.end(); i++) + delete *i; + } + + }; + + template + static ntree_node* ntree_copy(const ntree* new_owner, ntree_node* root) + { + if (!root) return 0; + ntree_node* new_tree = new ntree_node(new_owner, root->m_data); + for (TYPENAME std::vector*>::iterator i = root->m_children.begin(); i != root->m_children.end(); i++) + { + ntree_node* 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 + static unsigned ntree_size(ntree_node* root) + { + if (!root) return 0; + unsigned result = 1; + for (TYPENAME std::vector*>::iterator i = root->m_children.begin(); i != root->m_children.end(); i++) + result += ntree_size(*i); + return result; + } + + template + static unsigned ntree_depth(ntree_node* root) + { + unsigned depth = 0; + for (ntree_node* 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 + ntree_iterator::ntree_iterator(void) + { + } + + // used to create an alias of an iterator + template + ntree_iterator::ntree_iterator(const safe_iterator, ntree_node >& iterator) : + safe_iterator,ntree_node >(iterator) + { + } + + // constructor used by ntree to create a non-null iterator + template + ntree_iterator::ntree_iterator(ntree_node* node) : + safe_iterator,ntree_node >(node->m_master) + { + } + + // constructor used by ntree to create an end iterator + template + ntree_iterator::ntree_iterator(const ntree* owner) : + safe_iterator,ntree_node >(owner) + { + } + + // destructor + template + ntree_iterator::~ntree_iterator(void) + { + } + + template + TYPENAME ntree_iterator::const_iterator ntree_iterator::constify(void) const + { + return ntree_iterator(*this); + } + + template + TYPENAME ntree_iterator::iterator ntree_iterator::deconstify(void) const + { + return ntree_iterator(*this); + } + + template + bool ntree_iterator::operator == (const TYPENAME ntree_iterator::this_iterator& r) const + { + return equal(r); + } + + template + bool ntree_iterator::operator != (const TYPENAME ntree_iterator::this_iterator& r) const + { + return !operator==(r); + } + + template + bool ntree_iterator::operator < (const TYPENAME ntree_iterator::this_iterator& r) const + { + return compare(r) < 0; + } + + template + TYPENAME ntree_iterator::reference ntree_iterator::operator*(void) const + throw(null_dereference,end_dereference) + { + this->assert_valid(); + return this->node()->m_data; + } + + template + TYPENAME ntree_iterator::pointer ntree_iterator::operator->(void) const + throw(null_dereference,end_dereference) + { + return &(operator*()); + } + + //////////////////////////////////////////////////////////////////////////////// + // ntree_prefix_iterator + + template + ntree_prefix_iterator::ntree_prefix_iterator(void) + { + } + + template + ntree_prefix_iterator::~ntree_prefix_iterator(void) + { + } + + template + ntree_prefix_iterator::ntree_prefix_iterator(const ntree_iterator& i) : + m_iterator(i) + { + // this is initialised with the root node + // which is also the first node in prefix traversal order + } + + template + bool ntree_prefix_iterator::null(void) const + { + return m_iterator.null(); + } + + template + bool ntree_prefix_iterator::end(void) const + { + return m_iterator.end(); + } + + template + bool ntree_prefix_iterator::valid(void) const + { + return m_iterator.valid(); + } + + template + TYPENAME ntree_prefix_iterator::const_iterator ntree_prefix_iterator::constify(void) const + { + return ntree_prefix_iterator(m_iterator); + } + + template + TYPENAME ntree_prefix_iterator::iterator ntree_prefix_iterator::deconstify(void) const + { + return ntree_prefix_iterator(m_iterator); + } + + template + ntree_iterator ntree_prefix_iterator::simplify(void) const + { + return m_iterator; + } + + template + bool ntree_prefix_iterator::operator == (const TYPENAME ntree_prefix_iterator::this_iterator& r) const + { + return m_iterator == r.m_iterator; + } + + template + bool ntree_prefix_iterator::operator != (const TYPENAME ntree_prefix_iterator::this_iterator& r) const + { + return m_iterator != r.m_iterator; + } + + template + bool ntree_prefix_iterator::operator < (const TYPENAME ntree_prefix_iterator::this_iterator& r) const + { + return m_iterator < r.m_iterator; + } + + template + TYPENAME ntree_prefix_iterator::this_iterator& ntree_prefix_iterator::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* 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* 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*>::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 ntree_prefix_iterator::this_iterator ntree_prefix_iterator::operator ++ (int) + throw(null_dereference,end_dereference) + { + // post-increment is defined in terms of the pre-increment + ntree_prefix_iterator result(*this); + ++(*this); + return result; + } + + template + TYPENAME ntree_prefix_iterator::reference ntree_prefix_iterator::operator*(void) const + throw(null_dereference,end_dereference) + { + return m_iterator.operator*(); + } + + template + TYPENAME ntree_prefix_iterator::pointer ntree_prefix_iterator::operator->(void) const + throw(null_dereference,end_dereference) + { + return m_iterator.operator->(); + } + + template + const ntree_iterator& ntree_prefix_iterator::get_iterator(void) const + { + return m_iterator; + } + + template + ntree_iterator& ntree_prefix_iterator::get_iterator(void) + { + return m_iterator; + } + + //////////////////////////////////////////////////////////////////////////////// + // ntree_postfix_iterator + + template + ntree_postfix_iterator::ntree_postfix_iterator(void) + { + } + + template + ntree_postfix_iterator::~ntree_postfix_iterator(void) + { + } + + template + ntree_postfix_iterator::ntree_postfix_iterator(const ntree_iterator& 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* node = m_iterator.node(); + while (!node->m_children.empty()) + node = node->m_children[0]; + m_iterator.set(node->m_master); + } + } + + template + bool ntree_postfix_iterator::null(void) const + { + return m_iterator.null(); + } + + template + bool ntree_postfix_iterator::end(void) const + { + return m_iterator.end(); + } + + template + bool ntree_postfix_iterator::valid(void) const + { + return m_iterator.valid(); + } + + template + TYPENAME ntree_postfix_iterator::const_iterator ntree_postfix_iterator::constify(void) const + { + return ntree_postfix_iterator(m_iterator); + } + + template + TYPENAME ntree_postfix_iterator::iterator ntree_postfix_iterator::deconstify(void) const + { + return ntree_postfix_iterator(m_iterator); + } + + template + ntree_iterator ntree_postfix_iterator::simplify(void) const + { + return m_iterator; + } + + template + bool ntree_postfix_iterator::operator == (const TYPENAME ntree_postfix_iterator::this_iterator& r) const + { + return m_iterator == r.m_iterator; + } + + template + bool ntree_postfix_iterator::operator != (const TYPENAME ntree_postfix_iterator::this_iterator& r) const + { + return m_iterator != r.m_iterator; + } + + template + bool ntree_postfix_iterator::operator < (const TYPENAME ntree_postfix_iterator::this_iterator& r) const + { + return m_iterator < r.m_iterator; + } + + template + TYPENAME ntree_postfix_iterator::this_iterator& ntree_postfix_iterator::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* old_node = m_iterator.node(); + ntree_node* 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*>::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* 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 ntree_postfix_iterator::this_iterator ntree_postfix_iterator::operator ++ (int) + throw(null_dereference,end_dereference) + { + // post-increment is defined in terms of the pre-increment + ntree_postfix_iterator result(*this); + ++(*this); + return result; + } + + template + TYPENAME ntree_postfix_iterator::reference ntree_postfix_iterator::operator*(void) const + throw(null_dereference,end_dereference) + { + return m_iterator.operator*(); + } + + template + TYPENAME ntree_postfix_iterator::pointer ntree_postfix_iterator::operator->(void) const + throw(null_dereference,end_dereference) + { + return m_iterator.operator->(); + } + + template + const ntree_iterator& ntree_postfix_iterator::get_iterator(void) const + { + return m_iterator; + } + + template + ntree_iterator& ntree_postfix_iterator::get_iterator(void) + { + return m_iterator; + } + + //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + // ntree + //////////////////////////////////////////////////////////////////////////////// + //////////////////////////////////////////////////////////////////////////////// + + template + ntree::ntree(void) : m_root(0) + { + } + + template + ntree::~ntree(void) + { + if (m_root) delete m_root; + } + + template + ntree::ntree(const ntree& r) : m_root(0) + { + *this = r; + } + + template + ntree& ntree::operator=(const ntree& r) + { + if (m_root) delete m_root; + m_root = ntree_copy(this, r.m_root); + return *this; + } + + template + bool ntree::empty(void) const + { + return m_root == 0; + } + + template + unsigned ntree::size(void) const + { + return ntree_size(m_root); + } + + template + unsigned ntree::size(const TYPENAME ntree::const_iterator& i) const + throw(wrong_object,null_dereference,end_dereference) + { + i.assert_valid(this); + return ntree_size(i.node()); + } + + template + unsigned ntree::size(const TYPENAME ntree::iterator& i) + throw(wrong_object,null_dereference,end_dereference) + { + i.assert_valid(this); + return ntree_size(i.node()); + } + + template + unsigned ntree::depth(const TYPENAME ntree::const_iterator& i) const + throw(wrong_object,null_dereference,end_dereference) + { + i.assert_valid(this); + return ntree_depth(i.node()); + } + + template + unsigned ntree::depth(const TYPENAME ntree::iterator& i) + throw(wrong_object,null_dereference,end_dereference) + { + i.assert_valid(this); + return ntree_depth(i.node()); + } + + template + TYPENAME ntree::const_iterator ntree::root(void) const + { + if (!m_root) return ntree_iterator(this); + return ntree_iterator(m_root); + } + + template + TYPENAME ntree::iterator ntree::root(void) + { + if (!m_root) return ntree_iterator(this); + return ntree_iterator(m_root); + } + + template + unsigned ntree::children(const TYPENAME ntree::const_iterator& i) const + throw(wrong_object,null_dereference,end_dereference) + { + i.assert_valid(this); + return i.node()->m_children.size(); + } + + template + unsigned ntree::children(const ntree_iterator& i) + throw(wrong_object,null_dereference,end_dereference) + { + i.assert_valid(this); + return i.node()->m_children.size(); + } + + template + TYPENAME ntree::const_iterator ntree::child(const TYPENAME ntree::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(i.node()->m_children[child]); + } + + template + TYPENAME ntree::iterator ntree::child(const TYPENAME ntree::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(i.node()->m_children[child]); + } + + template + TYPENAME ntree::const_iterator ntree::parent(const TYPENAME ntree::const_iterator& i) const + throw(wrong_object,null_dereference,end_dereference) + { + i.assert_valid(this); + ntree_node* parent = i.node()->m_parent; + if (!parent) return ntree_iterator(this); + return ntree_iterator(parent); + } + + template + TYPENAME ntree::iterator ntree::parent(const TYPENAME ntree::iterator& i) + throw(wrong_object,null_dereference,end_dereference) + { + i.assert_valid(this); + ntree_node* parent = i.node()->m_parent; + if (!parent) return ntree_iterator(this); + return ntree_iterator(parent); + } + + template + TYPENAME ntree::const_prefix_iterator ntree::prefix_begin(void) const + { + return ntree_prefix_iterator(root()); + } + + template + TYPENAME ntree::prefix_iterator ntree::prefix_begin(void) + { + return ntree_prefix_iterator(root()); + } + + template + TYPENAME ntree::const_prefix_iterator ntree::prefix_end(void) const + { + return ntree_prefix_iterator(ntree_iterator(this)); + } + + template + TYPENAME ntree::prefix_iterator ntree::prefix_end(void) + { + return ntree_prefix_iterator(ntree_iterator(this)); + } + + template + TYPENAME ntree::const_postfix_iterator ntree::postfix_begin(void) const + { + return ntree_postfix_iterator(root()); + } + + template + TYPENAME ntree::postfix_iterator ntree::postfix_begin(void) + { + return ntree_postfix_iterator(root()); + } + + template + TYPENAME ntree::const_postfix_iterator ntree::postfix_end(void) const + { + return ntree_postfix_iterator(ntree_iterator(this)); + } + + template + TYPENAME ntree::postfix_iterator ntree::postfix_end(void) + { + return ntree_postfix_iterator(ntree_iterator(this)); + } + + template + TYPENAME ntree::iterator ntree::insert(const T& data) + { + // insert a new node as the root + return insert(ntree_iterator(this), 0, data); + } + + template + TYPENAME ntree::iterator ntree::insert(const TYPENAME ntree::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* new_node = new ntree_node(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(new_node); + } + + template + TYPENAME ntree::iterator ntree::append(const TYPENAME ntree::iterator& i, const T& data) + throw(wrong_object,null_dereference,end_dereference) + { + return insert(i, i.node()->m_children.size(), data); + } + + template + TYPENAME ntree::iterator ntree::insert(const TYPENAME ntree::iterator& i, unsigned offset, const ntree& 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* 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(new_node); + } + + template + TYPENAME ntree::iterator ntree::append(const TYPENAME ntree::iterator& i, const ntree& tree) + throw(wrong_object,null_dereference,end_dereference) + { + return insert(i, children(i), tree); + } + + template + TYPENAME ntree::iterator ntree::push(const TYPENAME ntree::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* new_node = new ntree_node(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(new_node); + } + + template + void ntree::pop(const TYPENAME ntree::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* node = parent.node(); + if (offset >= node->m_children.size()) throw std::out_of_range("stlplus::ntree"); + // move the grandchildren first + ntree_node* 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* 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 + void ntree::erase(void) + { + // erase the whole tree + erase(root()); + } + + template + void ntree::erase(const TYPENAME ntree::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* node = i.node(); + if (node == m_root) + { + delete m_root; + m_root = 0; + } + else + { + ntree_node* parent = node->m_parent; + // impossible for parent to be null - should assert this + TYPENAME std::vector*>::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 + void ntree::erase(const TYPENAME ntree::iterator& i, unsigned offset) + throw(wrong_object,null_dereference,end_dereference,std::out_of_range) + { + erase(child(i, offset)); + } + + template + ntree ntree::subtree(void) + { + return subtree(root()); + } + + template + ntree ntree::subtree(const TYPENAME ntree::iterator& i) + throw(wrong_object,null_dereference,end_dereference) + { + ntree result; + if (!i.end()) + { + i.assert_valid(this); + result.m_root = ntree_copy(&result, i.node()); + } + return result; + } + + template + ntree ntree::subtree(const TYPENAME ntree::iterator& i, unsigned offset) + throw(wrong_object,null_dereference,end_dereference,std::out_of_range) + { + return subtree(child(i, offset)); + } + + template + ntree ntree::cut(void) + { + return cut(root()); + } + + template + ntree ntree::cut(const TYPENAME ntree::iterator& i) + throw(wrong_object,null_dereference,end_dereference) + { + ntree result; + if (!i.end()) + { + i.assert_valid(this); + ntree_node* node = i.node(); + if (node == m_root) + { + result.m_root = m_root; + m_root = 0; + } + else + { + ntree_node* parent = node->m_parent; + // impossible for parent to be null - should assert this + TYPENAME std::vector*>::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 + ntree ntree::cut(const TYPENAME ntree::iterator& i, unsigned offset) + throw(wrong_object,null_dereference,end_dereference,std::out_of_range) + { + return cut(child(i, offset)); + } + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus + diff --git a/src/stlplus/containers/safe_iterator.hpp b/src/stlplus/containers/safe_iterator.hpp index 5bf80fb..7412687 100644 --- a/src/stlplus/containers/safe_iterator.hpp +++ b/src/stlplus/containers/safe_iterator.hpp @@ -1,155 +1,155 @@ -#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 - class safe_iterator_body; - - template - 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 - 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; - private: - master_iterator(const master_iterator&) throw(); - master_iterator& operator=(const master_iterator&) throw(); - safe_iterator_body* m_body; - }; - - //////////////////////////////////////////////////////////////////////////////// - // Safe Iterator - //////////////////////////////////////////////////////////////////////////////// - - template - 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&) throw(); - - // copy constructor does aliasing - safe_iterator(const safe_iterator&) throw(); - - // alias an iterator by assignment - safe_iterator& operator=(const safe_iterator&) throw(); - - // destructor - ~safe_iterator(void) throw(); - - // reassignment to another node used in increment/decrement operation - void set(const master_iterator&) 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& right) const throw(); - int compare(const safe_iterator& 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; - private: - safe_iterator_body* m_body; - }; - - //////////////////////////////////////////////////////////////////////////////// - -} // end namespace stlplus - -#include "safe_iterator.tpp" -#endif +#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 + class safe_iterator_body; + + template + 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 + 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; + private: + master_iterator(const master_iterator&) throw(); + master_iterator& operator=(const master_iterator&) throw(); + safe_iterator_body* m_body; + }; + + //////////////////////////////////////////////////////////////////////////////// + // Safe Iterator + //////////////////////////////////////////////////////////////////////////////// + + template + 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&) throw(); + + // copy constructor does aliasing + safe_iterator(const safe_iterator&) throw(); + + // alias an iterator by assignment + safe_iterator& operator=(const safe_iterator&) throw(); + + // destructor + ~safe_iterator(void) throw(); + + // reassignment to another node used in increment/decrement operation + void set(const master_iterator&) 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& right) const throw(); + int compare(const safe_iterator& 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; + private: + safe_iterator_body* m_body; + }; + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus + +#include "safe_iterator.tpp" +#endif diff --git a/src/stlplus/containers/safe_iterator.tpp b/src/stlplus/containers/safe_iterator.tpp index 14d89b9..3296482 100644 --- a/src/stlplus/containers/safe_iterator.tpp +++ b/src/stlplus/containers/safe_iterator.tpp @@ -1,373 +1,373 @@ -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - // body class implements the aliasing behaviour - - template - 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* right) const throw() - { -// return m_node == right->m_node; - return compare(right) == 0; - } - - int compare(const safe_iterator_body* 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 - master_iterator::master_iterator(const O* owner, N* node) throw() : - m_body(new safe_iterator_body(owner,node)) - { - } - - // destructor - disconnect all iterators from the node - // this usually happens when the node is deleted and must invalidate all aliases - template - master_iterator::~master_iterator(void) throw() - { - m_body->set_end(); - if(m_body->decrement()) - { - delete m_body; - m_body = 0; - } - } - - // dereference - template - N* master_iterator::node(void) const throw() - { - return m_body->node(); - } - - template - const O* master_iterator::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 - void master_iterator::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 - safe_iterator::safe_iterator(void) throw() : - m_body(new safe_iterator_body(0,0)) - { - } - - // construct a valid iterator by aliasing from the owner node's master iterator - template - safe_iterator::safe_iterator(const master_iterator& 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 - safe_iterator::safe_iterator(const safe_iterator& r) throw() : - m_body(0) - { - m_body = r.m_body; - m_body->increment(); - } - - // assignment implements dealiasing followed by aliasing - template - safe_iterator& safe_iterator::operator=(const safe_iterator& 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 - safe_iterator::~safe_iterator(void) throw() - { - if(m_body->decrement()) - { - delete m_body; - m_body = 0; - } - } - - - // increment/decrement operation - // implements dealiasing followed by aliasing - template - void safe_iterator::set(const master_iterator& 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 - N* safe_iterator::node(void) const throw() - { - return m_body->node(); - } - - template - const O* safe_iterator::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 - void safe_iterator::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(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 - safe_iterator::safe_iterator(const O* owner) throw() : - m_body(new safe_iterator_body(owner,0)) - { - } - - // change to an end iterator - e.g. as a result of incrementing off the end - template - void safe_iterator::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(owner(),0); - } - } - - //////////////////////////////////////////////////////////////////////////////// - // tests - - // comparison - template - bool safe_iterator::equal(const safe_iterator& right) const throw() - { - return compare(right) == 0; - } - - template - int safe_iterator::compare(const safe_iterator& 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 - bool safe_iterator::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 - bool safe_iterator::end(void) const throw() - { - return m_body->end(); - } - - // a valid iterator is one that can be dereferenced - template - bool safe_iterator::valid(void) const throw() - { - return m_body->valid(); - } - - // check the rules for a valid iterator that can be dereferenced - template - void safe_iterator::assert_valid(void) const throw(null_dereference,end_dereference) - { - m_body->assert_valid(); - } - - template - void safe_iterator::assert_valid(const O* owner) const throw(wrong_object,null_dereference,end_dereference) - { - m_body->assert_valid(); - m_body->assert_owner(owner); - } - - template - void safe_iterator::assert_non_null(void) const throw(null_dereference) - { - m_body->assert_non_null(); - } - - template - void safe_iterator::assert_owner(const O* owner) const throw(wrong_object) - { - m_body->assert_owner(owner); - } - -} // end namespace stlplus +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // body class implements the aliasing behaviour + + template + 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* right) const throw() + { +// return m_node == right->m_node; + return compare(right) == 0; + } + + int compare(const safe_iterator_body* 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 + master_iterator::master_iterator(const O* owner, N* node) throw() : + m_body(new safe_iterator_body(owner,node)) + { + } + + // destructor - disconnect all iterators from the node + // this usually happens when the node is deleted and must invalidate all aliases + template + master_iterator::~master_iterator(void) throw() + { + m_body->set_end(); + if(m_body->decrement()) + { + delete m_body; + m_body = 0; + } + } + + // dereference + template + N* master_iterator::node(void) const throw() + { + return m_body->node(); + } + + template + const O* master_iterator::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 + void master_iterator::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 + safe_iterator::safe_iterator(void) throw() : + m_body(new safe_iterator_body(0,0)) + { + } + + // construct a valid iterator by aliasing from the owner node's master iterator + template + safe_iterator::safe_iterator(const master_iterator& 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 + safe_iterator::safe_iterator(const safe_iterator& r) throw() : + m_body(0) + { + m_body = r.m_body; + m_body->increment(); + } + + // assignment implements dealiasing followed by aliasing + template + safe_iterator& safe_iterator::operator=(const safe_iterator& 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 + safe_iterator::~safe_iterator(void) throw() + { + if(m_body->decrement()) + { + delete m_body; + m_body = 0; + } + } + + + // increment/decrement operation + // implements dealiasing followed by aliasing + template + void safe_iterator::set(const master_iterator& 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 + N* safe_iterator::node(void) const throw() + { + return m_body->node(); + } + + template + const O* safe_iterator::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 + void safe_iterator::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(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 + safe_iterator::safe_iterator(const O* owner) throw() : + m_body(new safe_iterator_body(owner,0)) + { + } + + // change to an end iterator - e.g. as a result of incrementing off the end + template + void safe_iterator::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(owner(),0); + } + } + + //////////////////////////////////////////////////////////////////////////////// + // tests + + // comparison + template + bool safe_iterator::equal(const safe_iterator& right) const throw() + { + return compare(right) == 0; + } + + template + int safe_iterator::compare(const safe_iterator& 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 + bool safe_iterator::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 + bool safe_iterator::end(void) const throw() + { + return m_body->end(); + } + + // a valid iterator is one that can be dereferenced + template + bool safe_iterator::valid(void) const throw() + { + return m_body->valid(); + } + + // check the rules for a valid iterator that can be dereferenced + template + void safe_iterator::assert_valid(void) const throw(null_dereference,end_dereference) + { + m_body->assert_valid(); + } + + template + void safe_iterator::assert_valid(const O* owner) const throw(wrong_object,null_dereference,end_dereference) + { + m_body->assert_valid(); + m_body->assert_owner(owner); + } + + template + void safe_iterator::assert_non_null(void) const throw(null_dereference) + { + m_body->assert_non_null(); + } + + template + void safe_iterator::assert_owner(const O* owner) const throw(wrong_object) + { + m_body->assert_owner(owner); + } + +} // end namespace stlplus diff --git a/src/stlplus/containers/simple_ptr.hpp b/src/stlplus/containers/simple_ptr.hpp index 08fff03..72d1db7 100644 --- a/src/stlplus/containers/simple_ptr.hpp +++ b/src/stlplus/containers/simple_ptr.hpp @@ -1,264 +1,264 @@ -#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 -#include - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - // Base class - //////////////////////////////////////////////////////////////////////////////// - - template - 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 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& r); - - // assignment operator - required, else the output of GCC suffers segmentation faults - simple_ptr_base& operator=(const simple_ptr_base& 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&); - - // 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&) 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&) throw(illegal_copy); - - ////////////////////////////////////////////////////////////////////////////// - // functions that involve casting - -#ifdef STLPLUS_MEMBER_TEMPLATES - - // dynamic cast of underlying pointer to a derived/parent - template simple_ptr_base dyn_cast(void) const; - - // static cast of underlying pointer to a derived/parent - template simple_ptr_base stat_cast(void) const; - - // cast of underlying pointer to a base - while keeping the same ref-counted object - template simple_ptr_base 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 - class simple_ptr : public simple_ptr_base > - { - public: - simple_ptr(void) {} - explicit simple_ptr(const T& data) : simple_ptr_base >(data) {} - explicit simple_ptr(T* data) : simple_ptr_base >(data) {} - simple_ptr& operator=(const T& data) {set_value(data); return *this;} - simple_ptr& operator=(T* data) {set(data); return *this;} - ~simple_ptr(void) {} - }; - - //////////////////////////////////////////////////////////////////////////////// - // smart_ptr_clone for polymorphic class hierarchies which have a clone method - - template - class simple_ptr_clone : public simple_ptr_base > - { - public: - simple_ptr_clone(void) {} - explicit simple_ptr_clone(const T& data) : simple_ptr_base >(data) {} - explicit simple_ptr_clone(T* data) : simple_ptr_base >(data) {} - simple_ptr_clone& operator=(const T& data) {set_value(data); return *this;} - simple_ptr_clone& 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 - class simple_ptr_nocopy : public simple_ptr_base > - { - public: - simple_ptr_nocopy(void) {} - explicit simple_ptr_nocopy(const T& data) : simple_ptr_base >(data) {} - explicit simple_ptr_nocopy(T* data) : simple_ptr_base >(data) {} - simple_ptr_nocopy& operator=(const T& data) {set_value(data); return *this;} - simple_ptr_nocopy& operator=(T* data) {set(data); return *this;} - ~simple_ptr_nocopy(void) {} - }; - - //////////////////////////////////////////////////////////////////////////////// - -} // end namespace stlplus - -#include "simple_ptr.tpp" -#endif +#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 +#include + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // Base class + //////////////////////////////////////////////////////////////////////////////// + + template + 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 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& r); + + // assignment operator - required, else the output of GCC suffers segmentation faults + simple_ptr_base& operator=(const simple_ptr_base& 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&); + + // 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&) 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&) throw(illegal_copy); + + ////////////////////////////////////////////////////////////////////////////// + // functions that involve casting + +#ifdef STLPLUS_MEMBER_TEMPLATES + + // dynamic cast of underlying pointer to a derived/parent + template simple_ptr_base dyn_cast(void) const; + + // static cast of underlying pointer to a derived/parent + template simple_ptr_base stat_cast(void) const; + + // cast of underlying pointer to a base - while keeping the same ref-counted object + template simple_ptr_base 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 + class simple_ptr : public simple_ptr_base > + { + public: + simple_ptr(void) {} + explicit simple_ptr(const T& data) : simple_ptr_base >(data) {} + explicit simple_ptr(T* data) : simple_ptr_base >(data) {} + simple_ptr& operator=(const T& data) {set_value(data); return *this;} + simple_ptr& operator=(T* data) {set(data); return *this;} + ~simple_ptr(void) {} + }; + + //////////////////////////////////////////////////////////////////////////////// + // smart_ptr_clone for polymorphic class hierarchies which have a clone method + + template + class simple_ptr_clone : public simple_ptr_base > + { + public: + simple_ptr_clone(void) {} + explicit simple_ptr_clone(const T& data) : simple_ptr_base >(data) {} + explicit simple_ptr_clone(T* data) : simple_ptr_base >(data) {} + simple_ptr_clone& operator=(const T& data) {set_value(data); return *this;} + simple_ptr_clone& 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 + class simple_ptr_nocopy : public simple_ptr_base > + { + public: + simple_ptr_nocopy(void) {} + explicit simple_ptr_nocopy(const T& data) : simple_ptr_base >(data) {} + explicit simple_ptr_nocopy(T* data) : simple_ptr_base >(data) {} + simple_ptr_nocopy& operator=(const T& data) {set_value(data); return *this;} + simple_ptr_nocopy& operator=(T* data) {set(data); return *this;} + ~simple_ptr_nocopy(void) {} + }; + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus + +#include "simple_ptr.tpp" +#endif diff --git a/src/stlplus/containers/simple_ptr.tpp b/src/stlplus/containers/simple_ptr.tpp index 13f5596..ead6744 100644 --- a/src/stlplus/containers/simple_ptr.tpp +++ b/src/stlplus/containers/simple_ptr.tpp @@ -1,338 +1,338 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Daniel Milton -// Copyright: (c) Daniel Milton 2002-2009 - -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - // simple_ptr_base class - //////////////////////////////////////////////////////////////////////////////// - - //////////////////////////////////////////////////////////////////////////////// - // constructors, assignments and destructors - - // create a null pointer - template - simple_ptr_base::simple_ptr_base(void) : - m_pointer(0), - m_count(new unsigned(1)) - { - } - - // create a pointer containing a *copy* of the object pointer - template - simple_ptr_base::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 x(new type(args)) - template - simple_ptr_base::simple_ptr_base(T* data) : - m_pointer(data), - m_count(new unsigned(1)) - { - } - - // copy constructor implements counted referencing - no copy is made - template - simple_ptr_base::simple_ptr_base(const simple_ptr_base& r) : - m_pointer(r.m_pointer), - m_count(r.m_count) - { - increment(); - } - - // assignment operator - required, else the output of GCC suffers segmentation faults - template - simple_ptr_base& simple_ptr_base::operator=(const simple_ptr_base& r) - { - alias(r); - return *this; - } - - // destructor decrements the reference count and delete only when the last reference is destroyed - template - simple_ptr_base::~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 - bool simple_ptr_base::null(void) const - { - return m_pointer==0; - } - - template - bool simple_ptr_base::present(void) const - { - return m_pointer!=0; - } - - template - bool simple_ptr_base::operator!(void) const - { - return m_pointer==0; - } - - template - simple_ptr_base::operator bool(void) const - { - return m_pointer!=0; - } - - ////////////////////////////////////////////////////////////////////////////// - // dereference operators and functions - - template - T& simple_ptr_base::operator*(void) throw(null_dereference) - { - if (!m_pointer) throw null_dereference("null pointer dereferenced in simple_ptr::operator*"); - return *m_pointer; - } - - template - const T& simple_ptr_base::operator*(void) const throw(null_dereference) - { - if (!m_pointer) throw null_dereference("null pointer dereferenced in simple_ptr::operator*"); - return *m_pointer; - } - - template - T* simple_ptr_base::operator->(void) throw(null_dereference) - { - if (!m_pointer) throw null_dereference("null pointer dereferenced in simple_ptr::operator->"); - return m_pointer; - } - - template - const T* simple_ptr_base::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 - void simple_ptr_base::set_value(const T& data) throw(illegal_copy) - { - set(C()(data)); - } - - template - T& simple_ptr_base::value(void) throw(null_dereference) - { - if (!m_pointer) throw null_dereference("null pointer dereferenced in simple_ptr::value"); - return *m_pointer; - } - - template - const T& simple_ptr_base::value(void) const throw(null_dereference) - { - if (!m_pointer) throw null_dereference("null pointer dereferenced in simple_ptr::value"); - return *m_pointer; - } - - template - void simple_ptr_base::set(T* data) - { - unsigned& count = *m_count; - if (count<=1) - delete m_pointer; - else - { - --count; - m_count = new unsigned(1); - } - m_pointer = data; - } - - template - T* simple_ptr_base::pointer(void) - { - return m_pointer; - } - - template - const T* simple_ptr_base::pointer(void) const - { - return m_pointer; - } - - //////////////////////////////////////////////////////////////////////////////// - // functions to manage counted referencing - - template - void simple_ptr_base::increment(void) - { - ++(*m_count); - } - - template - bool simple_ptr_base::decrement(void) - { - unsigned& count = *m_count; - --count; - return count == 0; - } - - // make this an alias of the passed object - template - void simple_ptr_base::alias(const simple_ptr_base& 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 - bool simple_ptr_base::aliases(const simple_ptr_base& r) const - { - return m_count == r.m_count; - } - - template - unsigned simple_ptr_base::alias_count(void) const - { - return *m_count; - } - - template - void simple_ptr_base::clear(void) - { - set(0); - } - - template - void simple_ptr_base::clear_unique(void) - { - set(0); // no difference between clear and clear_unique with the simple_ptr - } - - template - void simple_ptr_base::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 - void simple_ptr_base::copy(const simple_ptr_base& data) throw(illegal_copy) - { - alias(data); - make_unique(); - } - -#ifdef STLPLUS_MEMBER_TEMPLATES - - // dynamic cast of underlying pointer to a derived/parent - template - template - simple_ptr_base simple_ptr_base::dyn_cast(void) const - { - simple_ptr_base rtn; - rtn.m_pointer = dynamic_cast(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 - template - simple_ptr_base simple_ptr_base::stat_cast(void) const - { - simple_ptr_base rtn; - rtn.m_pointer = static_cast(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 - template - simple_ptr_base simple_ptr_base::cast(void) const - { - simple_ptr_base 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 - unsigned* simple_ptr_base::_count(void) const - { - return m_count; - } - - template - T* simple_ptr_base::_pointer(void) const - { - return m_pointer; - } - - template - void simple_ptr_base::_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 - +//////////////////////////////////////////////////////////////////////////////// + +// Author: Daniel Milton +// Copyright: (c) Daniel Milton 2002-2009 + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // simple_ptr_base class + //////////////////////////////////////////////////////////////////////////////// + + //////////////////////////////////////////////////////////////////////////////// + // constructors, assignments and destructors + + // create a null pointer + template + simple_ptr_base::simple_ptr_base(void) : + m_pointer(0), + m_count(new unsigned(1)) + { + } + + // create a pointer containing a *copy* of the object pointer + template + simple_ptr_base::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 x(new type(args)) + template + simple_ptr_base::simple_ptr_base(T* data) : + m_pointer(data), + m_count(new unsigned(1)) + { + } + + // copy constructor implements counted referencing - no copy is made + template + simple_ptr_base::simple_ptr_base(const simple_ptr_base& r) : + m_pointer(r.m_pointer), + m_count(r.m_count) + { + increment(); + } + + // assignment operator - required, else the output of GCC suffers segmentation faults + template + simple_ptr_base& simple_ptr_base::operator=(const simple_ptr_base& r) + { + alias(r); + return *this; + } + + // destructor decrements the reference count and delete only when the last reference is destroyed + template + simple_ptr_base::~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 + bool simple_ptr_base::null(void) const + { + return m_pointer==0; + } + + template + bool simple_ptr_base::present(void) const + { + return m_pointer!=0; + } + + template + bool simple_ptr_base::operator!(void) const + { + return m_pointer==0; + } + + template + simple_ptr_base::operator bool(void) const + { + return m_pointer!=0; + } + + ////////////////////////////////////////////////////////////////////////////// + // dereference operators and functions + + template + T& simple_ptr_base::operator*(void) throw(null_dereference) + { + if (!m_pointer) throw null_dereference("null pointer dereferenced in simple_ptr::operator*"); + return *m_pointer; + } + + template + const T& simple_ptr_base::operator*(void) const throw(null_dereference) + { + if (!m_pointer) throw null_dereference("null pointer dereferenced in simple_ptr::operator*"); + return *m_pointer; + } + + template + T* simple_ptr_base::operator->(void) throw(null_dereference) + { + if (!m_pointer) throw null_dereference("null pointer dereferenced in simple_ptr::operator->"); + return m_pointer; + } + + template + const T* simple_ptr_base::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 + void simple_ptr_base::set_value(const T& data) throw(illegal_copy) + { + set(C()(data)); + } + + template + T& simple_ptr_base::value(void) throw(null_dereference) + { + if (!m_pointer) throw null_dereference("null pointer dereferenced in simple_ptr::value"); + return *m_pointer; + } + + template + const T& simple_ptr_base::value(void) const throw(null_dereference) + { + if (!m_pointer) throw null_dereference("null pointer dereferenced in simple_ptr::value"); + return *m_pointer; + } + + template + void simple_ptr_base::set(T* data) + { + unsigned& count = *m_count; + if (count<=1) + delete m_pointer; + else + { + --count; + m_count = new unsigned(1); + } + m_pointer = data; + } + + template + T* simple_ptr_base::pointer(void) + { + return m_pointer; + } + + template + const T* simple_ptr_base::pointer(void) const + { + return m_pointer; + } + + //////////////////////////////////////////////////////////////////////////////// + // functions to manage counted referencing + + template + void simple_ptr_base::increment(void) + { + ++(*m_count); + } + + template + bool simple_ptr_base::decrement(void) + { + unsigned& count = *m_count; + --count; + return count == 0; + } + + // make this an alias of the passed object + template + void simple_ptr_base::alias(const simple_ptr_base& 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 + bool simple_ptr_base::aliases(const simple_ptr_base& r) const + { + return m_count == r.m_count; + } + + template + unsigned simple_ptr_base::alias_count(void) const + { + return *m_count; + } + + template + void simple_ptr_base::clear(void) + { + set(0); + } + + template + void simple_ptr_base::clear_unique(void) + { + set(0); // no difference between clear and clear_unique with the simple_ptr + } + + template + void simple_ptr_base::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 + void simple_ptr_base::copy(const simple_ptr_base& data) throw(illegal_copy) + { + alias(data); + make_unique(); + } + +#ifdef STLPLUS_MEMBER_TEMPLATES + + // dynamic cast of underlying pointer to a derived/parent + template + template + simple_ptr_base simple_ptr_base::dyn_cast(void) const + { + simple_ptr_base rtn; + rtn.m_pointer = dynamic_cast(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 + template + simple_ptr_base simple_ptr_base::stat_cast(void) const + { + simple_ptr_base rtn; + rtn.m_pointer = static_cast(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 + template + simple_ptr_base simple_ptr_base::cast(void) const + { + simple_ptr_base 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 + unsigned* simple_ptr_base::_count(void) const + { + return m_count; + } + + template + T* simple_ptr_base::_pointer(void) const + { + return m_pointer; + } + + template + void simple_ptr_base::_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 + diff --git a/src/stlplus/containers/smart_ptr.hpp b/src/stlplus/containers/smart_ptr.hpp index 2d37e2e..0170771 100644 --- a/src/stlplus/containers/smart_ptr.hpp +++ b/src/stlplus/containers/smart_ptr.hpp @@ -1,222 +1,222 @@ -#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 -#include - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - // internals - - template class smart_ptr_holder; - - //////////////////////////////////////////////////////////////////////////////// - // Base class - //////////////////////////////////////////////////////////////////////////////// - - template - 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 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& r); - - // assignment operator - required, else the output of GCC suffers segmentation faults - smart_ptr_base& operator=(const smart_ptr_base& 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&); - - // 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&) 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&) throw(illegal_copy); - - protected: - smart_ptr_holder* 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* _handle(void) const; - void _make_alias(smart_ptr_holder* handle); - }; - - //////////////////////////////////////////////////////////////////////////////// - // smart_ptr for simple types and classes which have copy constructors - - template - class smart_ptr : public smart_ptr_base > - { - public: - smart_ptr(void) {} - explicit smart_ptr(const T& data) : smart_ptr_base >(data) {} - explicit smart_ptr(T* data) : smart_ptr_base >(data) {} - smart_ptr& operator=(const T& data) {set_value(data); return *this;} - smart_ptr& operator=(T* data) {set(data); return *this;} - ~smart_ptr(void) {} - }; - - //////////////////////////////////////////////////////////////////////////////// - // smart_ptr_clone for polymorphic class hierarchies which have a clone method - - template - class smart_ptr_clone : public smart_ptr_base > - { - public: - smart_ptr_clone(void) {} - explicit smart_ptr_clone(const T& data) : smart_ptr_base >(data) {} - explicit smart_ptr_clone(T* data) : smart_ptr_base >(data) {} - smart_ptr_clone& operator=(const T& data) {set_value(data); return *this;} - smart_ptr_clone& 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 - class smart_ptr_nocopy : public smart_ptr_base > - { - public: - smart_ptr_nocopy(void) {} - explicit smart_ptr_nocopy(const T& data) : smart_ptr_base >(data) {} - explicit smart_ptr_nocopy(T* data) : smart_ptr_base >(data) {} - smart_ptr_nocopy& operator=(const T& data) {set_value(data); return *this;} - smart_ptr_nocopy& operator=(T* data) {set(data); return *this;} - ~smart_ptr_nocopy(void) {} - }; - - //////////////////////////////////////////////////////////////////////////////// - -} // end namespace stlplus - -#include "smart_ptr.tpp" -#endif +#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 +#include + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // internals + + template class smart_ptr_holder; + + //////////////////////////////////////////////////////////////////////////////// + // Base class + //////////////////////////////////////////////////////////////////////////////// + + template + 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 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& r); + + // assignment operator - required, else the output of GCC suffers segmentation faults + smart_ptr_base& operator=(const smart_ptr_base& 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&); + + // 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&) 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&) throw(illegal_copy); + + protected: + smart_ptr_holder* 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* _handle(void) const; + void _make_alias(smart_ptr_holder* handle); + }; + + //////////////////////////////////////////////////////////////////////////////// + // smart_ptr for simple types and classes which have copy constructors + + template + class smart_ptr : public smart_ptr_base > + { + public: + smart_ptr(void) {} + explicit smart_ptr(const T& data) : smart_ptr_base >(data) {} + explicit smart_ptr(T* data) : smart_ptr_base >(data) {} + smart_ptr& operator=(const T& data) {set_value(data); return *this;} + smart_ptr& operator=(T* data) {set(data); return *this;} + ~smart_ptr(void) {} + }; + + //////////////////////////////////////////////////////////////////////////////// + // smart_ptr_clone for polymorphic class hierarchies which have a clone method + + template + class smart_ptr_clone : public smart_ptr_base > + { + public: + smart_ptr_clone(void) {} + explicit smart_ptr_clone(const T& data) : smart_ptr_base >(data) {} + explicit smart_ptr_clone(T* data) : smart_ptr_base >(data) {} + smart_ptr_clone& operator=(const T& data) {set_value(data); return *this;} + smart_ptr_clone& 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 + class smart_ptr_nocopy : public smart_ptr_base > + { + public: + smart_ptr_nocopy(void) {} + explicit smart_ptr_nocopy(const T& data) : smart_ptr_base >(data) {} + explicit smart_ptr_nocopy(T* data) : smart_ptr_base >(data) {} + smart_ptr_nocopy& operator=(const T& data) {set_value(data); return *this;} + smart_ptr_nocopy& operator=(T* data) {set(data); return *this;} + ~smart_ptr_nocopy(void) {} + }; + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus + +#include "smart_ptr.tpp" +#endif diff --git a/src/stlplus/containers/smart_ptr.tpp b/src/stlplus/containers/smart_ptr.tpp index ce72da2..3af0210 100644 --- a/src/stlplus/containers/smart_ptr.tpp +++ b/src/stlplus/containers/smart_ptr.tpp @@ -1,345 +1,345 @@ -//////////////////////////////////////////////////////////////////////////////// - -// 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 - 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 - smart_ptr_base::smart_ptr_base(void) : - m_holder(new smart_ptr_holder) - { - } - - // create a pointer containing a *copy* of the object pointer - template - smart_ptr_base::smart_ptr_base(const T& data) throw(illegal_copy) : - m_holder(new smart_ptr_holder) - { - 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 x(new type(args)) - template - smart_ptr_base::smart_ptr_base(T* data) : - m_holder(new smart_ptr_holder) - { - m_holder->set(data); - } - - // copy constructor implements counted referencing - no copy is made - template - smart_ptr_base::smart_ptr_base(const smart_ptr_base& r) : - m_holder(0) - { - m_holder = r.m_holder; - m_holder->increment(); - } - - // assignment operator - required, else the output of GCC suffers segmentation faults - template - smart_ptr_base& smart_ptr_base::operator=(const smart_ptr_base& r) - { - alias(r); - return *this; - } - - // destructor decrements the reference count and delete only when the last reference is destroyed - template - smart_ptr_base::~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 - bool smart_ptr_base::null(void) const - { - return m_holder->null(); - } - - template - bool smart_ptr_base::present(void) const - { - return !m_holder->null(); - } - - template - bool smart_ptr_base::operator!(void) const - { - return m_holder->null(); - } - - template - smart_ptr_base::operator bool(void) const - { - return !m_holder->null(); - } - - ////////////////////////////////////////////////////////////////////////////// - // dereference operators and functions - - template - T& smart_ptr_base::operator*(void) throw(null_dereference) - { - if (m_holder->null()) throw null_dereference("null pointer dereferenced in smart_ptr::operator*"); - return m_holder->value(); - } - - template - const T& smart_ptr_base::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 - T* smart_ptr_base::operator->(void) throw(null_dereference) - { - if (m_holder->null()) throw null_dereference("null pointer dereferenced in smart_ptr::operator->"); - return m_holder->pointer(); - } - - template - const T* smart_ptr_base::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 - void smart_ptr_base::set_value(const T& data) throw(illegal_copy) - { - m_holder->set(C()(data)); - } - - template - T& smart_ptr_base::value(void) throw(null_dereference) - { - if (m_holder->null()) throw null_dereference("null pointer dereferenced in smart_ptr::value"); - return m_holder->value(); - } - - template - const T& smart_ptr_base::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 - void smart_ptr_base::set(T* data) - { - m_holder->set(data); - } - - template - T* smart_ptr_base::pointer(void) - { - return m_holder->pointer(); - } - - template - const T* smart_ptr_base::pointer(void) const - { - return m_holder->pointer(); - } - - //////////////////////////////////////////////////////////////////////////////// - // functions to manage counted referencing - - // make this an alias of the passed object - template - void smart_ptr_base::alias(const smart_ptr_base& r) - { - _make_alias(r.m_holder); - } - - template - bool smart_ptr_base::aliases(const smart_ptr_base& r) const - { - return m_holder == r.m_holder; - } - - template - unsigned smart_ptr_base::alias_count(void) const - { - return m_holder->count(); - } - - template - void smart_ptr_base::clear(void) - { - m_holder->clear(); - } - - template - void smart_ptr_base::clear_unique(void) - { - if (m_holder->count() == 1) - m_holder->clear(); - else - { - m_holder->decrement(); - m_holder = 0; - m_holder = new smart_ptr_holder; - } - } - - template - void smart_ptr_base::make_unique(void) throw(illegal_copy) - { - if (m_holder->count() > 1) - { - smart_ptr_holder* old_holder = m_holder; - m_holder->decrement(); - m_holder = 0; - m_holder = new smart_ptr_holder; - if (old_holder->pointer()) - m_holder->set(C()(old_holder->value())); - } - } - - template - void smart_ptr_base::copy(const smart_ptr_base& data) throw(illegal_copy) - { - alias(data); - make_unique(); - } - - // internal function for distinguishing unique smart_ptr objects - // used for example in persistence routines - - template - smart_ptr_holder* smart_ptr_base::_handle(void) const - { - return m_holder; - } - - template - void smart_ptr_base::_make_alias(smart_ptr_holder* 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 - +//////////////////////////////////////////////////////////////////////////////// + +// 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 + 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 + smart_ptr_base::smart_ptr_base(void) : + m_holder(new smart_ptr_holder) + { + } + + // create a pointer containing a *copy* of the object pointer + template + smart_ptr_base::smart_ptr_base(const T& data) throw(illegal_copy) : + m_holder(new smart_ptr_holder) + { + 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 x(new type(args)) + template + smart_ptr_base::smart_ptr_base(T* data) : + m_holder(new smart_ptr_holder) + { + m_holder->set(data); + } + + // copy constructor implements counted referencing - no copy is made + template + smart_ptr_base::smart_ptr_base(const smart_ptr_base& r) : + m_holder(0) + { + m_holder = r.m_holder; + m_holder->increment(); + } + + // assignment operator - required, else the output of GCC suffers segmentation faults + template + smart_ptr_base& smart_ptr_base::operator=(const smart_ptr_base& r) + { + alias(r); + return *this; + } + + // destructor decrements the reference count and delete only when the last reference is destroyed + template + smart_ptr_base::~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 + bool smart_ptr_base::null(void) const + { + return m_holder->null(); + } + + template + bool smart_ptr_base::present(void) const + { + return !m_holder->null(); + } + + template + bool smart_ptr_base::operator!(void) const + { + return m_holder->null(); + } + + template + smart_ptr_base::operator bool(void) const + { + return !m_holder->null(); + } + + ////////////////////////////////////////////////////////////////////////////// + // dereference operators and functions + + template + T& smart_ptr_base::operator*(void) throw(null_dereference) + { + if (m_holder->null()) throw null_dereference("null pointer dereferenced in smart_ptr::operator*"); + return m_holder->value(); + } + + template + const T& smart_ptr_base::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 + T* smart_ptr_base::operator->(void) throw(null_dereference) + { + if (m_holder->null()) throw null_dereference("null pointer dereferenced in smart_ptr::operator->"); + return m_holder->pointer(); + } + + template + const T* smart_ptr_base::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 + void smart_ptr_base::set_value(const T& data) throw(illegal_copy) + { + m_holder->set(C()(data)); + } + + template + T& smart_ptr_base::value(void) throw(null_dereference) + { + if (m_holder->null()) throw null_dereference("null pointer dereferenced in smart_ptr::value"); + return m_holder->value(); + } + + template + const T& smart_ptr_base::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 + void smart_ptr_base::set(T* data) + { + m_holder->set(data); + } + + template + T* smart_ptr_base::pointer(void) + { + return m_holder->pointer(); + } + + template + const T* smart_ptr_base::pointer(void) const + { + return m_holder->pointer(); + } + + //////////////////////////////////////////////////////////////////////////////// + // functions to manage counted referencing + + // make this an alias of the passed object + template + void smart_ptr_base::alias(const smart_ptr_base& r) + { + _make_alias(r.m_holder); + } + + template + bool smart_ptr_base::aliases(const smart_ptr_base& r) const + { + return m_holder == r.m_holder; + } + + template + unsigned smart_ptr_base::alias_count(void) const + { + return m_holder->count(); + } + + template + void smart_ptr_base::clear(void) + { + m_holder->clear(); + } + + template + void smart_ptr_base::clear_unique(void) + { + if (m_holder->count() == 1) + m_holder->clear(); + else + { + m_holder->decrement(); + m_holder = 0; + m_holder = new smart_ptr_holder; + } + } + + template + void smart_ptr_base::make_unique(void) throw(illegal_copy) + { + if (m_holder->count() > 1) + { + smart_ptr_holder* old_holder = m_holder; + m_holder->decrement(); + m_holder = 0; + m_holder = new smart_ptr_holder; + if (old_holder->pointer()) + m_holder->set(C()(old_holder->value())); + } + } + + template + void smart_ptr_base::copy(const smart_ptr_base& data) throw(illegal_copy) + { + alias(data); + make_unique(); + } + + // internal function for distinguishing unique smart_ptr objects + // used for example in persistence routines + + template + smart_ptr_holder* smart_ptr_base::_handle(void) const + { + return m_holder; + } + + template + void smart_ptr_base::_make_alias(smart_ptr_holder* 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 + diff --git a/src/stlplus/containers/triple.hpp b/src/stlplus/containers/triple.hpp index 9546cce..bf501f5 100644 --- a/src/stlplus/containers/triple.hpp +++ b/src/stlplus/containers/triple.hpp @@ -1,54 +1,54 @@ -#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 - 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& t2); - }; - - //////////////////////////////////////////////////////////////////////////////// - // creation - - template - triple make_triple(const T1& first, const T2& second, const T3& third); - - //////////////////////////////////////////////////////////////////////////////// - // comparison - - template - bool operator == (const triple& left, const triple& right); - - //////////////////////////////////////////////////////////////////////////////// - -} // end namespace stlplus - -#include "triple.tpp" -#endif +#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 + 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& t2); + }; + + //////////////////////////////////////////////////////////////////////////////// + // creation + + template + triple make_triple(const T1& first, const T2& second, const T3& third); + + //////////////////////////////////////////////////////////////////////////////// + // comparison + + template + bool operator == (const triple& left, const triple& right); + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus + +#include "triple.tpp" +#endif diff --git a/src/stlplus/containers/triple.tpp b/src/stlplus/containers/triple.tpp index 60e94aa..3c1fa30 100644 --- a/src/stlplus/containers/triple.tpp +++ b/src/stlplus/containers/triple.tpp @@ -1,56 +1,56 @@ -//////////////////////////////////////////////////////////////////////////////// - -// 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 - triple::triple(void) : - first(), second(), third() - { - } - - template - triple::triple(const T1& p1, const T2& p2, const T3& p3) : - first(p1), second(p2), third(p3) - { - } - - template - triple::triple(const triple& t2) : - first(t2.first), second(t2.second), third(t2.third) - { - } - - //////////////////////////////////////////////////////////////////////////////// - // creation - - template - triple make_triple(const T1& first, const T2& second, const T3& third) - { - return triple(first,second,third); - } - - //////////////////////////////////////////////////////////////////////////////// - // comparison - - template - bool operator == (const triple& left, const triple& 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 - +//////////////////////////////////////////////////////////////////////////////// + +// 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 + triple::triple(void) : + first(), second(), third() + { + } + + template + triple::triple(const T1& p1, const T2& p2, const T3& p3) : + first(p1), second(p2), third(p3) + { + } + + template + triple::triple(const triple& t2) : + first(t2.first), second(t2.second), third(t2.third) + { + } + + //////////////////////////////////////////////////////////////////////////////// + // creation + + template + triple make_triple(const T1& first, const T2& second, const T3& third) + { + return triple(first,second,third); + } + + //////////////////////////////////////////////////////////////////////////////// + // comparison + + template + bool operator == (const triple& left, const triple& 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 + diff --git a/src/stlplus/messages/stlplus_messages.txt b/src/stlplus/messages/stlplus_messages.txt deleted file mode 100644 index 548a999..0000000 --- a/src/stlplus/messages/stlplus_messages.txt +++ /dev/null @@ -1,14 +0,0 @@ -# messages required by the CLI parser (see cli_parser.hpp) -CLI_VALUE_MISSING option @0 requires a value - end of line was reached instead -CLI_UNRECOGNISED_OPTION @0 is not a recognised option for this command -CLI_NO_VALUES argument @0 is invalid - this program doesn't take command-line arguments -CLI_USAGE_PROGRAM usage:\n\t@0 { arguments } -CLI_USAGE_DEFINITIONS arguments: -CLI_USAGE_VALUES values: -CLI_USAGE_VALUE_RESULT \t@0 : from @1 -CLI_USAGE_SWITCH_RESULT \t-@0 : from @1 -CLI_USAGE_OPTION_RESULT \t-@0 @1 : from @2 -CLI_INI_HEADER configuration files: -CLI_INI_FILE_PRESENT \t@0 -CLI_INI_FILE_ABSENT \t@0 (not found) -CLI_INI_VARIABLE unknown variable "@0" found in Ini file diff --git a/src/stlplus/persistence/persistence.hpp b/src/stlplus/persistence/persistence.hpp deleted file mode 100644 index 6a47838..0000000 --- a/src/stlplus/persistence/persistence.hpp +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef STLPLUS_PERSISTENCE -#define STLPLUS_PERSISTENCE -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Header that includes all the persistence routines in one go - -//////////////////////////////////////////////////////////////////////////////// - -#include "persistent_contexts.hpp" -#include "persistent_shortcuts.hpp" -#include "persistent_basic.hpp" -#include "persistent_pointers.hpp" -#include "persistent_stl.hpp" -#include "persistent_stlplus.hpp" - -//////////////////////////////////////////////////////////////////////////////// -#endif diff --git a/src/stlplus/persistence/persistence_fixes.hpp b/src/stlplus/persistence/persistence_fixes.hpp deleted file mode 100644 index d540ade..0000000 --- a/src/stlplus/persistence/persistence_fixes.hpp +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef STLPLUS_PERSISTENCE_FIXES -#define STLPLUS_PERSISTENCE_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 - -//////////////////////////////////////////////////////////////////////////////// - -//////////////////////////////////////////////////////////////////////////////// -// 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) -// 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 - -//////////////////////////////////////////////////////////////////////////////// -#endif diff --git a/src/stlplus/persistence/persistent.hpp b/src/stlplus/persistence/persistent.hpp deleted file mode 100644 index 7ddfe4e..0000000 --- a/src/stlplus/persistence/persistent.hpp +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef STLPLUS_PERSISTENT -#define STLPLUS_PERSISTENT -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Interface class inherited by classes using the interface approach to polymorphism - -//////////////////////////////////////////////////////////////////////////////// - -#include "persistence_fixes.hpp" -#include "persistent_exceptions.hpp" - -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - - class dump_context; - class restore_context; - - class persistent - { - public: - virtual void dump(dump_context&) const throw(persistent_dump_failed) = 0; - virtual void restore(restore_context&) throw(persistent_restore_failed) = 0; - virtual persistent* clone(void) const = 0; - virtual ~persistent(void) {} - }; - -} // end namespace stlplus - - //////////////////////////////////////////////////////////////////////////////// -#endif diff --git a/src/stlplus/persistence/persistent_basic.hpp b/src/stlplus/persistence/persistent_basic.hpp deleted file mode 100644 index 2ffc30f..0000000 --- a/src/stlplus/persistence/persistent_basic.hpp +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef STLPLUS_PERSISTENT_BASIC -#define STLPLUS_PERSISTENT_BASIC -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Persistence of basic types - -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// - -#include "persistent_bool.hpp" -#include "persistent_cstring.hpp" -#include "persistent_enum.hpp" -#include "persistent_float.hpp" -#include "persistent_int.hpp" - -//////////////////////////////////////////////////////////////////////////////// -#endif diff --git a/src/stlplus/persistence/persistent_bitset.hpp b/src/stlplus/persistence/persistent_bitset.hpp deleted file mode 100644 index 3f684e4..0000000 --- a/src/stlplus/persistence/persistent_bitset.hpp +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef STLPLUS_PERSISTENT_BITSET -#define STLPLUS_PERSISTENT_BITSET -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Persistence of STL bitset - -//////////////////////////////////////////////////////////////////////////////// - -#include "persistence_fixes.hpp" -#include "persistent_contexts.hpp" -#include - -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - - template - void dump_bitset(dump_context&, const std::bitset& data) throw(persistent_dump_failed); - - template - void restore_bitset(restore_context&, std::bitset& data) throw(persistent_restore_failed); - -} // end namespace stlplus - - //////////////////////////////////////////////////////////////////////////////// -#include "persistent_bitset.tpp" -#endif diff --git a/src/stlplus/persistence/persistent_bitset.tpp b/src/stlplus/persistence/persistent_bitset.tpp deleted file mode 100644 index e6b4d54..0000000 --- a/src/stlplus/persistence/persistent_bitset.tpp +++ /dev/null @@ -1,61 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// -#include "persistent_int.hpp" - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - // format: data msB first, packed into bytes with lowest index at the byte's lsb - - // Note: the interface does not provide access to the internal storage and yet - // to be efficient the std::bitset must be packed as bytes. Thus I have to do it the - // hard way. - - template - void dump_bitset(dump_context& context, const std::bitset& data) - throw(persistent_dump_failed) - { - size_t bits = data.size(); - size_t bytes = (bits+7)/8; - for (size_t B = bytes; B--; ) - { - unsigned char ch = 0; - for (size_t b = 0; b < 8; b++) - { - size_t bit = B*8+b; - if (bit < bits && data.test(bit)) - ch |= (0x01 << b); - } - dump_unsigned_char(context,ch); - } - } - - template - void restore_bitset(restore_context& context, std::bitset& data) - throw(persistent_restore_failed) - { - size_t bits = data.size(); - size_t bytes = (bits+7)/8; - for (size_t B = bytes; B--; ) - { - unsigned char ch = 0; - restore_unsigned_char(context,ch); - for (size_t b = 0; b < 8; b++) - { - size_t bit = B*8+b; - if (bit >= bits) break; - data.set(bit, ch & (0x01 << b) ? true : false); - } - } - } - - //////////////////////////////////////////////////////////////////////////////// - -} // end namespace stlplus diff --git a/src/stlplus/persistence/persistent_bool.cpp b/src/stlplus/persistence/persistent_bool.cpp deleted file mode 100644 index dde1828..0000000 --- a/src/stlplus/persistence/persistent_bool.cpp +++ /dev/null @@ -1,24 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// -#include "persistent_bool.hpp" - -//////////////////////////////////////////////////////////////////////////////// - -// bool is dumped and restored as an unsigned char -void stlplus::dump_bool(stlplus::dump_context& context, const bool& data) throw(stlplus::persistent_dump_failed) -{ - context.put((unsigned char)data); -} - -void stlplus::restore_bool(restore_context& context, bool& data) throw(stlplus::persistent_restore_failed) -{ - data = context.get() != 0; -} - -//////////////////////////////////////////////////////////////////////////////// diff --git a/src/stlplus/persistence/persistent_bool.hpp b/src/stlplus/persistence/persistent_bool.hpp deleted file mode 100644 index 5fbe237..0000000 --- a/src/stlplus/persistence/persistent_bool.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef STLPLUS_PERSISTENT_BOOL -#define STLPLUS_PERSISTENT_BOOL -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Persistence of bool - -//////////////////////////////////////////////////////////////////////////////// - -#include "persistence_fixes.hpp" -#include "persistent_contexts.hpp" - -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - - void dump_bool(dump_context&, const bool& data) throw(persistent_dump_failed); - void restore_bool(restore_context&, bool& data) throw(persistent_restore_failed); - -} // end namespace stlplus - -#endif diff --git a/src/stlplus/persistence/persistent_callback.hpp b/src/stlplus/persistence/persistent_callback.hpp deleted file mode 100644 index 034f43e..0000000 --- a/src/stlplus/persistence/persistent_callback.hpp +++ /dev/null @@ -1,53 +0,0 @@ -#ifndef STLPLUS_PERSISTENT_CALLBACK -#define STLPLUS_PERSISTENT_CALLBACK -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Persistence for pointers to polymorphic classes using the callback approach. - -// This works on a set of classes. Each subclass has a set of callback -// (non-method) functions that enable create/dump/restore operations. Each -// subclass must be registered with the persistence dump/restore context so -// that the system knows how to handle it. - -// This approach is suited to classes that cannot be modified to add -// persistence methods. See persistent_interface for a more C++-like way of -// handling polymorphism. - -// Objects are always dumped/restored as pointers to the superclass T. - -// Multiple pointers to the same object are handled in the same way as for -// simple pointers - -// Only classes registered with the context can be dumped and restored as -// polymorphic types - see dump_context::register_callback and -// restore_context::register_callback. Attempting to use any unrecognised class -// will throw an exception. - -//////////////////////////////////////////////////////////////////////////////// - -#include "persistence_fixes.hpp" -#include "persistent_contexts.hpp" - -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - - template - void dump_callback(dump_context&, const T* const data) - throw(persistent_dump_failed); - - template - void restore_callback(restore_context&, T*& data) - throw(persistent_restore_failed); - -} // end namespace stlplus - - //////////////////////////////////////////////////////////////////////////////// -#include "persistent_callback.tpp" -#endif diff --git a/src/stlplus/persistence/persistent_callback.tpp b/src/stlplus/persistence/persistent_callback.tpp deleted file mode 100644 index 0773dad..0000000 --- a/src/stlplus/persistence/persistent_callback.tpp +++ /dev/null @@ -1,100 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Polymorphous classes using the callback approach - -// format: magic [ key data ] - -//////////////////////////////////////////////////////////////////////////////// -#include "persistent_int.hpp" - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - - template - void dump_callback(dump_context& context, const T* const data) - throw(persistent_dump_failed) - { - try - { - // register the address and get the magic key for it - std::pair mapping = context.pointer_map(data); - dump_unsigned(context,mapping.second); - // if the address is null, then that is all that we need to do - // however, if it is non-null and this is the first sight of the address, dump the contents - if (data && !mapping.first) - { - // callback method - get the callback data and perform the dump - // this will throw persistent_illegal_type if not recognised, thus the try block - dump_context::callback_data callback = context.lookup_callback(typeid(*data)); - // dump the magic key for the type - dump_unsigned(context, callback.first); - // now call the callback that dumps the subclass - callback.second(context,data); - } - } - catch (const persistent_illegal_type& except) - { - // convert this to a simpler dump failed exception - throw persistent_dump_failed(except.what()); - } - } - - //////////////////////////////////////////////////////////////////////////////// - - template - void restore_callback(restore_context& context, T*& data) - throw(persistent_restore_failed) - { - try - { - // first delete any previous object pointed to since the restore creates the object of the right subclass - if (data) - { - delete data; - data = 0; - } - // get the magic key - unsigned magic = 0; - restore_unsigned(context,magic); - // now lookup the magic key to see if this pointer has already been restored - // null pointers are always flagged as already restored - std::pair address = context.pointer_map(magic); - if (address.first) - { - // seen before, so simply map it to the existing address - data = (T*)address.second; - } - else - { - // now restore the magic key that denotes the particular subclass - unsigned key = 0; - restore_unsigned(context, key); - // callback approach - // call the create callback to create an object of the right type - // then call the restore callback to get the contents - // this will throw persistent_illegal_type if not recognised - this is caught below - restore_context::callback_data callbacks = context.lookup_callback(key); - data = (T*)callbacks.first(); - // add this pointer to the set of already seen objects - // note that the address is mapped before it is dumped so that self-referential structures dump correctly - context.pointer_add(magic,data); - callbacks.second(context,data); - } - } - catch (const persistent_illegal_type& exception) - { - // convert this to a simpler dump failed exception - throw persistent_restore_failed(exception.what()); - } - } - - //////////////////////////////////////////////////////////////////////////////// - -} // end namespace stlplus diff --git a/src/stlplus/persistence/persistent_complex.hpp b/src/stlplus/persistence/persistent_complex.hpp deleted file mode 100644 index 4c02215..0000000 --- a/src/stlplus/persistence/persistent_complex.hpp +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef STLPLUS_PERSISTENT_COMPLEX -#define STLPLUS_PERSISTENT_COMPLEX -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Set of persistence routines for the STL classes - -//////////////////////////////////////////////////////////////////////////////// - -#include "persistence_fixes.hpp" -#include "persistent_contexts.hpp" -#include - -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - - template - void dump_complex(dump_context&, const std::complex& data, D dump_fn) throw(persistent_dump_failed); - - template - void restore_complex(restore_context&, std::complex& data, R restore_fn) throw(persistent_restore_failed); - -} // end namespace stlplus - - //////////////////////////////////////////////////////////////////////////////// -#include "persistent_complex.tpp" -#endif diff --git a/src/stlplus/persistence/persistent_complex.tpp b/src/stlplus/persistence/persistent_complex.tpp deleted file mode 100644 index 7db9bc4..0000000 --- a/src/stlplus/persistence/persistent_complex.tpp +++ /dev/null @@ -1,35 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - - template - void dump_complex(dump_context& context, const std::complex& data, D dump_fn) - throw(persistent_dump_failed) - { - dump_fn(context,data.real()); - dump_fn(context,data.imag()); - } - - template - void restore_complex(restore_context& context, std::complex& data, R restore_fn) - throw(persistent_restore_failed) - { - T re, im; - restore_fn(context,re); - restore_fn(context,im); - data = std::complex(re, im); - } - - //////////////////////////////////////////////////////////////////////////////// - -} // end namespace stlplus diff --git a/src/stlplus/persistence/persistent_contexts.cpp b/src/stlplus/persistence/persistent_contexts.cpp deleted file mode 100644 index 008fa9f..0000000 --- a/src/stlplus/persistence/persistent_contexts.cpp +++ /dev/null @@ -1,434 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// -#include "persistent_contexts.hpp" -#include "persistent.hpp" -#include -#include -#include - -namespace stlplus -{ - - - //////////////////////////////////////////////////////////////////////////////// - // File format version - - // This relates to the layout of basic types - if I change the file layout of, - // say, int or vector, then this will change - - // Early versions of the persistence routines did not have this - they are no longer supported - // - Change from version 1 to 2: changed the persistent representation of inf - - unsigned char PersistentVersion = 2; - - //////////////////////////////////////////////////////////////////////////////// - // avoid creating dependencies on other libraries - - static std::string to_string(int number) - { - // use sprintf in a very controlled way that cannot overrun - char* buffer = new char[50]; - sprintf(buffer, "%i", number); - std::string result = buffer; - delete buffer; - return result; - } - - static bool little_endian(void) - { - // TODO - find a compile-time way of doing this - int sample = 1; - char* sample_bytes = (char*)&sample; - return sample_bytes[0] != 0; - } - - //////////////////////////////////////////////////////////////////////////////// - // dump context classes - //////////////////////////////////////////////////////////////////////////////// - - class dump_context_body - { - public: - typedef std::map magic_map; - typedef std::map callback_map; - typedef std::map interface_map; - - unsigned m_max_key; - unsigned char m_version; - bool m_little_endian; - std::ostream* m_device; - magic_map m_pointers; - callback_map m_callbacks; - interface_map m_interfaces; - - dump_context_body(std::ostream& device, unsigned char version) throw(persistent_dump_failed) : - m_max_key(0), m_version(version), m_little_endian(stlplus::little_endian()), m_device(&device) - { - // write the version number as a single byte - put(version); - // map a null pointer onto magic number zero - m_pointers[0] = 0; - // test whether the version number is supported - if (m_version != 1 && m_version != 2) - throw persistent_dump_failed(std::string("wrong version: ") + to_string(m_version)); - } - - void put(unsigned char data) throw(persistent_dump_failed) - { - if (!m_device->put(data)) - throw persistent_dump_failed(std::string("output device error")); - } - - const std::ostream& device(void) const - { - return *m_device; - } - - unsigned char version(void) const - { - return m_version; - } - - bool little_endian(void) const - { - return m_little_endian; - } - - std::pair pointer_map(const void* const pointer) - { - magic_map::iterator found = m_pointers.find(pointer); - if (found == m_pointers.end()) - { - // add a new mapping - unsigned magic = m_pointers.size(); - m_pointers[pointer] = magic; - return std::pair(false,magic); - } - // return the old mapping - return std::pair(true,found->second); - } - - unsigned register_callback(const std::type_info& info, dump_context::dump_callback callback) - { - std::string key = info.name(); - unsigned data = ++m_max_key; - m_callbacks[key] = std::make_pair(data,callback); - return data; - } - - bool is_callback(const std::type_info& info) const - { - return m_callbacks.find(info.name()) != m_callbacks.end(); - } - - dump_context::callback_data lookup_callback(const std::type_info& info) const throw(persistent_illegal_type) - { - std::string key = info.name(); - callback_map::const_iterator found = m_callbacks.find(key); - if (found == m_callbacks.end()) - throw persistent_illegal_type(key); - return found->second; - } - - unsigned register_interface(const std::type_info& info) - { - std::string key = info.name(); - unsigned data = ++m_max_key; - m_interfaces[key] = data; - return data; - } - - bool is_interface(const std::type_info& info) const - { - return m_interfaces.find(info.name()) != m_interfaces.end(); - } - - unsigned lookup_interface(const std::type_info& info) const throw(persistent_illegal_type) - { - std::string key = info.name(); - interface_map::const_iterator found = m_interfaces.find(key); - if (found == m_interfaces.end()) - throw persistent_illegal_type(key); - return found->second; - } - }; - - //////////////////////////////////////////////////////////////////////////////// - - dump_context::dump_context(std::ostream& device, unsigned char version) throw(persistent_dump_failed) : m_body(0) - { - m_body = new dump_context_body(device,version); - } - - dump_context::~dump_context(void) - { - delete m_body; - } - - void dump_context::put(unsigned char data) throw(persistent_dump_failed) - { - m_body->put(data); - } - - const std::ostream& dump_context::device(void) const - { - return m_body->device(); - } - - unsigned char dump_context::version(void) const - { - return m_body->version(); - } - - bool dump_context::little_endian(void) const - { - return m_body->little_endian(); - } - - std::pair dump_context::pointer_map(const void* const pointer) - { - return m_body->pointer_map(pointer); - } - - unsigned dump_context::register_callback(const std::type_info& info, dump_context::dump_callback callback) - { - return m_body->register_callback(info,callback); - } - - bool dump_context::is_callback(const std::type_info& info) const - { - return m_body->is_callback(info); - } - - dump_context::callback_data dump_context::lookup_callback(const std::type_info& info) const throw(persistent_illegal_type) - { - return m_body->lookup_callback(info); - } - - unsigned dump_context::register_interface(const std::type_info& info) - { - return m_body->register_interface(info); - } - - bool dump_context::is_interface(const std::type_info& info) const - { - return m_body->is_interface(info); - } - - unsigned dump_context::lookup_interface(const std::type_info& info) const throw(persistent_illegal_type) - { - return m_body->lookup_interface(info); - } - - void dump_context::register_all(dump_context::installer installer) - { - if (installer) installer(*this); - } - - //////////////////////////////////////////////////////////////////////////////// - // restore context classes - //////////////////////////////////////////////////////////////////////////////// - - class restore_context_body - { - public: - typedef persistent* persistent_ptr; - typedef std::map magic_map; - typedef std::map callback_map; - typedef std::map interface_map; - - unsigned m_max_key; - unsigned char m_version; - bool m_little_endian; - std::istream* m_device; - magic_map m_pointers; - callback_map m_callbacks; - interface_map m_interfaces; - - restore_context_body(std::istream& device) throw(persistent_restore_failed) : - m_max_key(0), m_little_endian(stlplus::little_endian()), m_device(&device) - { - // map a null pointer onto magic number zero - m_pointers[0] = 0; - // get the dump version and see if we support it - m_version = (unsigned char)get(); - if (m_version != 1 && m_version != 2) - throw persistent_restore_failed(std::string("wrong version: ") + to_string(m_version)); - } - - ~restore_context_body(void) - { - // need to delete all interfaces - // I used to use smart_ptr_clone for storing them but I want to disconnect as many dependencies as possible - for (unsigned i = 0; i < m_interfaces.size(); i++) - delete m_interfaces[i]; - } - - const std::istream& device(void) const - { - return *m_device; - } - - unsigned char version(void) const - { - return m_version; - } - - bool little_endian(void) const - { - return m_little_endian; - } - - int get(void) throw(persistent_restore_failed) - { - int result = m_device->get(); - if (!m_device->good()) - throw persistent_restore_failed(std::string("device error or premature end of file")); - return result; - } - - std::pair pointer_map(unsigned magic) - { - magic_map::iterator found = m_pointers.find(magic); - if (found == m_pointers.end()) - { - // this magic number has never been seen before - return std::pair(false,0); - } - return std::pair(true,found->second); - } - - void pointer_add(unsigned magic, void* new_pointer) - { - m_pointers[magic] = new_pointer; - } - - unsigned register_callback(restore_context::create_callback create, restore_context::restore_callback restore) - { - unsigned key = ++m_max_key; - m_callbacks[key] = std::make_pair(create,restore); - return key; - } - - bool is_callback(unsigned key) const - { - return m_callbacks.find(key) != m_callbacks.end(); - } - - restore_context::callback_data lookup_callback(unsigned key) const throw(persistent_illegal_type) - { - callback_map::const_iterator found = m_callbacks.find(key); - if (found == m_callbacks.end()) - throw persistent_illegal_type(key); - return found->second; - } - - unsigned register_interface(persistent* sample) - { - unsigned key = ++m_max_key; - m_interfaces[key] = sample; - return key; - } - - bool is_interface(unsigned key) const - { - return m_interfaces.find(key) != m_interfaces.end(); - } - - persistent* lookup_interface(unsigned key) const throw(persistent_illegal_type) - { - interface_map::const_iterator found = m_interfaces.find(key); - if (found == m_interfaces.end()) - throw persistent_illegal_type(key); - return found->second; - } - }; - - //////////////////////////////////////////////////////////////////////////////// - - restore_context::restore_context(std::istream& device) throw(persistent_restore_failed) : - m_body(0) - { - m_body = new restore_context_body(device); - } - - restore_context::~restore_context(void) - { - delete m_body; - } - - const std::istream& restore_context::device(void) const - { - return m_body->device(); - } - - unsigned char restore_context::version(void) const - { - return m_body->version(); - } - - bool restore_context::little_endian(void) const - { - return m_body->little_endian(); - } - - int restore_context::get(void) throw(persistent_restore_failed) - { - return m_body->get(); - } - - std::pair restore_context::pointer_map(unsigned magic) - { - return m_body->pointer_map(magic); - } - - void restore_context::pointer_add(unsigned magic, void* new_pointer) - { - m_body->pointer_add(magic,new_pointer); - } - - unsigned restore_context::register_callback(restore_context::create_callback create, restore_context::restore_callback restore) - { - return m_body->register_callback(create,restore); - } - - bool restore_context::is_callback(unsigned key) const - { - return m_body->is_callback(key); - } - - restore_context::callback_data restore_context::lookup_callback(unsigned key) const throw(persistent_illegal_type) - { - return m_body->lookup_callback(key); - } - - unsigned restore_context::register_interface(persistent* sample) - { - return m_body->register_interface(sample); - } - - bool restore_context::is_interface(unsigned key) const - { - return m_body->is_interface(key); - } - - persistent* restore_context::lookup_interface(unsigned key) const throw(persistent_illegal_type) - { - return m_body->lookup_interface(key); - } - - void restore_context::register_all(restore_context::installer installer) - { - if (installer) installer(*this); - } - - //////////////////////////////////////////////////////////////////////////////// - -} // end namespace stlplus - diff --git a/src/stlplus/persistence/persistent_contexts.hpp b/src/stlplus/persistence/persistent_contexts.hpp deleted file mode 100644 index dfef17b..0000000 --- a/src/stlplus/persistence/persistent_contexts.hpp +++ /dev/null @@ -1,156 +0,0 @@ -#ifndef STLPLUS_PERSISTENT_CONTEXTS -#define STLPLUS_PERSISTENT_CONTEXTS -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Core context classes used to control the persistent dump/restore operations - -//////////////////////////////////////////////////////////////////////////////// - -#include "persistence_fixes.hpp" -#include "persistent.hpp" -#include -#include -#include - -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - // Internals - - class dump_context_body; - class restore_context_body; - - //////////////////////////////////////////////////////////////////////////////// - // The format version number currently supported - //////////////////////////////////////////////////////////////////////////////// - - extern unsigned char PersistentVersion; - - //////////////////////////////////////////////////////////////////////////////// - // dump_context controls the formatting of a persistent dump - //////////////////////////////////////////////////////////////////////////////// - - class dump_context - { - friend class persistent; - public: - ////////////////////////////////////////////////////////////////////////////// - - // device must be in binary mode - dump_context(std::ostream& device, unsigned char version = PersistentVersion) throw(persistent_dump_failed); - ~dump_context(void); - - // low level output used to dump a byte - void put(unsigned char data) throw(persistent_dump_failed); - - // access the device, for example to check the error status - const std::ostream& device(void) const; - - // recover the version number of the dumped output - unsigned char version(void) const; - - // test whether the current platform uses little-endian or big-endian addressing of bytes - // this is used in dump/restore of integers and is exported so that other routines can use it - bool little_endian(void) const; - - // Assist functions for Pointers - // the return pair value is a flag saying whether this is a new pointer and the magic key to dump to file - std::pair pointer_map(const void* const pointer); - - // Assist functions for Polymorphous classes (i.e. subclasses) using callback approach - typedef void (*dump_callback)(dump_context&,const void*); - unsigned register_callback(const std::type_info& info, dump_callback); - bool is_callback(const std::type_info& info) const; - typedef std::pair callback_data; - callback_data lookup_callback(const std::type_info&) const throw(persistent_illegal_type); - - // Assist functions for Polymorphous classes (i.e. subclasses) using interface approach - unsigned register_interface(const std::type_info& info); - bool is_interface(const std::type_info& info) const; - unsigned lookup_interface(const std::type_info&) const throw(persistent_illegal_type); - - // Register all Polymorphous classes using either approach by calling an installer callback - typedef void (*installer)(dump_context&); - void register_all(installer); - - private: - friend class dump_context_body; - dump_context_body* m_body; - - // disallow copying by making assignment and copy constructor private - dump_context(const dump_context&); - dump_context& operator=(const dump_context&); - }; - - //////////////////////////////////////////////////////////////////////////////// - // restore_context controls the reading of the persistent data during a restore - - class restore_context - { - friend class persistent; - public: - ////////////////////////////////////////////////////////////////////////////// - - // device must be in binary mode - restore_context(std::istream& device) throw(persistent_restore_failed); - ~restore_context(void); - - // low level input used to restore a byte - int get(void) throw(persistent_restore_failed); - - // access the device, for example to check the error status - const std::istream& device(void) const; - - // access the version number of the input being restored - unsigned char version(void) const; - - // test whether the current platform uses little-endian or big-endian addressing of bytes - // this is used in dump/restore of integers - bool little_endian(void) const; - - // Assist functions for Pointers - std::pair pointer_map(unsigned magic); - void pointer_add(unsigned magic, void* new_pointer); - - // Assist functions for Polymorphous classes using the callback approach - typedef void* (*create_callback)(void); - typedef void (*restore_callback)(restore_context&,void*); - unsigned register_callback(create_callback,restore_callback); - bool is_callback(unsigned) const; - typedef std::pair callback_data; - callback_data lookup_callback(unsigned) const throw(persistent_illegal_type); - - // Assist functions for Polymorphous classes using the interface approach - unsigned register_interface(persistent*); - bool is_interface(unsigned) const; - persistent* lookup_interface(unsigned) const throw(persistent_illegal_type); - - // Register all Polymorphous classes using either approach by calling an installer callback - typedef void (*installer)(restore_context&); - void register_all(installer); - - private: - friend class restore_context_body; - restore_context_body* m_body; - - typedef std::pair interface_data; - - // disallow copying by making assignment and copy constructor private - restore_context(const restore_context&); - restore_context& operator=(const restore_context&); - }; - - //////////////////////////////////////////////////////////////////////////////// - -} // end namespace stlplus - - //////////////////////////////////////////////////////////////////////////////// -#endif diff --git a/src/stlplus/persistence/persistent_cstring.cpp b/src/stlplus/persistence/persistent_cstring.cpp deleted file mode 100644 index a829b9a..0000000 --- a/src/stlplus/persistence/persistent_cstring.cpp +++ /dev/null @@ -1,63 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// - -#include "persistent_cstring.hpp" -#include "persistent_int.hpp" -#include - -//////////////////////////////////////////////////////////////////////////////// -// Null-terminated char arrays -// Format: address [ size data ] - -void stlplus::dump_cstring(stlplus::dump_context& context, const char* data) throw(stlplus::persistent_dump_failed) -{ - // register the address and get the magic key for it - std::pair mapping = context.pointer_map(data); - stlplus::dump_unsigned(context,mapping.second); - // if the address is null, then that is all that we need to do - // however, if it is non-null and this is the first sight of the address, dump the contents - if (data && !mapping.first) - { - unsigned size = strlen(data); - stlplus::dump_unsigned(context,size); - for (unsigned i = 0; i < size; i++) - stlplus::dump_char(context,data[i]); - } -} - -void stlplus::restore_cstring(restore_context& context, char*& data) throw(stlplus::persistent_restore_failed) -{ - // destroy any previous contents - if (data) - { - delete[] data; - data = 0; - } - // get the magic key - unsigned magic = 0; - stlplus::restore_unsigned(context,magic); - // now lookup the magic key to see if this pointer has already been restored - // null pointers are always flagged as already restored - std::pair address = context.pointer_map(magic); - if (!address.first) - { - // this pointer has never been seen before and is non-null - // restore the string - unsigned size = 0; - stlplus::restore_unsigned(context,size); - data = new char[size+1]; - for (unsigned i = 0; i < size; i++) - stlplus::restore_char(context,data[i]); - data[size] = '\0'; - // add this pointer to the set of already seen objects - context.pointer_add(magic,data); - } -} - -//////////////////////////////////////////////////////////////////////////////// diff --git a/src/stlplus/persistence/persistent_cstring.hpp b/src/stlplus/persistence/persistent_cstring.hpp deleted file mode 100644 index 8e3e94a..0000000 --- a/src/stlplus/persistence/persistent_cstring.hpp +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef STLPLUS_PERSISTENT_CSTRING -#define STLPLUS_PERSISTENT_CSTRING -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Persistence of C-style char* strings - -// These are handled differently to other pointer types - -// Warning! This means that pointers to char cannot be supported, since there -// is no type difference between a pointer to char and a C-style array of char. - -// Warning! The restore deletes any old value of the data parameter and -// allocates a new char* which is (just) big enough and assigns it to the data -// field. This is because there is no way of knowing how long a char* is so -// the passed parameter is not safe to use. The allocation is done using -// standard new. If the data field is non-null on entry it will be deleted by -// standard delete. Best to make it null in the first place. - -//////////////////////////////////////////////////////////////////////////////// - -#include "persistence_fixes.hpp" -#include "persistent_contexts.hpp" - -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - - void dump_cstring(dump_context&, const char* data) throw(persistent_dump_failed); - void restore_cstring(restore_context&, char*& data) throw(persistent_restore_failed); - -} // end namespace stlplus - - //////////////////////////////////////////////////////////////////////////////// -#endif diff --git a/src/stlplus/persistence/persistent_deque.hpp b/src/stlplus/persistence/persistent_deque.hpp deleted file mode 100644 index 2a47180..0000000 --- a/src/stlplus/persistence/persistent_deque.hpp +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef STLPLUS_PERSISTENT_DEQUE -#define STLPLUS_PERSISTENT_DEQUE -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Persistence of the STL deque - -//////////////////////////////////////////////////////////////////////////////// -#include "persistence_fixes.hpp" -#include "persistent_contexts.hpp" -#include - -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - - template - void dump_deque(dump_context&, const std::deque& data, D dump_fn) throw(persistent_dump_failed); - - template - void restore_deque(restore_context&, std::deque& data, R restore_fn) throw(persistent_restore_failed); - -} // end namespace stlplus - - //////////////////////////////////////////////////////////////////////////////// -#include "persistent_deque.tpp" -#endif diff --git a/src/stlplus/persistence/persistent_deque.tpp b/src/stlplus/persistence/persistent_deque.tpp deleted file mode 100644 index 26854ce..0000000 --- a/src/stlplus/persistence/persistent_deque.tpp +++ /dev/null @@ -1,41 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// -#include "persistent_int.hpp" - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - - template - void dump_deque(dump_context& context, const std::deque& data, D dump_fn) - throw(persistent_dump_failed) - { - dump_unsigned(context,data.size()); - for (typename std::deque::const_iterator i = data.begin(); i != data.end(); i++) - dump_fn(context,*i); - } - - template - void restore_deque(restore_context& context, std::deque& data, R restore_fn) - throw(persistent_restore_failed) - { - data.clear(); - unsigned size = 0; - restore_unsigned(context,size); - for (unsigned i = 0; i < size; i++) - { - data.push_back(T()); - restore_fn(context,data.back()); - } - } - - //////////////////////////////////////////////////////////////////////////////// - -} // end namespace stlplus diff --git a/src/stlplus/persistence/persistent_digraph.hpp b/src/stlplus/persistence/persistent_digraph.hpp deleted file mode 100644 index fcc57a0..0000000 --- a/src/stlplus/persistence/persistent_digraph.hpp +++ /dev/null @@ -1,56 +0,0 @@ -#ifndef STLPLUS_PERSISTENT_DIGRAPH -#define STLPLUS_PERSISTENT_DIGRAPH -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Persistence of STLplus digraph - -//////////////////////////////////////////////////////////////////////////////// -#include "persistence_fixes.hpp" -#include "persistent_contexts.hpp" -#include "digraph.hpp" - -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - - // digraph - - template - void dump_digraph(dump_context&, const digraph& data, DN dump_node, DA dump_arc) - throw(persistent_dump_failed); - - template - void restore_digraph(restore_context&, digraph& data, RN restore_node, RA restore_arc) - throw(persistent_restore_failed); - - // node iterator - - template - void dump_digraph_iterator(dump_context& str, const digraph_iterator& data) - throw(persistent_dump_failed); - - template - void restore_digraph_iterator(restore_context& str, digraph_iterator& data) - throw(persistent_restore_failed); - - // arc iterator - - template - void dump_digraph_arc_iterator(dump_context& str, const digraph_arc_iterator& data) - throw(persistent_dump_failed); - - template - void restore_digraph_arc_iterator(restore_context& str, digraph_arc_iterator& data) - throw(persistent_restore_failed); - -} // end namespace stlplus - - //////////////////////////////////////////////////////////////////////////////// -#include "persistent_digraph.tpp" -#endif diff --git a/src/stlplus/persistence/persistent_digraph.tpp b/src/stlplus/persistence/persistent_digraph.tpp deleted file mode 100644 index 3ff94a5..0000000 --- a/src/stlplus/persistence/persistent_digraph.tpp +++ /dev/null @@ -1,153 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// -#include "persistent_int.hpp" -#include "persistent_xref.hpp" - -namespace stlplus -{ - //////////////////////////////////////////////////////////////////////////////// - - template - void dump_digraph(dump_context& context, const digraph& data, - DN dump_node, DA dump_arc) - throw(persistent_dump_failed) - { - // dump a magic key to the address of the graph for use in persistence of iterators - // and register it as a dumped address - std::pair mapping = context.pointer_map(&data); - if (mapping.first) throw persistent_dump_failed("digraph: already dumped this graph"); - dump_unsigned(context,mapping.second); - // dump the nodes - dump_unsigned(context,data.size()); - for (typename digraph::const_iterator node = data.begin(); node != data.end(); node++) - { - // nodes are keyed by the magic key to the node address - // this key is then used in dumping the arc from/to pointers - std::pair node_mapping = context.pointer_map(node.node()); - if (node_mapping.first) throw persistent_dump_failed("digraph: already dumped this node"); - dump_unsigned(context,node_mapping.second); - // finally, dump the node contents - dump_node(context,*node); - } - // dump the arcs - dump_unsigned(context,data.arc_size()); - for (typename digraph::const_arc_iterator arc = data.arc_begin(); arc != data.arc_end(); arc++) - { - // dump the magic key to the arc address - // this is used by iterator persistence too - std::pair arc_mapping = context.pointer_map(arc.node()); - if (arc_mapping.first) throw persistent_dump_failed("digraph: already dumped this arc"); - dump_unsigned(context,arc_mapping.second); - // now dump the from/to pointers as cross-references - dump_xref(context,data.arc_from(arc).node()); - dump_xref(context,data.arc_to(arc).node()); - // now dump the arc's data - dump_arc(context,*arc); - } - } - - //////////////////////////////////////////////////////////////////////////////// - - template - void restore_digraph(restore_context& context, digraph& data, - RN restore_node, RA restore_arc) - throw(persistent_restore_failed) - { - data.clear(); - // restore the graph's magic key and map it onto the graph's address - // this is used in the persistence of iterators - unsigned magic = 0; - restore_unsigned(context,magic); - context.pointer_add(magic,&data); - // restore the nodes - unsigned nodes = 0; - restore_unsigned(context, nodes); - for (unsigned n = 0; n < nodes; n++) - { - unsigned node_magic = 0; - restore_unsigned(context,node_magic); - // create a new node and map the magic key onto the new address - typename digraph::iterator node = data.insert(NT()); - context.pointer_add(node_magic,node.node()); - // now restore the user's data - restore_node(context,*node); - } - // restore the arcs - unsigned arcs = 0; - restore_unsigned(context, arcs); - for (unsigned a = 0; a < arcs; a++) - { - unsigned arc_magic = 0; - restore_unsigned(context,arc_magic); - // restore the from and to cross-references - digraph_node* from = 0; - digraph_node* to = 0; - restore_xref(context,from); - restore_xref(context,to); - // create an arc with these from/to pointers - digraph_arc_iterator arc = - data.arc_insert(digraph_iterator(from), - digraph_iterator(to)); - context.pointer_add(arc_magic,arc.node()); - // restore the user data - restore_arc(context,*arc); - } - } - - //////////////////////////////////////////////////////////////////////////////// - - template - void dump_digraph_iterator(dump_context& context, - const digraph_iterator& data) - throw(persistent_dump_failed) - { - dump_xref(context,data.owner()); - dump_xref(context,data.node()); - } - - template - void restore_digraph_iterator(restore_context& context, - digraph_iterator& data) - throw(persistent_restore_failed) - { - digraph* owner = 0; - digraph_node* node = 0; - restore_xref(context,owner); - restore_xref(context,node); - data = digraph_iterator(node); - data.assert_owner(owner); - } - - //////////////////////////////////////////////////////////////////////////////// - - template - void dump_digraph_arc_iterator(dump_context& context, - const digraph_arc_iterator& data) - throw(persistent_dump_failed) - { - dump_xref(context,data.owner()); - dump_xref(context,data.node()); - } - - template - void restore_digraph_arc_iterator(restore_context& context, - digraph_arc_iterator& data) - throw(persistent_restore_failed) - { - digraph* owner = 0; - digraph_arc* arc = 0; - restore_xref(context,owner); - restore_xref(context,arc); - data = digraph_arc_iterator(arc); - data.assert_owner(owner); - } - - //////////////////////////////////////////////////////////////////////////////// - -} // end namespace stlplus diff --git a/src/stlplus/persistence/persistent_enum.hpp b/src/stlplus/persistence/persistent_enum.hpp deleted file mode 100644 index ddce1b4..0000000 --- a/src/stlplus/persistence/persistent_enum.hpp +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef STLPLUS_PERSISTENT_ENUM -#define STLPLUS_PERSISTENT_ENUM -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Persistence of enumeration types - -//////////////////////////////////////////////////////////////////////////////// -#include "persistence_fixes.hpp" -#include "persistent_contexts.hpp" - -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - - template - void dump_enum(dump_context&, const T& data) throw(persistent_dump_failed); - - template - void restore_enum(restore_context&, T& data) throw(persistent_restore_failed); - -} // end namespace stlplus - - //////////////////////////////////////////////////////////////////////////////// -#include "persistent_enum.tpp" -#endif diff --git a/src/stlplus/persistence/persistent_enum.tpp b/src/stlplus/persistence/persistent_enum.tpp deleted file mode 100644 index 9778688..0000000 --- a/src/stlplus/persistence/persistent_enum.tpp +++ /dev/null @@ -1,36 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// - -#include "persistent_int.hpp" - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - // enumeration types - - template - void dump_enum(dump_context& context, const T& data) - throw(persistent_dump_failed) - { - dump_unsigned(context,(unsigned)data); - } - - template - void restore_enum(restore_context& context, T& data) - throw(persistent_restore_failed) - { - unsigned value = 0; - restore_unsigned(context, value); - data = (T)value; - } - - //////////////////////////////////////////////////////////////////////////////// - -} // end namespace stlplus diff --git a/src/stlplus/persistence/persistent_exceptions.cpp b/src/stlplus/persistence/persistent_exceptions.cpp deleted file mode 100644 index d233826..0000000 --- a/src/stlplus/persistence/persistent_exceptions.cpp +++ /dev/null @@ -1,64 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// - -#include "persistent_exceptions.hpp" -#include - -//////////////////////////////////////////////////////////////////////////////// - -static std::string to_string(int number) -{ - // use sprintf in a very controlled way that cannot overrun - char* buffer = new char[50]; - sprintf(buffer, "%i", number); - std::string result = buffer; - delete buffer; - return result; -} - -//////////////////////////////////////////////////////////////////////////////// -// exceptions - -stlplus::persistent_illegal_type::persistent_illegal_type(const std::string& type) throw() : - std::logic_error(std::string("illegal type: ") + type) -{ -} - -stlplus::persistent_illegal_type::persistent_illegal_type(unsigned key) throw() : - std::logic_error(std::string("illegal key: ") + to_string((int)key)) -{ -} - -stlplus::persistent_illegal_type::~persistent_illegal_type(void) throw() -{ -} - -//////////////////////////////////////////////////////////////////////////////// - -stlplus::persistent_dump_failed::persistent_dump_failed(const std::string& message) throw() : - std::runtime_error(std::string("dump failed: ") + message) -{ -} - -stlplus::persistent_dump_failed::~persistent_dump_failed(void) throw() -{ -} - -//////////////////////////////////////////////////////////////////////////////// - -stlplus::persistent_restore_failed::persistent_restore_failed(const std::string& message) throw() : - std::runtime_error(std::string("restore failed: ") + message) -{ -} - -stlplus::persistent_restore_failed::~persistent_restore_failed(void) throw() -{ -} - -//////////////////////////////////////////////////////////////////////////////// diff --git a/src/stlplus/persistence/persistent_exceptions.hpp b/src/stlplus/persistence/persistent_exceptions.hpp deleted file mode 100644 index 2e2a861..0000000 --- a/src/stlplus/persistence/persistent_exceptions.hpp +++ /dev/null @@ -1,53 +0,0 @@ -#ifndef STLPLUS_PERSISTENT_EXCEPTIONS -#define STLPLUS_PERSISTENT_EXCEPTIONS -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Exceptions thrown by persistence routines - -//////////////////////////////////////////////////////////////////////////////// -#include "persistence_fixes.hpp" -#include -#include - -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - - // exception thrown if you try to dump or restore an illegal polymorphic type - class persistent_illegal_type : public std::logic_error - { - public: - persistent_illegal_type(const std::string& type) throw(); - persistent_illegal_type(unsigned key) throw(); - ~persistent_illegal_type(void) throw(); - }; - - // exception thrown if a dump fails for any reason - but typically because the output stream couldn't take the data - class persistent_dump_failed : public std::runtime_error - { - public: - persistent_dump_failed(const std::string& message) throw(); - ~persistent_dump_failed(void) throw(); - }; - - // exception thrown if you try to restore from an out of date or unrecognised byte stream - class persistent_restore_failed : public std::runtime_error - { - public: - persistent_restore_failed(const std::string& message) throw(); - ~persistent_restore_failed(void) throw(); - }; - - //////////////////////////////////////////////////////////////////////////////// - -} // end namespace stlplus - -#endif diff --git a/src/stlplus/persistence/persistent_float.cpp b/src/stlplus/persistence/persistent_float.cpp deleted file mode 100644 index 59a6678..0000000 --- a/src/stlplus/persistence/persistent_float.cpp +++ /dev/null @@ -1,86 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// -#include "persistent_float.hpp" - -//////////////////////////////////////////////////////////////////////////////// -// Macro for mapping either endian data onto little-endian addressing to make -// my life easier in writing this code! I think better in little-endian mode -// so the macro does nothing in that mode but maps little-endian onto -// big-endian addressing in big-endian mode -// TODO - make this compile-time configurable - -#define INDEX(index) ((context.little_endian()) ? (index) : ((bytes) - (index) - 1)) - -///////////////////////////////////////////////////////////////////// -// floating point types -// format: {size}{byte}*size -// ordering is msB first - -// this uses a similar mechanism to integer dumps. However, it is not clear how -// the big-endian and little-endian argument applies to multi-word data so -// this may need reworking by splitting into words and then bytes. - -namespace stlplus -{ - - static void dump_float(stlplus::dump_context& context, unsigned bytes, unsigned char* data) - throw(stlplus::persistent_dump_failed) - { - unsigned i = bytes; - // put the size - context.put((unsigned char)i); - // and put the bytes - while(i--) - context.put(data[INDEX(i)]); - } - - static void restore_float(stlplus::restore_context& context, unsigned bytes, unsigned char* data) - throw(stlplus::persistent_restore_failed) - { - // get the dumped size from the file - unsigned dumped_bytes = (unsigned)context.get(); - // get the bytes from the file - unsigned i = dumped_bytes; - while(i--) - { - int ch = context.get(); - if (i < bytes) - data[INDEX(i)] = (unsigned char)ch; - } - // however, if the dumped size was different I don't know how to map the formats, so give an error - if (dumped_bytes != bytes) - throw stlplus::persistent_restore_failed(std::string("size mismatch")); - } - -} // end namespace stlplus - - //////////////////////////////////////////////////////////////////////////////// - // exported functions which simply call the low-level byte-dump and byte-restore routines above - -void stlplus::dump_float(stlplus::dump_context& context, const float& data) throw(stlplus::persistent_dump_failed) -{ - stlplus::dump_float(context, sizeof(float), (unsigned char*)&data); -} - -void stlplus::restore_float(restore_context& context, float& data) throw(stlplus::persistent_restore_failed) -{ - stlplus::restore_float(context, sizeof(float), (unsigned char*)&data); -} - -void stlplus::dump_double(stlplus::dump_context& context, const double& data) throw(stlplus::persistent_dump_failed) -{ - stlplus::dump_float(context, sizeof(double), (unsigned char*)&data); -} - -void stlplus::restore_double(restore_context& context, double& data) throw(stlplus::persistent_restore_failed) -{ - stlplus::restore_float(context, sizeof(double), (unsigned char*)&data); -} - -//////////////////////////////////////////////////////////////////////////////// diff --git a/src/stlplus/persistence/persistent_float.hpp b/src/stlplus/persistence/persistent_float.hpp deleted file mode 100644 index 4a01e30..0000000 --- a/src/stlplus/persistence/persistent_float.hpp +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef STLPLUS_PERSISTENT_FLOAT -#define STLPLUS_PERSISTENT_FLOAT -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Persistence of floating-point types - -// Note: despite years and years of IEEE standardisation, not all -// architectures use IEEE-standard representations of floating-point numbers. -// Therefore a binary dump is not necessarily portable between platforms. -// Solving this is (currently) beyond the scope of the STLplus project. - -// If you want to be strictly portable to all platforms, do not dump/restore float - -//////////////////////////////////////////////////////////////////////////////// -#include "persistence_fixes.hpp" -#include "persistent_contexts.hpp" - -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - - void dump_float(dump_context&, const float& data) throw(persistent_dump_failed); - void restore_float(restore_context&, float& data) throw(persistent_restore_failed); - - void dump_double(dump_context&, const double& data) throw(persistent_dump_failed); - void restore_double(restore_context&, double& data) throw(persistent_restore_failed); - -} // end namespace stlplus - - //////////////////////////////////////////////////////////////////////////////// -#endif diff --git a/src/stlplus/persistence/persistent_foursome.hpp b/src/stlplus/persistence/persistent_foursome.hpp deleted file mode 100644 index b326a85..0000000 --- a/src/stlplus/persistence/persistent_foursome.hpp +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef STLPLUS_PERSISTENT_FOURSOME -#define STLPLUS_PERSISTENT_FOURSOME -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Persistence of STL foursome - -//////////////////////////////////////////////////////////////////////////////// -#include "persistence_fixes.hpp" -#include "persistent_contexts.hpp" -#include "foursome.hpp" - -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - - template - void dump_foursome(dump_context&, const stlplus::foursome& data, - D1 dump_fn1, D2 dump_fn2, D3 dump_fn3, D4 dump_fn4) - throw(persistent_dump_failed); - - template - void restore_foursome(restore_context&, stlplus::foursome& data, - R1 restore_fn1, R2 restore_fn2, R3 restore_fn3, R4 restore_fn4) - throw(persistent_restore_failed); - -} // end namespace stlplus - - //////////////////////////////////////////////////////////////////////////////// -#include "persistent_foursome.tpp" -#endif diff --git a/src/stlplus/persistence/persistent_foursome.tpp b/src/stlplus/persistence/persistent_foursome.tpp deleted file mode 100644 index cfcf6ae..0000000 --- a/src/stlplus/persistence/persistent_foursome.tpp +++ /dev/null @@ -1,39 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - - template - void dump_foursome(dump_context& context, const foursome& data, - D1 dump_fn1, D2 dump_fn2, D3 dump_fn3, D4 dump_fn4) - throw(persistent_dump_failed) - { - dump_fn1(context,data.first); - dump_fn2(context,data.second); - dump_fn3(context,data.third); - dump_fn4(context,data.fourth); - } - - template - void restore_foursome(restore_context& context, foursome& data, - R1 restore_fn1, R2 restore_fn2, R3 restore_fn3, R4 restore_fn4) - throw(persistent_restore_failed) - { - restore_fn1(context,data.first); - restore_fn2(context,data.second); - restore_fn3(context,data.third); - restore_fn4(context,data.fourth); - } - - //////////////////////////////////////////////////////////////////////////////// - -} // end namespace stlplus diff --git a/src/stlplus/persistence/persistent_hash.hpp b/src/stlplus/persistence/persistent_hash.hpp deleted file mode 100644 index 623f285..0000000 --- a/src/stlplus/persistence/persistent_hash.hpp +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef STLPLUS_PERSISTENT_HASH -#define STLPLUS_PERSISTENT_HASH -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Persistence for STLplus hash - -//////////////////////////////////////////////////////////////////////////////// -#include "persistence_fixes.hpp" -#include "persistent_contexts.hpp" -#include "hash.hpp" - -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - - template - void dump_hash(dump_context&, const hash& data, DK key_dump_fn, DT val_dump_fn) - throw(persistent_dump_failed); - - template - void restore_hash(restore_context&, hash& data, RK key_restore_fn, RT val_restore_fn) - throw(persistent_restore_failed); - -} // end namespace stlplus - - //////////////////////////////////////////////////////////////////////////////// -#include "persistent_hash.tpp" -#endif diff --git a/src/stlplus/persistence/persistent_hash.tpp b/src/stlplus/persistence/persistent_hash.tpp deleted file mode 100644 index 9fe2dbc..0000000 --- a/src/stlplus/persistence/persistent_hash.tpp +++ /dev/null @@ -1,45 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// -#include "persistent_int.hpp" - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - - template - void dump_hash(dump_context& context, const hash& data, DK key_fn, DT val_fn) - throw(persistent_dump_failed) - { - dump_unsigned(context,data.size()); - for (typename hash::const_iterator i = data.begin(); i != data.end(); i++) - { - key_fn(context,i->first); - val_fn(context,i->second); - } - } - - template - void restore_hash(restore_context& context, hash& data, RK key_fn, RT val_fn) - throw(persistent_restore_failed) - { - data.erase(); - unsigned size = 0; - restore_unsigned(context,size); - for (unsigned j = 0; j < size; j++) - { - K key; - key_fn(context,key); - val_fn(context,data[key]); - } - } - - //////////////////////////////////////////////////////////////////////////////// - -} // end namespace stlplus diff --git a/src/stlplus/persistence/persistent_inf.cpp b/src/stlplus/persistence/persistent_inf.cpp deleted file mode 100644 index 29637c9..0000000 --- a/src/stlplus/persistence/persistent_inf.cpp +++ /dev/null @@ -1,66 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// - -// can be excluded to break the dependency on the portability library -#ifndef NO_STLPLUS_INF - -#include "persistent_int.hpp" -#include "persistent_string.hpp" -#include "persistent_inf.hpp" - -//////////////////////////////////////////////////////////////////////////////// - -void stlplus::dump_inf(stlplus::dump_context& context, const stlplus::inf& data) - throw(stlplus::persistent_dump_failed) -{ - // don't support dumping of old versions - if (context.version() < 2) - throw stlplus::persistent_dump_failed(std::string("stlplus::inf::dump: wrong version")); - // just dump the string - stlplus::dump_string(context,data.get_bytes()); -} - -//////////////////////////////////////////////////////////////////////////////// - -void stlplus::restore_inf(stlplus::restore_context& context, stlplus::inf& data) - throw(stlplus::persistent_restore_failed) -{ - if (context.version() < 1) - throw stlplus::persistent_restore_failed(std::string("stlplus::inf::restore: wrong version")); - if (context.version() == 1) - { - // old-style restore relies on the word size being the same - 32-bits - on all platforms - // this can be restored on such machines but is not portable to 64-bit machines - std::string value; - unsigned bits = 0; - stlplus::restore_unsigned(context,bits); - unsigned words = (bits+7)/32; - // inf was dumped msB first - for (unsigned i = words; i--; ) - { - // restore a word - unsigned word = 0; - stlplus::restore_unsigned(context,word); - // now extract the bytes - unsigned char* byte_ptr = (unsigned char*)(&word); - for (unsigned b = 4; b--; ) - value.insert(value.begin(),byte_ptr[context.little_endian() ? b : 3 - b]); - } - data.set_bytes(value); - } - else - { - // new-style dump just uses the string persistence - std::string value; - stlplus::restore_string(context,value); - data.set_bytes(value); - } -} -//////////////////////////////////////////////////////////////////////////////// -#endif diff --git a/src/stlplus/persistence/persistent_inf.hpp b/src/stlplus/persistence/persistent_inf.hpp deleted file mode 100644 index 2b456d9..0000000 --- a/src/stlplus/persistence/persistent_inf.hpp +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef STLPLUS_PERSISTENT_INF -#define STLPLUS_PERSISTENT_INF -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Persistence of stlplus infinite integer type - inf - -//////////////////////////////////////////////////////////////////////////////// -#include "persistence_fixes.hpp" -#include "persistent_contexts.hpp" -#include "inf.hpp" - -namespace stlplus -{ - - void dump_inf(dump_context&, const inf& data) throw(persistent_dump_failed); - void restore_inf(restore_context&, inf& data) throw(persistent_restore_failed); - -} // end namespace stlplus - -#endif diff --git a/src/stlplus/persistence/persistent_int.cpp b/src/stlplus/persistence/persistent_int.cpp deleted file mode 100644 index 87d403a..0000000 --- a/src/stlplus/persistence/persistent_int.cpp +++ /dev/null @@ -1,223 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// -#include "persistent_int.hpp" - -//////////////////////////////////////////////////////////////////////////////// -// Macro for mapping either endian data onto little-endian addressing to make -// my life easier in writing this code! I think better in little-endian mode -// so the macro does nothing in that mode but maps little-endian onto -// big-endian addressing in big-endian mode -// TODO - make this compile-time configurable so it's more efficient - -#define INDEX(index) ((context.little_endian()) ? (index) : ((bytes) - (index) - 1)) - -//////////////////////////////////////////////////////////////////////////////// -// Integer types -// format: {size}{byte}*size -// size can be zero! -// -// A major problem is that integer types may be different sizes on different -// machines or even with different compilers on the same machine (though I -// haven't come across that yet). Neither the C nor the C++ standards specify -// the size of integer types. Dumping an int on one machine might dump 16 -// bits. Restoring it on another machine might try to restore 32 bits. These -// functions must therefore handle different type sizes. It does this by -// writing the size to the file as well as the data, so the restore can -// therefore know how many bytes to restore independent of the type size. -// -// In fact, the standard does not even specify the size of char (true! And -// mind-numbingly stupid...). However, to be able to do anything at all, I've -// had to assume that a char is 1 byte. - -static void dump_unsigned(stlplus::dump_context& context, unsigned bytes, unsigned char* data) - throw(stlplus::persistent_dump_failed) -{ - // first skip zero bytes - this may reduce the data to zero bytes long - unsigned i = bytes; - while(i >= 1 && data[INDEX(i-1)] == 0) - i--; - // put the remaining size - context.put((unsigned char)i); - // and put the bytes - while(i--) - context.put(data[INDEX(i)]); -} - -static void dump_signed(stlplus::dump_context& context, unsigned bytes, unsigned char* data) - throw(stlplus::persistent_dump_failed) -{ - // first skip all-zero or all-one bytes but only if doing so does not change the sign - unsigned i = bytes; - if (data[INDEX(i-1)] < 128) - { - // positive number so discard leading zeros but only if the following byte is positive - while(i >= 2 && data[INDEX(i-1)] == 0 && data[INDEX(i-2)] < 128) - i--; - } - else - { - // negative number so discard leading ones but only if the following byte is negative - while(i >= 2 && data[INDEX(i-1)] == 255 && data[INDEX(i-2)] >= 128) - i--; - } - // put the remaining size - context.put((unsigned char)i); - // and put the bytes - while(i--) - context.put(data[INDEX(i)]); -} - -static void restore_unsigned(stlplus::restore_context& context, unsigned bytes, unsigned char* data) - throw(stlplus::persistent_restore_failed) -{ - // get the dumped size from the file - unsigned dumped_bytes = (unsigned)context.get(); - // zero fill any empty space - unsigned i = bytes; - for (; i > dumped_bytes; i--) - data[INDEX(i-1)] = 0; - // restore the dumped bytes but discard any that don't fit - while(i--) - { - int ch = context.get(); - if (i < bytes) - data[INDEX(i)] = (unsigned char)ch; - else - throw stlplus::persistent_restore_failed(std::string("integer overflow")); - } -} - -static void restore_signed(stlplus::restore_context& context, unsigned bytes, unsigned char* data) - throw(stlplus::persistent_restore_failed) -{ - // get the dumped size from the file - unsigned dumped_bytes = (unsigned)context.get(); - // restore the dumped bytes but discard any that don't fit - unsigned i = dumped_bytes; - while(i--) - { - int ch = context.get(); - if (i < bytes) - data[INDEX(i)] = (unsigned char)ch; - else - throw stlplus::persistent_restore_failed(std::string("integer overflow")); - } - // sign extend if the dumped integer was smaller - if (dumped_bytes < bytes) - { - if (data[INDEX(dumped_bytes-1)] < 128) - { - // positive so zero fill - for (i = dumped_bytes; i < bytes; i++) - data[INDEX(i)] = 0; - } - else - { - // negative so one fill - for (i = dumped_bytes; i < bytes; i++) - data[INDEX(i)] = 0xff; - } - } -} - -//////////////////////////////////////////////////////////////////////////////// -// exported functions - -// char is dumped and restored as an unsigned char because the signedness of char is not defined and can vary -void stlplus::dump_char(stlplus::dump_context& context, const char& data) throw(stlplus::persistent_dump_failed) -{ - context.put((unsigned char)data); -} - -void stlplus::restore_char(restore_context& context, char& data) throw(stlplus::persistent_restore_failed) -{ - data = (char)(unsigned char)context.get(); -} - -void stlplus::dump_signed_char(stlplus::dump_context& context, const signed char& data) throw(stlplus::persistent_dump_failed) -{ - context.put((unsigned char)data); -} - -void stlplus::restore_signed_char(restore_context& context, signed char& data) throw(stlplus::persistent_restore_failed) -{ - data = (signed char)(unsigned char)context.get(); -} - -void stlplus::dump_unsigned_char(stlplus::dump_context& context, const unsigned char& data) throw(stlplus::persistent_dump_failed) -{ - context.put((unsigned char)data); -} - -void stlplus::restore_unsigned_char(restore_context& context, unsigned char& data) throw(stlplus::persistent_restore_failed) -{ - data = (signed char)(unsigned char)context.get(); -} - -void stlplus::dump_short(stlplus::dump_context& context, const short& data) throw(stlplus::persistent_dump_failed) -{ - ::dump_signed(context, sizeof(short), (unsigned char*)&data); -} - -void stlplus::restore_short(restore_context& context, short& data) throw(stlplus::persistent_restore_failed) -{ - ::restore_signed(context, sizeof(short),(unsigned char*)&data); -} - -void stlplus::dump_unsigned_short(stlplus::dump_context& context, const unsigned short& data) throw(stlplus::persistent_dump_failed) -{ - ::dump_unsigned(context, sizeof(unsigned short), (unsigned char*)&data); -} - -void stlplus::restore_unsigned_short(restore_context& context, unsigned short& data) throw(stlplus::persistent_restore_failed) -{ - ::restore_unsigned(context, sizeof(unsigned short),(unsigned char*)&data); -} - -void stlplus::dump_int(stlplus::dump_context& context, const int& data) throw(stlplus::persistent_dump_failed) -{ - ::dump_signed(context, sizeof(int), (unsigned char*)&data); -} - -void stlplus::restore_int(restore_context& context, int& data) throw(stlplus::persistent_restore_failed) -{ - ::restore_signed(context, sizeof(int),(unsigned char*)&data); -} - -void stlplus::dump_unsigned(stlplus::dump_context& context, const unsigned& data) throw(stlplus::persistent_dump_failed) -{ - ::dump_unsigned(context, sizeof(unsigned), (unsigned char*)&data); -} - -void stlplus::restore_unsigned(restore_context& context, unsigned& data) throw(stlplus::persistent_restore_failed) -{ - ::restore_unsigned(context, sizeof(unsigned),(unsigned char*)&data); -} - -void stlplus::dump_long(stlplus::dump_context& context, const long& data) throw(stlplus::persistent_dump_failed) -{ - ::dump_signed(context, sizeof(long), (unsigned char*)&data); -} - -void stlplus::restore_long(restore_context& context, long& data) throw(stlplus::persistent_restore_failed) -{ - ::restore_signed(context, sizeof(long),(unsigned char*)&data); -} - -void stlplus::dump_unsigned_long(stlplus::dump_context& context, const unsigned long& data) throw(stlplus::persistent_dump_failed) -{ - ::dump_unsigned(context, sizeof(unsigned long), (unsigned char*)&data); -} - -void stlplus::restore_unsigned_long(restore_context& context, unsigned long& data) throw(stlplus::persistent_restore_failed) -{ - ::restore_unsigned(context, sizeof(unsigned long),(unsigned char*)&data); -} - -//////////////////////////////////////////////////////////////////////////////// diff --git a/src/stlplus/persistence/persistent_int.hpp b/src/stlplus/persistence/persistent_int.hpp deleted file mode 100644 index 404d1c5..0000000 --- a/src/stlplus/persistence/persistent_int.hpp +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef STLPLUS_PERSISTENT_INT -#define STLPLUS_PERSISTENT_INT -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Persistence of basic integer types - -//////////////////////////////////////////////////////////////////////////////// -#include "persistence_fixes.hpp" -#include "persistent_contexts.hpp" - -namespace stlplus -{ - - void dump_char(dump_context&, const char& data) throw(persistent_dump_failed); - void restore_char(restore_context&, char& data) throw(persistent_restore_failed); - - void dump_signed_char(dump_context&, const signed char& data) throw(persistent_dump_failed); - void restore_signed_char(restore_context&, signed char& data) throw(persistent_restore_failed); - - void dump_unsigned_char(dump_context&, const unsigned char& data) throw(persistent_dump_failed); - void restore_unsigned_char(restore_context&, unsigned char& data) throw(persistent_restore_failed); - - void dump_short(dump_context&, const short& data) throw(persistent_dump_failed); - void restore_short(restore_context&, short& data) throw(persistent_restore_failed); - - void dump_unsigned_short(dump_context&, const unsigned short& data) throw(persistent_dump_failed); - void restore_unsigned_short(restore_context&, unsigned short& data) throw(persistent_restore_failed); - - void dump_int(dump_context&, const int& data) throw(persistent_dump_failed); - void restore_int(restore_context&, int& data) throw(persistent_restore_failed); - - void dump_unsigned(dump_context&, const unsigned& data) throw(persistent_dump_failed); - void restore_unsigned(restore_context&, unsigned& data) throw(persistent_restore_failed); - - void dump_long(dump_context&, const long& data) throw(persistent_dump_failed); - void restore_long(restore_context&, long& data) throw(persistent_restore_failed); - - void dump_unsigned_long(dump_context&, const unsigned long& data) throw(persistent_dump_failed); - void restore_unsigned_long(restore_context&, unsigned long& data) throw(persistent_restore_failed); - - //////////////////////////////////////////////////////////////////////////////// - -} // end namespace stlplus - -#endif diff --git a/src/stlplus/persistence/persistent_interface.hpp b/src/stlplus/persistence/persistent_interface.hpp deleted file mode 100644 index 366954c..0000000 --- a/src/stlplus/persistence/persistent_interface.hpp +++ /dev/null @@ -1,54 +0,0 @@ -#ifndef STLPLUS_PERSISTENT_INTERFACE -#define STLPLUS_PERSISTENT_INTERFACE -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Persistence for pointers to polymorphic classes using the interface approach. - -// This works on a set of classes derived from a common superclass called -// persistent which is declared as an interface. Each subclass has a set of -// methods that enable clone/dump/restore operations. Each subclass must be -// registered with the persistence dump/restore context so that the system -// knows how to dump it. - -// This approach is suited to classes that can be modified to add persistence -// methods. See persistent_callback for a non-invasive way of handling -// polymorphism. - -// Objects are always dumped/restored as pointers to the superclass T. - -// Multiple pointers to the same object are handled in the same way as for -// simple pointers - -// Only classes registered with the context can be dumped and restored as -// polymorphic types - see dump_context::register_interface and -// restore_context::register_interface. Attempting to use any unrecognised class -// will throw an exception. - -//////////////////////////////////////////////////////////////////////////////// -#include "persistence_fixes.hpp" -#include "persistent_contexts.hpp" -#include "persistent.hpp" - -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - - template - void dump_interface(dump_context&, const T* const data) - throw(persistent_dump_failed); - - template - void restore_interface(restore_context&, T*& data) - throw(persistent_restore_failed); - -} // end namespace stlplus - - //////////////////////////////////////////////////////////////////////////////// -#include "persistent_interface.tpp" -#endif diff --git a/src/stlplus/persistence/persistent_interface.tpp b/src/stlplus/persistence/persistent_interface.tpp deleted file mode 100644 index a684aa3..0000000 --- a/src/stlplus/persistence/persistent_interface.tpp +++ /dev/null @@ -1,99 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Polymorphous classes using the interface approach - -// format: magic [ key data ] - -//////////////////////////////////////////////////////////////////////////////// -#include "persistent_int.hpp" - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - - template - void dump_interface(dump_context& context, const T* const data) - throw(persistent_dump_failed) - { - try - { - // register the address and get the magic key for it - std::pair mapping = context.pointer_map(data); - dump_unsigned(context,mapping.second); - // if the address is null, then that is all that we need to do - // however, if it is non-null and this is the first sight of the address, dump the contents - if (data && !mapping.first) - { - // interface method - // the lookup just finds the magic key and the type has a dump method - // this will throw persistent_illegal_type if the type is not registered - unsigned key = context.lookup_interface(typeid(*data)); - // dump the magic key for the type - dump_unsigned(context, key); - // now call the dump method defined by the interface - data->dump(context); - } - } - catch (const persistent_illegal_type& except) - { - // convert this to a simpler dump failed exception - throw persistent_dump_failed(except.what()); - } - } - - //////////////////////////////////////////////////////////////////////////////// - - template - void restore_interface(restore_context& context, T*& data) - throw(persistent_restore_failed) - { - try - { - // first delete any previous object pointed to since the restore creates the object of the right subclass - if (data) - { - delete data; - data = 0; - } - // get the magic key - unsigned magic = 0; - restore_unsigned(context,magic); - // now lookup the magic key to see if this pointer has already been restored - // null pointers are always flagged as already restored - std::pair address = context.pointer_map(magic); - if (address.first) - { - // seen before, so simply map it to the existing address - data = (T*)address.second; - } - else - { - // now restore the magic key that denotes the particular subclass - unsigned key = 0; - restore_unsigned(context, key); - // interface approach - // first clone the sample object stored in the map - lookup_interface can throw persistent_illegal_type - data = (T*)(context.lookup_interface(key)->clone()); - // add this pointer to the set of already seen objects - // do this before restoring the object so that self-referential structures restore correctly - context.pointer_add(magic,data); - // now restore the contents using the object's method - data->restore(context); - } - } - catch (const persistent_illegal_type& exception) - { - // convert this to a simpler dump failed exception - throw persistent_restore_failed(exception.what()); - } - } - - //////////////////////////////////////////////////////////////////////////////// - -} // end namespace stlplus diff --git a/src/stlplus/persistence/persistent_list.hpp b/src/stlplus/persistence/persistent_list.hpp deleted file mode 100644 index b95d767..0000000 --- a/src/stlplus/persistence/persistent_list.hpp +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef STLPLUS_PERSISTENT_LIST -#define STLPLUS_PERSISTENT_LIST -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Persistence of STL list - -//////////////////////////////////////////////////////////////////////////////// -#include "persistence_fixes.hpp" -#include "persistent_contexts.hpp" -#include - -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - - template - void dump_list(dump_context&, const std::list& data, D dump_fn) throw(persistent_dump_failed); - - template - void restore_list(restore_context&, std::list& data, R restore_fn) throw(persistent_restore_failed); - -} // end namespace stlplus - - //////////////////////////////////////////////////////////////////////////////// -#include "persistent_list.tpp" -#endif diff --git a/src/stlplus/persistence/persistent_list.tpp b/src/stlplus/persistence/persistent_list.tpp deleted file mode 100644 index aaf696f..0000000 --- a/src/stlplus/persistence/persistent_list.tpp +++ /dev/null @@ -1,41 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// -#include "persistent_int.hpp" - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - - template - void dump_list(dump_context& context, const std::list& data, D dump_fn) - throw(persistent_dump_failed) - { - dump_unsigned(context,data.size()); - for (typename std::list::const_iterator i = data.begin(); i != data.end(); i++) - dump_fn(context,*i); - } - - template - void restore_list(restore_context& context, std::list& data, R restore_fn) - throw(persistent_restore_failed) - { - data.clear(); - unsigned size = 0; - restore_unsigned(context,size); - for (unsigned i = 0; i < size; i++) - { - data.push_back(T()); - restore_fn(context,data.back()); - } - } - - //////////////////////////////////////////////////////////////////////////////// - -} // end namespace stlplus diff --git a/src/stlplus/persistence/persistent_map.hpp b/src/stlplus/persistence/persistent_map.hpp deleted file mode 100644 index 4f9449f..0000000 --- a/src/stlplus/persistence/persistent_map.hpp +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef STLPLUS_PERSISTENT_MAP -#define STLPLUS_PERSISTENT_MAP -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Persistence for STL map - -//////////////////////////////////////////////////////////////////////////////// -#include "persistence_fixes.hpp" -#include "persistent_contexts.hpp" -#include - -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - - template - void dump_map(dump_context&, const std::map& data, DK key_dump_fn, DT val_dump_fn) - throw(persistent_dump_failed); - - template - void restore_map(restore_context&, std::map& data, RK key_restore_fn, RT val_restore_fn) - throw(persistent_restore_failed); - -} // end namespace stlplus - - //////////////////////////////////////////////////////////////////////////////// -#include "persistent_map.tpp" -#endif diff --git a/src/stlplus/persistence/persistent_map.tpp b/src/stlplus/persistence/persistent_map.tpp deleted file mode 100644 index b1af42c..0000000 --- a/src/stlplus/persistence/persistent_map.tpp +++ /dev/null @@ -1,45 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// -#include "persistent_int.hpp" - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - - template - void dump_map(dump_context& context, const std::map& data, DK key_fn, DT val_fn) - throw(persistent_dump_failed) - { - dump_unsigned(context,data.size()); - for (typename std::map::const_iterator i = data.begin(); i != data.end(); i++) - { - key_fn(context,i->first); - val_fn(context,i->second); - } - } - - template - void restore_map(restore_context& context, std::map& data, RK key_fn, RT val_fn) - throw(persistent_restore_failed) - { - data.clear(); - unsigned size = 0; - restore_unsigned(context,size); - for (unsigned j = 0; j < size; j++) - { - K key; - key_fn(context,key); - val_fn(context,data[key]); - } - } - - //////////////////////////////////////////////////////////////////////////////// - -} // end namespace stlplus diff --git a/src/stlplus/persistence/persistent_matrix.hpp b/src/stlplus/persistence/persistent_matrix.hpp deleted file mode 100644 index 2f727a2..0000000 --- a/src/stlplus/persistence/persistent_matrix.hpp +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef STLPLUS_PERSISTENT_MATRIX -#define STLPLUS_PERSISTENT_MATRIX -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Persistence of STLplus matrix - -//////////////////////////////////////////////////////////////////////////////// -#include "persistence_fixes.hpp" -#include "persistent_contexts.hpp" -#include "matrix.hpp" - -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - - template - void dump_matrix(dump_context&, const matrix& data, DT dump_fn) - throw(persistent_dump_failed); - - template - void restore_matrix(restore_context&, matrix& data, RT restore_fn) - throw(persistent_restore_failed); - -} // end namespace stlplus - - //////////////////////////////////////////////////////////////////////////////// -#include "persistent_matrix.tpp" -#endif diff --git a/src/stlplus/persistence/persistent_matrix.tpp b/src/stlplus/persistence/persistent_matrix.tpp deleted file mode 100644 index ac81f85..0000000 --- a/src/stlplus/persistence/persistent_matrix.tpp +++ /dev/null @@ -1,49 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// -#include "persistent_int.hpp" - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - - template - void dump_matrix(dump_context& context, const matrix& data, - DT dump_fn) - throw(persistent_dump_failed) - { - unsigned rows = data.rows(); - unsigned cols = data.columns(); - dump_unsigned(context, rows); - dump_unsigned(context, cols); - for (unsigned r = 0; r < rows; r++) - for (unsigned c = 0; c < cols; c++) - dump_fn(context, data(r,c)); - } - - //////////////////////////////////////////////////////////////////////////////// - - template - void restore_matrix(restore_context& context, matrix& data, - RT restore_fn) - throw(persistent_restore_failed) - { - unsigned rows = 0; - restore_unsigned(context, rows); - unsigned cols = 0; - restore_unsigned(context, cols); - data.resize(rows,cols); - for (unsigned r = 0; r < rows; r++) - for (unsigned c = 0; c < cols; c++) - restore_fn(context, data(r,c)); - } - - //////////////////////////////////////////////////////////////////////////////// - -} // end namespace stlplus diff --git a/src/stlplus/persistence/persistent_multimap.hpp b/src/stlplus/persistence/persistent_multimap.hpp deleted file mode 100644 index 0ad0716..0000000 --- a/src/stlplus/persistence/persistent_multimap.hpp +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef STLPLUS_PERSISTENT_MULTIMAP -#define STLPLUS_PERSISTENT_MULTIMAP -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Persistence of STL multimap - -//////////////////////////////////////////////////////////////////////////////// -#include "persistence_fixes.hpp" -#include "persistent_contexts.hpp" -#include - -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - - template - void dump_multimap(dump_context&, const std::multimap& data, DK key_dump_fn, DT val_dump_fn) - throw(persistent_dump_failed); - - template - void restore_multimap(restore_context&, std::multimap& data, RK key_restore_fn, RT val_restore_fn) - throw(persistent_restore_failed); - -} // end namespace stlplus - - //////////////////////////////////////////////////////////////////////////////// -#include "persistent_multimap.tpp" -#endif diff --git a/src/stlplus/persistence/persistent_multimap.tpp b/src/stlplus/persistence/persistent_multimap.tpp deleted file mode 100644 index 5f96a3d..0000000 --- a/src/stlplus/persistence/persistent_multimap.tpp +++ /dev/null @@ -1,46 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// -#include "persistent_int.hpp" - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - - template - void dump_multimap(dump_context& context, const std::multimap& data, DK key_fn, DT val_fn) - throw(persistent_dump_failed) - { - dump_unsigned(context,data.size()); - for (typename std::multimap::const_iterator i = data.begin(); i != data.end(); i++) - { - key_fn(context,i->first); - val_fn(context,i->second); - } - } - - template - void restore_multimap(restore_context& context, std::multimap& data, RK key_fn, RT val_fn) - throw(persistent_restore_failed) - { - data.clear(); - unsigned size = 0; - restore_unsigned(context,size); - for (unsigned j = 0; j < size; j++) - { - K key; - key_fn(context,key); - typename std::multimap::iterator v = data.insert(std::pair(key,T())); - val_fn(context,v->second); - } - } - - //////////////////////////////////////////////////////////////////////////////// - -} // end namespace stlplus diff --git a/src/stlplus/persistence/persistent_multiset.hpp b/src/stlplus/persistence/persistent_multiset.hpp deleted file mode 100644 index d451a8e..0000000 --- a/src/stlplus/persistence/persistent_multiset.hpp +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef STLPLUS_PERSISTENT_MULTISET -#define STLPLUS_PERSISTENT_MULTISET -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Persistence of STL multiset - -//////////////////////////////////////////////////////////////////////////////// -#include "persistence_fixes.hpp" -#include "persistent_contexts.hpp" -#include - -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - - template - void dump_multiset(dump_context&, const std::multiset& data, D dump_fn) - throw(persistent_dump_failed); - - template - void restore_multiset(restore_context&, std::multiset& data, R restore_fn) - throw(persistent_restore_failed); - -} // end namespace stlplus - - //////////////////////////////////////////////////////////////////////////////// -#include "persistent_multiset.tpp" -#endif diff --git a/src/stlplus/persistence/persistent_multiset.tpp b/src/stlplus/persistence/persistent_multiset.tpp deleted file mode 100644 index 0a31353..0000000 --- a/src/stlplus/persistence/persistent_multiset.tpp +++ /dev/null @@ -1,44 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// -#include "persistent_int.hpp" - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - - template - void dump_multiset(dump_context& context, const std::multiset& data, D dump_fn) - throw(persistent_dump_failed) - { - dump_unsigned(context,data.size()); - for (typename std::multiset::const_iterator i = data.begin(); i != data.end(); i++) - dump_fn(context,*i); - } - - template - void restore_multiset(restore_context& context, std::multiset& data, R restore_fn) - throw(persistent_restore_failed) - { - data.clear(); - unsigned size = 0; - restore_unsigned(context,size); - typename std::multiset::iterator i = data.begin(); - for (unsigned j = 0; j < size; j++) - { - K key; - restore_fn(context,key); - // inserting using an iterator is O(n) rather than O(n*log(n)) - i = data.insert(i, key); - } - } - - //////////////////////////////////////////////////////////////////////////////// - -} // end namespace stlplus diff --git a/src/stlplus/persistence/persistent_ntree.hpp b/src/stlplus/persistence/persistent_ntree.hpp deleted file mode 100644 index 635da50..0000000 --- a/src/stlplus/persistence/persistent_ntree.hpp +++ /dev/null @@ -1,66 +0,0 @@ -#ifndef STLPLUS_PERSISTENT_NTREE -#define STLPLUS_PERSISTENT_NTREE -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Persistence of STLplus ntree - -//////////////////////////////////////////////////////////////////////////////// -#include "persistence_fixes.hpp" -#include "persistent_contexts.hpp" -#include "ntree.hpp" - -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - - // ntree - - template - void dump_ntree(dump_context&, const ntree& data, D dump_fn) - throw(persistent_dump_failed); - - template - void restore_ntree(restore_context&, ntree& data, R restore_fn) - throw(persistent_restore_failed); - - // iterator - - template - void dump_ntree_iterator(dump_context&, const ntree_iterator&) - throw(persistent_dump_failed); - - template - void restore_ntree_iterator(restore_context&, ntree_iterator&) - throw(persistent_restore_failed); - - // prefix iterator - - template - void dump_ntree_prefix_iterator(dump_context&, const ntree_prefix_iterator&) - throw(persistent_dump_failed); - - template - void restore_ntree_prefix_iterator(restore_context&, ntree_prefix_iterator&) - throw(persistent_restore_failed); - - // postfix iterator - - template - void dump_ntree_postfix_iterator(dump_context&, const ntree_postfix_iterator&) - throw(persistent_dump_failed); - - template - void restore_ntree_postfix_iterator(restore_context&, ntree_postfix_iterator&) - throw(persistent_restore_failed); - -} // end namespace stlplus - - //////////////////////////////////////////////////////////////////////////////// -#include "persistent_ntree.tpp" -#endif diff --git a/src/stlplus/persistence/persistent_ntree.tpp b/src/stlplus/persistence/persistent_ntree.tpp deleted file mode 100644 index d50ba17..0000000 --- a/src/stlplus/persistence/persistent_ntree.tpp +++ /dev/null @@ -1,174 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// -#include "persistent_bool.hpp" -#include "persistent_int.hpp" -#include "persistent_xref.hpp" - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - - template - void dump_ntree_r(dump_context& context, - const ntree& tree, - const TYPENAME ntree::const_iterator& node, - D dump_fn) - throw(persistent_dump_failed) - { - // the magic key of the ntree_node is dumped as well as the contents - this is used in iterator persistence - std::pair node_mapping = context.pointer_map(node.node()); - if (node_mapping.first) throw persistent_dump_failed("ntree: already dumped this node"); - dump_unsigned(context,node_mapping.second); - // now dump the contents - dump_fn(context,*node); - // dump the number of children - unsigned children = tree.children(node); - dump_unsigned(context,children); - // recurse on the children - for (unsigned i = 0; i < children; i++) - dump_ntree_r(context,tree,tree.child(node,i),dump_fn); - } - - template - void dump_ntree(dump_context& context, - const ntree& tree, - D dump_fn) - throw(persistent_dump_failed) - { - // dump a magic key to the address of the tree for use in persistence of iterators - // and register it as a dumped address - std::pair mapping = context.pointer_map(&tree); - if (mapping.first) throw persistent_dump_failed("ntree: already dumped this tree"); - dump_unsigned(context,mapping.second); - // now dump the tree contents - start with a flag to indicate whether the tree is empty - dump_bool(context, tree.empty()); - // now recursively dump the contents - if (!tree.empty()) - dump_ntree_r(context,tree,tree.root(),dump_fn); - } - - //////////////////////////////////////////////////////////////////////////////// - - template - void restore_ntree_r(restore_context& context, - ntree& tree, - const TYPENAME ntree::iterator& node, - R restore_fn) - throw(persistent_restore_failed) - { - // restore the node magic key, check whether it has been used before and add it to the set of known addresses - unsigned node_magic = 0; - restore_unsigned(context,node_magic); - std::pair node_mapping = context.pointer_map(node_magic); - if (node_mapping.first) throw persistent_restore_failed("ntree: restored this tree node already"); - context.pointer_add(node_magic,node.node()); - // now restore the node contents - restore_fn(context,*node); - // restore the number of children - unsigned children = 0; - restore_unsigned(context,children); - // recurse on each child - for (unsigned i = 0; i < children; i++) - { - typename ntree::iterator child = tree.insert(node,i,T()); - restore_ntree_r(context,tree,child,restore_fn); - } - } - - template - void restore_ntree(restore_context& context, - ntree& tree, - R restore_fn) - throw(persistent_restore_failed) - { - tree.erase(); - // restore the tree's magic key and map it onto the tree's address - // this is used in the persistence of iterators - unsigned magic = 0; - restore_unsigned(context,magic); - context.pointer_add(magic,&tree); - // now restore the contents - bool empty = true; - restore_bool(context, empty); - if (!empty) - { - typename ntree::iterator node = tree.insert(T()); - restore_ntree_r(context,tree,node,restore_fn); - } - } - - //////////////////////////////////////////////////////////////////////////////// - - template - void dump_ntree_iterator(dump_context& context, - const ntree_iterator& data) - throw(persistent_dump_failed) - { - data.assert_valid(); - dump_xref(context,data.owner()); - dump_xref(context,data.node()); - } - - template - void restore_ntree_iterator(restore_context& context, - ntree_iterator& data) - throw(persistent_restore_failed) - { - const ntree* owner = 0; - ntree_node* node = 0; - restore_xref(context,owner); - restore_xref(context,node); - data = ntree_iterator(node->m_master); - data.assert_valid(owner); - } - - //////////////////////////////////////////////////////////////////////////////// - - template - void dump_ntree_prefix_iterator(dump_context& context, - const ntree_prefix_iterator& data) - throw(persistent_dump_failed) - { - dump_ntree_iterator(context,data.iterator()); - } - - template - void restore_ntree_prefix_iterator(restore_context& context, - ntree_prefix_iterator& data) - throw(persistent_restore_failed) - { - ntree_iterator iterator; - restore_ntree_iterator(context,iterator); - data = ntree_prefix_iterator(iterator); - } - - //////////////////////////////////////////////////////////////////////////////// - - template - void dump_ntree_postfix_iterator(dump_context& context, - const ntree_postfix_iterator& data) - throw(persistent_dump_failed) - { - dump_ntree_iterator(context,data.iterator()); - } - - template - void restore_ntree_postfix_iterator(restore_context& context, - ntree_postfix_iterator& data) - throw(persistent_restore_failed) - { - ntree_iterator iterator; - restore_ntree_iterator(context,iterator); - data = ntree_postfix_iterator(iterator); - } - - //////////////////////////////////////////////////////////////////////////////// - -} // end namespace stlplus diff --git a/src/stlplus/persistence/persistent_pair.hpp b/src/stlplus/persistence/persistent_pair.hpp deleted file mode 100644 index 7d23107..0000000 --- a/src/stlplus/persistence/persistent_pair.hpp +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef STLPLUS_PERSISTENT_PAIR -#define STLPLUS_PERSISTENT_PAIR -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Persistence of STL pair - -//////////////////////////////////////////////////////////////////////////////// -#include "persistence_fixes.hpp" -#include "persistent_contexts.hpp" -#include - -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - - template - void dump_pair(dump_context&, const std::pair& data, D1 dump_fn1, D2 dump_fn2) - throw(persistent_dump_failed); - - template - void restore_pair(restore_context&, std::pair& data, R1 restore_fn1, R2 restore_fn2) - throw(persistent_restore_failed); - -} // end namespace stlplus - - //////////////////////////////////////////////////////////////////////////////// -#include "persistent_pair.tpp" -#endif diff --git a/src/stlplus/persistence/persistent_pair.tpp b/src/stlplus/persistence/persistent_pair.tpp deleted file mode 100644 index aa118cf..0000000 --- a/src/stlplus/persistence/persistent_pair.tpp +++ /dev/null @@ -1,34 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// -#include "persistent_pair.hpp" - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - - template - void dump_pair(dump_context& context, const std::pair& data, D1 dump_fn1, D2 dump_fn2) - throw(persistent_dump_failed) - { - dump_fn1(context,data.first); - dump_fn2(context,data.second); - } - - template - void restore_pair(restore_context& context, std::pair& data, R1 restore_fn1, R2 restore_fn2) - throw(persistent_restore_failed) - { - restore_fn1(context,data.first); - restore_fn2(context,data.second); - } - - //////////////////////////////////////////////////////////////////////////////// - -} // end namespace stlplus diff --git a/src/stlplus/persistence/persistent_pointer.hpp b/src/stlplus/persistence/persistent_pointer.hpp deleted file mode 100644 index 02d0951..0000000 --- a/src/stlplus/persistence/persistent_pointer.hpp +++ /dev/null @@ -1,47 +0,0 @@ -#ifndef STLPLUS_PERSISTENT_POINTER -#define STLPLUS_PERSISTENT_POINTER -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Persistence for pointers to persistent objects - -// Warning! The pointer must be a dynamically-allocated type, since the -// implementation uses new/delete - -// Multiple pointers to the same object *will* be restored as multiple pointers -// to the same object. The object is dumped only the first time it is -// encountered along with a "magic key". Subsequent pointers to the same object -// cause only the magic key to be dumped. On restore, the object is only -// restored once and the magic keys are matched up so that the other pointers -// now point to the restored object. - -// Supports null pointers too! If the data field to restore is null and the -// file format non-null, allocates a new T(). If the data field is non-null and -// the file format is null, deletes it and sets it null - -//////////////////////////////////////////////////////////////////////////////// -#include "persistence_fixes.hpp" -#include "persistent_contexts.hpp" - -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - - template - void dump_pointer(dump_context&, const T* const data, D dump_fn) - throw(persistent_dump_failed); - - template - void restore_pointer(restore_context&, T*& data, R restore_fn) - throw(persistent_restore_failed); - -} // end namespace stlplus - - //////////////////////////////////////////////////////////////////////////////// -#include "persistent_pointer.tpp" -#endif diff --git a/src/stlplus/persistence/persistent_pointer.tpp b/src/stlplus/persistence/persistent_pointer.tpp deleted file mode 100644 index c760b17..0000000 --- a/src/stlplus/persistence/persistent_pointer.tpp +++ /dev/null @@ -1,68 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// format: magic_key [ data ] - -//////////////////////////////////////////////////////////////////////////////// -#include "persistent_int.hpp" - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - - template - void dump_pointer(dump_context& context, const T* const data, D dump_fn) - throw(persistent_dump_failed) - { - // register the address and get the magic key for it - std::pair mapping = context.pointer_map(data); - dump_unsigned(context,mapping.second); - // if the address is null, then that is all that we need to do - // however, if it is non-null and this is the first sight of the address, dump the contents - // note that the address is mapped before it is dumped so that self-referential structures dump correctly - if (data && !mapping.first) - dump_fn(context,*data); - } - - //////////////////////////////////////////////////////////////////////////////// - - template - void restore_pointer(restore_context& context, T*& data, R restore_fn) - throw(persistent_restore_failed) - { - if (data) - { - delete data; - data = 0; - } - // get the magic key - unsigned magic = 0; - restore_unsigned(context,magic); - // now lookup the magic key to see if this pointer has already been restored - // null pointers are always flagged as already restored - std::pair address = context.pointer_map(magic); - if (address.first) - { - // seen before, so simply assign the old address - data = (T*)address.second; - } - else - { - // this pointer has never been seen before and is non-null - data = new T(); - // add this pointer to the set of already seen objects - // do this before restoring the object so that self-referential structures restore correctly - context.pointer_add(magic,data); - // now restore it - restore_fn(context,*data); - } - } - - //////////////////////////////////////////////////////////////////////////////// - -} // end namespace stlplus diff --git a/src/stlplus/persistence/persistent_pointers.hpp b/src/stlplus/persistence/persistent_pointers.hpp deleted file mode 100644 index 7e84759..0000000 --- a/src/stlplus/persistence/persistent_pointers.hpp +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef STLPLUS_PERSISTENT_POINTERS -#define STLPLUS_PERSISTENT_POINTERS -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Persistence of all pointer types - -//////////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////////// - -#include "persistent_pointer.hpp" -#include "persistent_xref.hpp" -#include "persistent_callback.hpp" -#include "persistent_interface.hpp" - -//////////////////////////////////////////////////////////////////////////////// -#endif diff --git a/src/stlplus/persistence/persistent_set.hpp b/src/stlplus/persistence/persistent_set.hpp deleted file mode 100644 index a2f8c7a..0000000 --- a/src/stlplus/persistence/persistent_set.hpp +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef STLPLUS_PERSISTENT_SET -#define STLPLUS_PERSISTENT_SET -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Set of persistence routines for the STL classes - -//////////////////////////////////////////////////////////////////////////////// -#include "persistence_fixes.hpp" -#include "persistent_contexts.hpp" -#include - -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - - template - void dump_set(dump_context&, const std::set& data, D dump_fn) - throw(persistent_dump_failed); - - template - void restore_set(restore_context&, std::set& data, R restore_fn) - throw(persistent_restore_failed); - -} // end namespace stlplus - - //////////////////////////////////////////////////////////////////////////////// -#include "persistent_set.tpp" -#endif diff --git a/src/stlplus/persistence/persistent_set.tpp b/src/stlplus/persistence/persistent_set.tpp deleted file mode 100644 index 007d068..0000000 --- a/src/stlplus/persistence/persistent_set.tpp +++ /dev/null @@ -1,44 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// -#include "persistent_int.hpp" - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - - template - void dump_set(dump_context& context, const std::set& data, D dump_fn) - throw(persistent_dump_failed) - { - dump_unsigned(context,data.size()); - for (typename std::set::const_iterator i = data.begin(); i != data.end(); i++) - dump_fn(context,*i); - } - - template - void restore_set(restore_context& context, std::set& data, R restore_fn) - throw(persistent_restore_failed) - { - data.clear(); - unsigned size = 0; - restore_unsigned(context,size); - typename std::set::iterator i = data.begin(); - for (unsigned j = 0; j < size; j++) - { - K key; - restore_fn(context,key); - // inserting using an iterator is O(n) rather than O(n*log(n)) - i = data.insert(i, key); - } - } - - //////////////////////////////////////////////////////////////////////////////// - -} // end namespace stlplus diff --git a/src/stlplus/persistence/persistent_shortcuts.hpp b/src/stlplus/persistence/persistent_shortcuts.hpp deleted file mode 100644 index beb54d0..0000000 --- a/src/stlplus/persistence/persistent_shortcuts.hpp +++ /dev/null @@ -1,70 +0,0 @@ -#ifndef STLPLUS_PERSISTENT_SHORTCUTS -#define STLPLUS_PERSISTENT_SHORTCUTS -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Short-cut functions for dumping and restoring to common targets. These do -// the whole dump operation in a single function call. - -// They take as their second template argument a dump or restore functor which -// is then called to perform the dump/restore operation. - -// They use an installer callback function to install any polymorphic type -// handlers required prior to performing the dump/restore. If there are no -// polymorphic types used in the data structure, then the callback can be set -// to null (i.e. 0). - -//////////////////////////////////////////////////////////////////////////////// -#include "persistence_fixes.hpp" -#include "persistent_contexts.hpp" - -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - // arbitrary IOStream device - // must be in binary mode - - template - void dump_to_device(const T& source, std::ostream& result, D dump_fn, dump_context::installer installer) - throw(persistent_dump_failed); - - template - void restore_from_device(std::istream& source, T& result, R restore_fn, restore_context::installer installer) - throw(persistent_restore_failed); - - //////////////////////////////////////////////////////////////////////////////// - // string IO device - - template - void dump_to_string(const T& source, std::string& result, D dump_fn, dump_context::installer installer) - throw(persistent_dump_failed); - - template - void restore_from_string(const std::string& source, T& result, R restore_fn, restore_context::installer installer) - throw(persistent_restore_failed); - - //////////////////////////////////////////////////////////////////////////////// - // file IO device - - template - void dump_to_file(const T& source, const std::string& filename, D dump_fn, dump_context::installer installer) - throw(persistent_dump_failed); - - template - void restore_from_file(const std::string& filename, T& result, R restore_fn, restore_context::installer installer) - throw(persistent_restore_failed); - - //////////////////////////////////////////////////////////////////////////////// - -} // end namespace stlplus - - //////////////////////////////////////////////////////////////////////////////// -#include "persistent_shortcuts.tpp" -#endif diff --git a/src/stlplus/persistence/persistent_shortcuts.tpp b/src/stlplus/persistence/persistent_shortcuts.tpp deleted file mode 100644 index 9b5aac5..0000000 --- a/src/stlplus/persistence/persistent_shortcuts.tpp +++ /dev/null @@ -1,80 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// -#include -#include - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - - template - void dump_to_device(const T& source, std::ostream& result, D dump_fn, - dump_context::installer installer) - throw(persistent_dump_failed) - { - dump_context context(result); - context.register_all(installer); - dump_fn(context, source); - } - - template - void restore_from_device(std::istream& source, T& result, R restore_fn, - restore_context::installer installer) - throw(persistent_restore_failed) - { - restore_context context(source); - context.register_all(installer); - restore_fn(context, result); - } - - //////////////////////////////////////////////////////////////////////////////// - - template - void dump_to_string(const T& source, std::string& result, D dump_fn, - dump_context::installer installer) - throw(persistent_dump_failed) - { - std::ostringstream output(std::ios_base::out | std::ios_base::binary); - dump_to_device(source, output, dump_fn, installer); - result = output.str(); - } - - template - void restore_from_string(const std::string& source, T& result, R restore_fn, - restore_context::installer installer) - throw(persistent_restore_failed) - { - std::istringstream input(source, std::ios_base::in | std::ios_base::binary); - restore_from_device(input, result, restore_fn, installer); - } - - //////////////////////////////////////////////////////////////////////////////// - - template - void dump_to_file(const T& source, const std::string& filename, D dump_fn, - dump_context::installer installer) - throw(persistent_dump_failed) - { - std::ofstream output(filename.c_str(), std::ios_base::out | std::ios_base::binary); - dump_to_device(source, output, dump_fn, installer); - } - - template - void restore_from_file(const std::string& filename, T& result, R restore_fn, - restore_context::installer installer) - throw(persistent_restore_failed) - { - std::ifstream input(filename.c_str(), std::ios_base::in | std::ios_base::binary); - restore_from_device(input, result, restore_fn, installer); - } - - //////////////////////////////////////////////////////////////////////////////// - -} // end namespace stlplus diff --git a/src/stlplus/persistence/persistent_simple_ptr.hpp b/src/stlplus/persistence/persistent_simple_ptr.hpp deleted file mode 100644 index ccf4260..0000000 --- a/src/stlplus/persistence/persistent_simple_ptr.hpp +++ /dev/null @@ -1,58 +0,0 @@ -#ifndef STLPLUS_PERSISTENT_SIMPLE_PTR -#define STLPLUS_PERSISTENT_SIMPLE_PTR -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Persistence of STLplus simple_ptr - -//////////////////////////////////////////////////////////////////////////////// -#include "persistence_fixes.hpp" -#include "persistent_contexts.hpp" -#include "simple_ptr.hpp" - -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - - // simple_ptr - uses dump/restore_pointer on the contents - - template - void dump_simple_ptr(dump_context&, const simple_ptr& data, DE dump_element) - throw(persistent_dump_failed); - - template - void restore_simple_ptr(restore_context&, simple_ptr& data, RE restore_element) - throw(persistent_restore_failed); - - // simple_ptr_clone using the polymorphic callback approach - uses dump/restore_callback on the contents - - template - void dump_simple_ptr_clone_callback(dump_context&, const simple_ptr_clone& data) - throw(persistent_dump_failed); - - template - void restore_simple_ptr_clone_callback(restore_context&, simple_ptr_clone& data) - throw(persistent_restore_failed); - - // simple_ptr_clone using the interface approach - uses dump/restore_interface on the contents - - template - void dump_simple_ptr_clone_interface(dump_context&, const simple_ptr_clone& data) - throw(persistent_dump_failed); - - template - void restore_simple_ptr_clone_interface(restore_context&, simple_ptr_clone& data) - throw(persistent_restore_failed); - - // simple_ptr_nocopy is not made persistent because if it is uncopyable, it must be undumpable - -} // end namespace stlplus - - //////////////////////////////////////////////////////////////////////////////// -#include "persistent_simple_ptr.tpp" -#endif diff --git a/src/stlplus/persistence/persistent_simple_ptr.tpp b/src/stlplus/persistence/persistent_simple_ptr.tpp deleted file mode 100644 index 36f88bd..0000000 --- a/src/stlplus/persistence/persistent_simple_ptr.tpp +++ /dev/null @@ -1,144 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// -#include "persistent_int.hpp" -#include "persistent_pointer.hpp" -#include "persistent_callback.hpp" -#include "persistent_interface.hpp" - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - - template - void dump_simple_ptr(dump_context& context, const simple_ptr& data, - DE dump_element) - throw(persistent_dump_failed) - { - // Many smart pointers can point to the same object. - // I could have used the address of the object to differentiate, - // but that would not have differentiated between different null smart pointers - // so I use the address of the count to differentiate between different objects. - // get a magic key for the substructure - this also returns a flag saying whether its been seen before - std::pair mapping = context.pointer_map(data._count()); - // dump the magic key for the count - dump_unsigned(context,mapping.second); - // dump the contents always - this is because I need to rely on the pointer routines dumping a second magic key - // use the existing routines for ordinary pointers to dump the contents - dump_pointer(context,data._pointer(),dump_element); - } - - template - void restore_simple_ptr(restore_context& context, simple_ptr& data, - RE restore_element) - throw(persistent_restore_failed) - { - // get the old counter magic key - unsigned magic = 0; - restore_unsigned(context,magic); - // lookup this magic number to see if we have seen this already - std::pair mapping = context.pointer_map(magic); - if (mapping.first) - { - // this holder has already been restored - // now restore the object and rely on the pointer routines to return the existing object - T* value = 0; - restore_pointer(context,value,restore_element); - // dealias the existing holder and replace it with the seen-before holder to make this object an alias of it - data._make_alias(value, (unsigned*)mapping.second); - } - else - { - // this is the first contact with this holder - // make sure this smart pointer is unique to prevent side-effects - data.clear_unique(); - // map the magic key onto this structure's holder - // do this before restoring the object so that self-referential structures restore correctly - context.pointer_add(magic,data._count()); - // now restore the object - T* value = 0; - restore_pointer(context,value,restore_element); - // and add it to the pointer - data.set(value); - } - } - - //////////////////////////////////////////////////////////////////////////////// - // simple_ptr_clone using callbacks - - template - void dump_simple_ptr_clone_callback(dump_context& context, const simple_ptr_clone& data) - throw(persistent_dump_failed) - { - std::pair mapping = context.pointer_map(data._count()); - dump_unsigned(context,mapping.second); - dump_callback(context,data._pointer()); - } - - template - void restore_simple_ptr_clone_callback(restore_context& context, simple_ptr_clone& data) - throw(persistent_restore_failed) - { - unsigned magic = 0; - restore_unsigned(context,magic); - std::pair mapping = context.pointer_map(magic); - if (mapping.first) - { - T* value = 0; - restore_callback(context,value); - data._make_alias(value, (unsigned*)mapping.second); - } - else - { - data.clear_unique(); - context.pointer_add(magic,data._count()); - T* value = 0; - restore_callback(context,value); - data.set(value); - } - } - - //////////////////////////////////////////////////////////////////////////////// - // simple_ptr_clone using interface - - template - void dump_simple_ptr_clone_interface(dump_context& context, const simple_ptr_clone& data) - throw(persistent_dump_failed) - { - std::pair mapping = context.pointer_map(data._count()); - dump_unsigned(context,mapping.second); - dump_interface(context,data._pointer()); - } - - template - void restore_simple_ptr_clone_interface(restore_context& context, simple_ptr_clone& data) - throw(persistent_restore_failed) - { - unsigned magic = 0; - restore_unsigned(context,magic); - std::pair mapping = context.pointer_map(magic); - if (mapping.first) - { - T* value = 0; - restore_interface(context,value); - data._make_alias(value, (unsigned*)mapping.second); - } - else - { - data.clear_unique(); - context.pointer_add(magic,data._count()); - T* value = 0; - restore_interface(context,value); - data.set(value); - } - } - - //////////////////////////////////////////////////////////////////////////////// - -} // end namespace stlplus diff --git a/src/stlplus/persistence/persistent_smart_ptr.hpp b/src/stlplus/persistence/persistent_smart_ptr.hpp deleted file mode 100644 index 5f37725..0000000 --- a/src/stlplus/persistence/persistent_smart_ptr.hpp +++ /dev/null @@ -1,58 +0,0 @@ -#ifndef STLPLUS_PERSISTENT_SMART_PTR -#define STLPLUS_PERSISTENT_SMART_PTR -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Persistence of STLplus smart_ptr - -//////////////////////////////////////////////////////////////////////////////// -#include "persistence_fixes.hpp" -#include "persistent_contexts.hpp" -#include "smart_ptr.hpp" - -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - - // smart_ptr - uses dump/restore_pointer on the contents - - template - void dump_smart_ptr(dump_context&, const smart_ptr& data, DE dump_element) - throw(persistent_dump_failed); - - template - void restore_smart_ptr(restore_context&, smart_ptr& data, RE restore_element) - throw(persistent_restore_failed); - - // smart_ptr_clone using the polymorphic callback approach - uses dump/restore_callback on the contents - - template - void dump_smart_ptr_clone_callback(dump_context&, const smart_ptr_clone& data) - throw(persistent_dump_failed); - - template - void restore_smart_ptr_clone_callback(restore_context&, smart_ptr_clone& data) - throw(persistent_restore_failed); - - // smart_ptr_clone using the interface approach - uses dump/restore_interface on the contents - - template - void dump_smart_ptr_clone_interface(dump_context&, const smart_ptr_clone& data) - throw(persistent_dump_failed); - - template - void restore_smart_ptr_clone_interface(restore_context&, smart_ptr_clone& data) - throw(persistent_restore_failed); - - // smart_ptr_nocopy is not made persistent because if it is uncopyable, it must be undumpable - -} // end namespace stlplus - - //////////////////////////////////////////////////////////////////////////////// -#include "persistent_smart_ptr.tpp" -#endif diff --git a/src/stlplus/persistence/persistent_smart_ptr.tpp b/src/stlplus/persistence/persistent_smart_ptr.tpp deleted file mode 100644 index 2ce5315..0000000 --- a/src/stlplus/persistence/persistent_smart_ptr.tpp +++ /dev/null @@ -1,176 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// -#include "persistent_int.hpp" -#include "persistent_pointer.hpp" -#include "persistent_callback.hpp" -#include "persistent_interface.hpp" - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - - template - void dump_smart_ptr(dump_context& context, const smart_ptr& data, - DE dump_element) - throw(persistent_dump_failed) - { - // similar to the simple pointer routines, but use the address of the holder to tell which objects are aliases - // Many smart pointers can point to the same object. - // I could have used the address of the object to differentiate, - // but that would not have differentiated between different null smart pointers - // so I use the address of the substructure to differentiate between different objects. - // get a magic key for the substructure - this also returns a flag saying whether its been seen before - std::pair mapping = context.pointer_map(data._handle()); - // dump the magic key - dump_unsigned(context,mapping.second); - // dump the contents but only if this is the first time this object has been seen - // use the existing routines for ordinary pointers to dump the contents - if (!mapping.first) - dump_pointer(context,data.pointer(),dump_element); - } - - template - void restore_smart_ptr(restore_context& context, smart_ptr& data, - RE restore_element) - throw(persistent_restore_failed) - { - // get the old substructure magic key - unsigned magic = 0; - restore_unsigned(context,magic); - // lookup this magic number to see if we have seen this already - std::pair mapping = context.pointer_map(magic); - if (mapping.first) - { - // this holder has already been restored - // dealias the existing holder and replace it with the seen-before holder to make this object an alias of it - data._make_alias((smart_ptr_holder*)mapping.second); - } - else - { - // this is the first contact with this holder - // make sure this smart pointer is unique to prevent side-effects - data.clear_unique(); - // map the magic key onto this structure's holder - // do this before restoring the object so that self-referential structures restore correctly - context.pointer_add(magic,data._handle()); - // now restore the object - T* value = 0; - restore_pointer(context,value,restore_element); - data.set(value); - } - } - - //////////////////////////////////////////////////////////////////////////////// - // smart_ptr_clone using callbacks - - template - void dump_smart_ptr_clone_callback(dump_context& context, const smart_ptr_clone& data) - throw(persistent_dump_failed) - { - // similar to the simple pointer routines, but use the address of the holder to tell which objects are aliases - // Many smart pointers can point to the same object. - // I could have used the address of the object to differentiate, - // but that would not have differentiated between different null smart pointers - // so I use the address of the substructure to differentiate between different objects. - // get a magic key for the substructure - this also returns a flag saying whether its been seen before - std::pair mapping = context.pointer_map(data._handle()); - // dump the magic key - dump_unsigned(context,mapping.second); - // dump the contents but only if this is the first time this object has been seen - // use the existing routines for ordinary pointers to dump the contents - if (!mapping.first) - dump_callback(context,data.pointer()); - } - - template - void restore_smart_ptr_clone_callback(restore_context& context, smart_ptr_clone& data) - throw(persistent_restore_failed) - { - // get the old substructure magic key - unsigned magic = 0; - restore_unsigned(context,magic); - // lookup this magic number to see if we have seen this already - std::pair mapping = context.pointer_map(magic); - if (mapping.first) - { - // this holder has already been restored - // dealias the existing holder and replace it with the seen-before holder to make this object an alias of it - data._make_alias((smart_ptr_holder*)mapping.second); - } - else - { - // this is the first contact with this holder - // make sure this smart pointer is unique to prevent side-effects - data.clear_unique(); - // map the magic key onto this structure's holder - // do this before restoring the object so that self-referential structures restore correctly - context.pointer_add(magic,data._handle()); - // now restore the object - T* value = 0; - restore_callback(context,value); - data.set(value); - } - } - - //////////////////////////////////////////////////////////////////////////////// - // smart_ptr_clone using interface - - template - void dump_smart_ptr_clone_interface(dump_context& context, const smart_ptr_clone& data) - throw(persistent_dump_failed) - { - // similar to the simple pointer routines, but use the address of the holder to tell which objects are aliases - // Many smart pointers can point to the same object. - // I could have used the address of the object to differentiate, - // but that would not have differentiated between different null smart pointers - // so I use the address of the substructure to differentiate between different objects. - // get a magic key for the substructure - this also returns a flag saying whether its been seen before - std::pair mapping = context.pointer_map(data._handle()); - // dump the magic key - dump_unsigned(context,mapping.second); - // dump the contents but only if this is the first time this object has been seen - // use the existing routines for ordinary pointers to dump the contents - if (!mapping.first) - dump_interface(context,data.pointer()); - } - - template - void restore_smart_ptr_clone_interface(restore_context& context, smart_ptr_clone& data) - throw(persistent_restore_failed) - { - // get the old substructure magic key - unsigned magic = 0; - restore_unsigned(context,magic); - // lookup this magic number to see if we have seen this already - std::pair mapping = context.pointer_map(magic); - if (mapping.first) - { - // this holder has already been restored - // dealias the existing holder and replace it with the seen-before holder to make this object an alias of it - data._make_alias((smart_ptr_holder*)mapping.second); - } - else - { - // this is the first contact with this holder - // make sure this smart pointer is unique to prevent side-effects - data.clear_unique(); - // map the magic key onto this structure's holder - // do this before restoring the object so that self-referential structures restore correctly - context.pointer_add(magic,data._handle()); - // now restore the object - T* value = 0; - restore_interface(context,value); - data.set(value); - } - } - - //////////////////////////////////////////////////////////////////////////////// - -} // end namespace stlplus diff --git a/src/stlplus/persistence/persistent_stl.hpp b/src/stlplus/persistence/persistent_stl.hpp deleted file mode 100644 index bc2f32b..0000000 --- a/src/stlplus/persistence/persistent_stl.hpp +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef STLPLUS_PERSISTENT_STL -#define STLPLUS_PERSISTENT_STL -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Set of persistence routines for the STL classes - -//////////////////////////////////////////////////////////////////////////////// - -#include "persistent_bitset.hpp" -#include "persistent_complex.hpp" -#include "persistent_deque.hpp" -#include "persistent_list.hpp" -#include "persistent_map.hpp" -#include "persistent_multimap.hpp" -#include "persistent_multiset.hpp" -#include "persistent_pair.hpp" -#include "persistent_set.hpp" -#include "persistent_string.hpp" -#include "persistent_vector.hpp" - -#endif diff --git a/src/stlplus/persistence/persistent_stlplus.hpp b/src/stlplus/persistence/persistent_stlplus.hpp deleted file mode 100644 index a3cb476..0000000 --- a/src/stlplus/persistence/persistent_stlplus.hpp +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef STLPLUS_PERSISTENT_STLPLUS -#define STLPLUS_PERSISTENT_STLPLUS -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Set of persistence routines for the STLplus classes - -//////////////////////////////////////////////////////////////////////////////// - -// can be excluded to break the dependency on the containers library -#ifndef NO_STLPLUS_CONTAINERS -#include "persistent_digraph.hpp" -#include "persistent_foursome.hpp" -#include "persistent_hash.hpp" -#include "persistent_matrix.hpp" -#include "persistent_ntree.hpp" -#include "persistent_smart_ptr.hpp" -#include "persistent_triple.hpp" -#endif - -// can be excluded to break the dependency on the portability library -#ifndef NO_STLPLUS_INF -#include "persistent_inf.hpp" -#endif - -//////////////////////////////////////////////////////////////////////////////// -#endif diff --git a/src/stlplus/persistence/persistent_string.cpp b/src/stlplus/persistence/persistent_string.cpp deleted file mode 100644 index 80940d1..0000000 --- a/src/stlplus/persistence/persistent_string.cpp +++ /dev/null @@ -1,25 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// -#include "persistent_string.hpp" - -//////////////////////////////////////////////////////////////////////////////// - -void stlplus::dump_string(stlplus::dump_context& context, const std::string& data) - throw(stlplus::persistent_dump_failed) -{ - stlplus::dump_basic_string(context, data, stlplus::dump_char); -} - -void stlplus::restore_string(stlplus::restore_context& context, std::string& data) - throw(stlplus::persistent_restore_failed) -{ - stlplus::restore_basic_string(context, data, stlplus::restore_char); -} - -//////////////////////////////////////////////////////////////////////////////// diff --git a/src/stlplus/persistence/persistent_string.hpp b/src/stlplus/persistence/persistent_string.hpp deleted file mode 100644 index ff065c7..0000000 --- a/src/stlplus/persistence/persistent_string.hpp +++ /dev/null @@ -1,48 +0,0 @@ -#ifndef STLPLUS_PERSISTENT_STRING -#define STLPLUS_PERSISTENT_STRING -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Persistence for STL strings - -//////////////////////////////////////////////////////////////////////////////// -#include "persistence_fixes.hpp" -#include "persistent_contexts.hpp" -#include - -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - - // basic_string - - template - void dump_basic_string(dump_context&, const std::basic_string& data, D dump_fn) - throw(persistent_dump_failed); - - template - void restore_basic_string(restore_context&, std::basic_string& data, R restore_fn) - throw(persistent_restore_failed); - - // string - - void dump_string(dump_context&, const std::string& data) - throw(persistent_dump_failed); - - void restore_string(restore_context&, std::string& data) - throw(persistent_restore_failed); - - - // Note: persistence of wstring not supported because it is too weakly defined and messy - // decide on a byte-wide encoding of wide strings (e.g. UTF8) and use the string persistence on that - -} // end namespace stlplus - - //////////////////////////////////////////////////////////////////////////////// -#include "persistent_string.tpp" -#endif diff --git a/src/stlplus/persistence/persistent_string.tpp b/src/stlplus/persistence/persistent_string.tpp deleted file mode 100644 index c78f7e8..0000000 --- a/src/stlplus/persistence/persistent_string.tpp +++ /dev/null @@ -1,47 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// -#include "persistent_int.hpp" - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - // STL strings - - template - void dump_basic_string(dump_context& context, const std::basic_string& data, D dump_fn) - throw(persistent_dump_failed) - { - unsigned size = data.size(); - dump_unsigned(context, size); - for (unsigned i = 0; i < size; i++) - { - charT ch = data[i]; - dump_fn(context,ch); - } - } - - template - void restore_basic_string(restore_context& context, std::basic_string& data, R restore_fn) - throw(persistent_restore_failed) - { - data.erase(); - unsigned size = 0; - restore_unsigned(context, size); - for (unsigned i = 0; i < size; i++) - { - charT ch; - restore_fn(context,ch); - data += ch; - } - } - - //////////////////////////////////////////////////////////////////////////////// - -} // end namespace stlplus diff --git a/src/stlplus/persistence/persistent_triple.hpp b/src/stlplus/persistence/persistent_triple.hpp deleted file mode 100644 index d9e30d6..0000000 --- a/src/stlplus/persistence/persistent_triple.hpp +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef STLPLUS_PERSISTENT_TRIPLE -#define STLPLUS_PERSISTENT_TRIPLE -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Persistence of STL triple - -//////////////////////////////////////////////////////////////////////////////// -#include "persistence_fixes.hpp" -#include "persistent_contexts.hpp" -#include "triple.hpp" - -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - - template - void dump_triple(dump_context&, const stlplus::triple& data, - D1 dump_fn1, D2 dump_fn2, D3 dump_fn3) - throw(persistent_dump_failed); - - template - void restore_triple(restore_context&, stlplus::triple& data, - R1 restore_fn1, R2 restore_fn2, R3 restore_fn3) - throw(persistent_restore_failed); - -} // end namespace stlplus - - //////////////////////////////////////////////////////////////////////////////// -#include "persistent_triple.tpp" -#endif diff --git a/src/stlplus/persistence/persistent_triple.tpp b/src/stlplus/persistence/persistent_triple.tpp deleted file mode 100644 index 9c3c978..0000000 --- a/src/stlplus/persistence/persistent_triple.tpp +++ /dev/null @@ -1,37 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - - template - void dump_triple(dump_context& context, const triple& data, - D1 dump_fn1, D2 dump_fn2, D3 dump_fn3) - throw(persistent_dump_failed) - { - dump_fn1(context,data.first); - dump_fn2(context,data.second); - dump_fn3(context,data.third); - } - - template - void restore_triple(restore_context& context, triple& data, - R1 restore_fn1, R2 restore_fn2, R3 restore_fn3) - throw(persistent_restore_failed) - { - restore_fn1(context,data.first); - restore_fn2(context,data.second); - restore_fn3(context,data.third); - } - - //////////////////////////////////////////////////////////////////////////////// - -} // end namespace stlplus diff --git a/src/stlplus/persistence/persistent_vector.cpp b/src/stlplus/persistence/persistent_vector.cpp deleted file mode 100644 index b983d37..0000000 --- a/src/stlplus/persistence/persistent_vector.cpp +++ /dev/null @@ -1,56 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// -#include "persistent_vector.hpp" - -//////////////////////////////////////////////////////////////////////////////// -// specialisation for a vector of bool which has a different implementation to a vector of anything else - -void stlplus::dump_vector_bool(stlplus::dump_context& context, const std::vector& data) - throw(stlplus::persistent_dump_failed) -{ - stlplus::dump_unsigned(context,data.size()); - unsigned size = data.size(); - unsigned bytes = ((size + 7) / 8); - for (unsigned b = 0; b < bytes; b++) - { - unsigned char byte = 0; - unsigned char mask = 1; - for (unsigned e = 0; e < 8; e++) - { - unsigned i = b*8 + e; - if (i >= size) break; - if (data[i]) byte |= mask; - mask <<= 1; - } - context.put(byte); - } -} - -void stlplus::restore_vector_bool(stlplus::restore_context& context, std::vector& data) - throw(stlplus::persistent_restore_failed) -{ - unsigned size = 0; - stlplus::restore_unsigned(context,size); - data.resize(size); - unsigned bytes = ((size + 7) / 8); - for (unsigned b = 0; b < bytes; b++) - { - unsigned char byte = context.get(); - unsigned char mask = 1; - for (unsigned e = 0; e < 8; e++) - { - unsigned i = b*8 + e; - if (i >= size) break; - data[i] = ((byte & mask) != 0); - mask <<= 1; - } - } -} - -//////////////////////////////////////////////////////////////////////////////// diff --git a/src/stlplus/persistence/persistent_vector.hpp b/src/stlplus/persistence/persistent_vector.hpp deleted file mode 100644 index fd108aa..0000000 --- a/src/stlplus/persistence/persistent_vector.hpp +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef STLPLUS_PERSISTENT_VECTOR -#define STLPLUS_PERSISTENT_VECTOR -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Persistence of STL vector - -//////////////////////////////////////////////////////////////////////////////// -#include "persistence_fixes.hpp" -#include "persistent_contexts.hpp" -#include - -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - - // vector - - template - void dump_vector(dump_context&, const std::vector& data, D dump_fn) - throw(persistent_dump_failed); - - template - void restore_vector(restore_context&, std::vector& data, R restore_fn) - throw(persistent_restore_failed); - - // specialism for vector - - void dump_vector_bool(dump_context&, const std::vector& data) - throw(persistent_dump_failed); - - void restore_vector_bool(restore_context&, std::vector& data) - throw(persistent_restore_failed); - -} // end namespace stlplus - - //////////////////////////////////////////////////////////////////////////////// -#include "persistent_vector.tpp" -#endif diff --git a/src/stlplus/persistence/persistent_vector.tpp b/src/stlplus/persistence/persistent_vector.tpp deleted file mode 100644 index 4ad69b6..0000000 --- a/src/stlplus/persistence/persistent_vector.tpp +++ /dev/null @@ -1,39 +0,0 @@ - -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// -#include "persistent_int.hpp" - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - - template - void dump_vector(dump_context& context, const std::vector& data, D dump_fn) - throw(persistent_dump_failed) - { - dump_unsigned(context,data.size()); - for (unsigned i = 0; i < data.size(); i++) - dump_fn(context,data[i]); - } - - template - void restore_vector(restore_context& context, std::vector& data, R restore_fn) - throw(persistent_restore_failed) - { - unsigned size = 0; - restore_unsigned(context,size); - data.resize(size); - for (unsigned i = 0; i < size; i++) - restore_fn(context,data[i]); - } - - //////////////////////////////////////////////////////////////////////////////// - -} // end namespace stlplus diff --git a/src/stlplus/persistence/persistent_xref.hpp b/src/stlplus/persistence/persistent_xref.hpp deleted file mode 100644 index c41e28d..0000000 --- a/src/stlplus/persistence/persistent_xref.hpp +++ /dev/null @@ -1,48 +0,0 @@ -#ifndef STLPLUS_PERSISTENT_XREF -#define STLPLUS_PERSISTENT_XREF -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Persistence for cross-references to persistent objects - -// A cross-reference is a pointer to an object that has definitely been dumped -// already by one of dump_pointer, dump_interface or dump_callback, i.e. by -// one of the dump routines for pointers to objects. - -// These are typically used in data structures as back-pointers or pointers -// between nodes. - -// For example, you may have a tree with cross links. Dump the tree as the -// primary data structure first, then dump the cross links as cross-references -// afterwards. The whole tree must be dumped before any cross-references to -// ensure that all cross-references are known to the persistence system. - -// These functions will throw an exception if the cross-reference points to -// something not dumped before. - -//////////////////////////////////////////////////////////////////////////////// -#include "persistence_fixes.hpp" -#include "persistent_contexts.hpp" - -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - - template - void dump_xref(dump_context&, const T* const data) - throw(persistent_dump_failed); - - template - void restore_xref(restore_context&, T*& data) - throw(persistent_restore_failed); - -} // end namespace stlplus - - //////////////////////////////////////////////////////////////////////////////// -#include "persistent_xref.tpp" -#endif diff --git a/src/stlplus/persistence/persistent_xref.tpp b/src/stlplus/persistence/persistent_xref.tpp deleted file mode 100644 index e12639d..0000000 --- a/src/stlplus/persistence/persistent_xref.tpp +++ /dev/null @@ -1,51 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// format: magic_key [ data ] - -//////////////////////////////////////////////////////////////////////////////// -#include "persistent_int.hpp" - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - - template - void dump_xref(dump_context& context, const T* const data) - throw(persistent_dump_failed) - { - // register the address and get the magic key for it - std::pair mapping = context.pointer_map(data); - // if this is the first view of this pointer, simply throw an exception - if (!mapping.first) throw persistent_dump_failed("tried to dump a cross-reference not seen before"); - // otherwise, just dump the magic key - dump_unsigned(context,mapping.second); - } - - ////////////////////////////////////////////////////////////////////////////////> - - template - void restore_xref(restore_context& context, T*& data) - throw(persistent_restore_failed) - { - // Note: I do not try to delete the old data because this is a cross-reference - // get the magic key - unsigned magic = 0; - restore_unsigned(context,magic); - // now lookup the magic key to see if this pointer has already been restored - // null pointers are always flagged as already restored - std::pair address = context.pointer_map(magic); - // if this is the first view of this pointer, simply throw an exception - if (!address.first) throw persistent_restore_failed("tried to restore a cross-reference not seen before"); - // seen before, so simply assign the old address - data = (T*)address.second; - } - - //////////////////////////////////////////////////////////////////////////////// - -} // end namespace stlplus diff --git a/src/stlplus/portability/build.cpp b/src/stlplus/portability/build.cpp index 6a9cd64..e591a34 100644 --- a/src/stlplus/portability/build.cpp +++ b/src/stlplus/portability/build.cpp @@ -1,60 +1,60 @@ -//////////////////////////////////////////////////////////////////////////////// - -// 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 +//////////////////////////////////////////////////////////////////////////////// + +// 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 diff --git a/src/stlplus/portability/build.hpp b/src/stlplus/portability/build.hpp index b105f19..a648beb 100644 --- a/src/stlplus/portability/build.hpp +++ b/src/stlplus/portability/build.hpp @@ -1,34 +1,34 @@ -#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 - -namespace stlplus -{ - - std::string build(void); - -} -//////////////////////////////////////////////////////////////////////////////// -#endif +#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 + +namespace stlplus +{ + + std::string build(void); + +} +//////////////////////////////////////////////////////////////////////////////// +#endif diff --git a/src/stlplus/portability/debug.cpp b/src/stlplus/portability/debug.cpp index 562518e..d5b1534 100644 --- a/src/stlplus/portability/debug.cpp +++ b/src/stlplus/portability/debug.cpp @@ -1,187 +1,187 @@ -//////////////////////////////////////////////////////////////////////////////// - -// 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 -#include -#include - -//////////////////////////////////////////////////////////////////////////////// - -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 +//////////////////////////////////////////////////////////////////////////////// + +// 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 +#include +#include + +//////////////////////////////////////////////////////////////////////////////// + +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 diff --git a/src/stlplus/portability/debug.hpp b/src/stlplus/portability/debug.hpp index a7aafd6..981490b 100644 --- a/src/stlplus/portability/debug.hpp +++ b/src/stlplus/portability/debug.hpp @@ -1,128 +1,128 @@ -#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 -#include - -//////////////////////////////////////////////////////////////////////////////// -// 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 +#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 +#include + +//////////////////////////////////////////////////////////////////////////////// +// 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 diff --git a/src/stlplus/portability/dprintf.cpp b/src/stlplus/portability/dprintf.cpp index f03217e..b0e05c6 100644 --- a/src/stlplus/portability/dprintf.cpp +++ b/src/stlplus/portability/dprintf.cpp @@ -1,92 +1,92 @@ -//////////////////////////////////////////////////////////////////////////////// - -// 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 -#include -#include -#include -#include - -//////////////////////////////////////////////////////////////////////////////// - -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 +//////////////////////////////////////////////////////////////////////////////// + +// 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 +#include +#include +#include +#include + +//////////////////////////////////////////////////////////////////////////////// + +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 diff --git a/src/stlplus/portability/dprintf.hpp b/src/stlplus/portability/dprintf.hpp index f7de78b..360e5c1 100644 --- a/src/stlplus/portability/dprintf.hpp +++ b/src/stlplus/portability/dprintf.hpp @@ -1,122 +1,122 @@ -#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 -#include -#include - -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 +#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 +#include +#include + +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 diff --git a/src/stlplus/portability/dynaload.cpp b/src/stlplus/portability/dynaload.cpp index cdd1848..4cd5741 100644 --- a/src/stlplus/portability/dynaload.cpp +++ b/src/stlplus/portability/dynaload.cpp @@ -1,184 +1,184 @@ -//////////////////////////////////////////////////////////////////////////////// - -// 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 -#else -#include -#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 +//////////////////////////////////////////////////////////////////////////////// + +// 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 +#else +#include +#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 diff --git a/src/stlplus/portability/dynaload.hpp b/src/stlplus/portability/dynaload.hpp index dafaf24..d35d324 100644 --- a/src/stlplus/portability/dynaload.hpp +++ b/src/stlplus/portability/dynaload.hpp @@ -1,86 +1,86 @@ -#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 - -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 +#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 + +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 diff --git a/src/stlplus/portability/file_system.cpp b/src/stlplus/portability/file_system.cpp index 983d984..cfca256 100644 --- a/src/stlplus/portability/file_system.cpp +++ b/src/stlplus/portability/file_system.cpp @@ -1,1058 +1,1058 @@ -//////////////////////////////////////////////////////////////////////////////// - -// 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 -#include -#include -#include -#include - -#ifdef MSWINDOWS -#include -#include -#include -#include -#include -#include -#include -#else -#include -#include -#include -#include -#include -#include -#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 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& path(void) const {return m_path;} - std::vector& path(void) {return m_path;} - void set_path(const std::vector& 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 subdirectories = folder_subdirectories(dir); - for (std::vector::size_type d = 0; d < subdirectories.size(); ++d) - if (!folder_delete(folder_down(dir,subdirectories[d]),true)) - result = false; - std::vector files = folder_files(dir); - for (std::vector::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 folder_subdirectories (const std::string& directory) - { - return folder_wildcard(directory, "*", true, false); - } - - std::vector folder_files (const std::string& directory) - { - return folder_wildcard(directory, "*", false, true); - } - - std::vector folder_all(const std::string& directory) - { - return folder_wildcard(directory, "*", true, true); - } - - std::vector folder_wildcard (const std::string& directory, const std::string& wild, bool subdirs, bool files) - { - std::string dir = directory.empty() ? std::string(".") : directory; - std::vector 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 filespec_elements(const std::string& filespec) - { - file_specification spec; - spec.initialise_file(filespec); - std::vector 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 folder_elements(const std::string& folder) - { - file_specification spec; - spec.initialise_folder(folder); - std::vector 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 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 +//////////////////////////////////////////////////////////////////////////////// + +// 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 +#include +#include +#include +#include + +#ifdef MSWINDOWS +#include +#include +#include +#include +#include +#include +#include +#else +#include +#include +#include +#include +#include +#include +#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 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& path(void) const {return m_path;} + std::vector& path(void) {return m_path;} + void set_path(const std::vector& 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 subdirectories = folder_subdirectories(dir); + for (std::vector::size_type d = 0; d < subdirectories.size(); ++d) + if (!folder_delete(folder_down(dir,subdirectories[d]),true)) + result = false; + std::vector files = folder_files(dir); + for (std::vector::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 folder_subdirectories (const std::string& directory) + { + return folder_wildcard(directory, "*", true, false); + } + + std::vector folder_files (const std::string& directory) + { + return folder_wildcard(directory, "*", false, true); + } + + std::vector folder_all(const std::string& directory) + { + return folder_wildcard(directory, "*", true, true); + } + + std::vector folder_wildcard (const std::string& directory, const std::string& wild, bool subdirs, bool files) + { + std::string dir = directory.empty() ? std::string(".") : directory; + std::vector 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 filespec_elements(const std::string& filespec) + { + file_specification spec; + spec.initialise_file(filespec); + std::vector 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 folder_elements(const std::string& folder) + { + file_specification spec; + spec.initialise_folder(folder); + std::vector 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 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 diff --git a/src/stlplus/portability/file_system.hpp b/src/stlplus/portability/file_system.hpp index 374e86d..ce33ffe 100644 --- a/src/stlplus/portability/file_system.hpp +++ b/src/stlplus/portability/file_system.hpp @@ -1,201 +1,201 @@ -#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 -#include -#include -//////////////////////////////////////////////////////////////////////////////// - -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 folder_subdirectories(const std::string& folder); - // the set of all files - std::vector folder_files(const std::string& folder); - // the set of all folders and files - std::vector 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 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 folder_elements(const std::string& folder); - std::vector 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 +#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 +#include +#include +//////////////////////////////////////////////////////////////////////////////// + +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 folder_subdirectories(const std::string& folder); + // the set of all files + std::vector folder_files(const std::string& folder); + // the set of all folders and files + std::vector 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 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 folder_elements(const std::string& folder); + std::vector 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 diff --git a/src/stlplus/portability/inf.cpp b/src/stlplus/portability/inf.cpp index 80b0475..d70ab76 100644 --- a/src/stlplus/portability/inf.cpp +++ b/src/stlplus/portability/inf.cpp @@ -1,1482 +1,1482 @@ -//////////////////////////////////////////////////////////////////////////////// - -// 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 -//////////////////////////////////////////////////////////////////////////////// - -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 - 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 - 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 - 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 - 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 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::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(quotient,remainder); - } - - inf& inf::operator /= (const inf& r) throw(divide_by_zero) - { - std::pair result = divide(r); - operator=(result.first); - return *this; - } - - inf inf::operator / (const inf& r) const throw(divide_by_zero) - { - std::pair result = divide(r); - return result.first; - } - - inf& inf::operator %= (const inf& r) throw(divide_by_zero) - { - std::pair result = divide(r); - operator=(result.second); - return *this; - } - - inf inf::operator % (const inf& r) const throw(divide_by_zero) - { - std::pair 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 +//////////////////////////////////////////////////////////////////////////////// + +// 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 +//////////////////////////////////////////////////////////////////////////////// + +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 + 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 + 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 + 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 + 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 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::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(quotient,remainder); + } + + inf& inf::operator /= (const inf& r) throw(divide_by_zero) + { + std::pair result = divide(r); + operator=(result.first); + return *this; + } + + inf inf::operator / (const inf& r) const throw(divide_by_zero) + { + std::pair result = divide(r); + return result.first; + } + + inf& inf::operator %= (const inf& r) throw(divide_by_zero) + { + std::pair result = divide(r); + operator=(result.second); + return *this; + } + + inf inf::operator % (const inf& r) const throw(divide_by_zero) + { + std::pair 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 diff --git a/src/stlplus/portability/inf.hpp b/src/stlplus/portability/inf.hpp index f28541a..c20acd4 100644 --- a/src/stlplus/portability/inf.hpp +++ b/src/stlplus/portability/inf.hpp @@ -1,228 +1,228 @@ -#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 -#include - -//////////////////////////////////////////////////////////////////////////////// - -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 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 +#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 +#include + +//////////////////////////////////////////////////////////////////////////////// + +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 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 diff --git a/src/stlplus/portability/ip_sockets.cpp b/src/stlplus/portability/ip_sockets.cpp index 6dc1fad..0db880e 100644 --- a/src/stlplus/portability/ip_sockets.cpp +++ b/src/stlplus/portability/ip_sockets.cpp @@ -1,961 +1,961 @@ -//////////////////////////////////////////////////////////////////////////////// - -// 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 - -#ifdef MSWINDOWS -// Windoze-specific includes -#include -#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 -#include -#include -#include -#include -#include -#include -#include -#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 -#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 +//////////////////////////////////////////////////////////////////////////////// + +// 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 + +#ifdef MSWINDOWS +// Windoze-specific includes +#include +#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 +#include +#include +#include +#include +#include +#include +#include +#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 +#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 diff --git a/src/stlplus/portability/ip_sockets.hpp b/src/stlplus/portability/ip_sockets.hpp index ac86ebc..f0bd314 100644 --- a/src/stlplus/portability/ip_sockets.hpp +++ b/src/stlplus/portability/ip_sockets.hpp @@ -1,233 +1,233 @@ -#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 - -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 +#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 + +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 diff --git a/src/stlplus/portability/portability.hpp b/src/stlplus/portability/portability.hpp index 942c182..92ec10a 100644 --- a/src/stlplus/portability/portability.hpp +++ b/src/stlplus/portability/portability.hpp @@ -1,28 +1,28 @@ -#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 +#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 diff --git a/src/stlplus/portability/portability_exceptions.hpp b/src/stlplus/portability/portability_exceptions.hpp index c66b7bf..e47e6a0 100644 --- a/src/stlplus/portability/portability_exceptions.hpp +++ b/src/stlplus/portability/portability_exceptions.hpp @@ -1,33 +1,33 @@ -#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 -#include - -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 +#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 +#include + +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 diff --git a/src/stlplus/portability/portability_fixes.cpp b/src/stlplus/portability/portability_fixes.cpp index e26ee28..5b49e0c 100644 --- a/src/stlplus/portability/portability_fixes.cpp +++ b/src/stlplus/portability/portability_fixes.cpp @@ -1,39 +1,39 @@ -//////////////////////////////////////////////////////////////////////////////// - -// 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; -} - -//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +// 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; +} + +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/stlplus/portability/portability_fixes.hpp b/src/stlplus/portability/portability_fixes.hpp index b6a030c..26c426a 100644 --- a/src/stlplus/portability/portability_fixes.hpp +++ b/src/stlplus/portability/portability_fixes.hpp @@ -1,127 +1,127 @@ -#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 const T& maximum(const T& l, const T& r) {return l > r ? l : r;} - template 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 -#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 +#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 const T& maximum(const T& l, const T& r) {return l > r ? l : r;} + template 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 +#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 diff --git a/src/stlplus/portability/subprocesses.cpp b/src/stlplus/portability/subprocesses.cpp index a8139c2..0c7d76f 100644 --- a/src/stlplus/portability/subprocesses.cpp +++ b/src/stlplus/portability/subprocesses.cpp @@ -1,2077 +1,2077 @@ -//////////////////////////////////////////////////////////////////////////////// - -// 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 -#include -#include - -#ifdef MSWINDOWS -#else -#include -#include -#include -#include -#include -#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 - - extern char** environ; - - 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 env_vector::operator [] (unsigned index) const throw(std::out_of_range) - { - return get(index); - } - - std::pair 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 backtick_subprocess::text(void) const - { - std::vector 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 backtick(const std::string& path, const arg_vector& argv) - { - backtick_subprocess sub; - sub.spawn(path, argv); - return sub.text(); - } - - std::vector 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 +//////////////////////////////////////////////////////////////////////////////// + +// 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 +#include +#include + +#ifdef MSWINDOWS +#else +#include +#include +#include +#include +#include +#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 + + extern char** environ; + + 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 env_vector::operator [] (unsigned index) const throw(std::out_of_range) + { + return get(index); + } + + std::pair 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 backtick_subprocess::text(void) const + { + std::vector 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 backtick(const std::string& path, const arg_vector& argv) + { + backtick_subprocess sub; + sub.spawn(path, argv); + return sub.text(); + } + + std::vector 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 diff --git a/src/stlplus/portability/subprocesses.hpp b/src/stlplus/portability/subprocesses.hpp index 0369daa..5056463 100644 --- a/src/stlplus/portability/subprocesses.hpp +++ b/src/stlplus/portability/subprocesses.hpp @@ -1,282 +1,282 @@ -#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 -#endif -#include -#include -#include -#include // 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 operator [] (unsigned index) const throw(std::out_of_range); - std::pair 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 text(void) const; - }; - - std::vector backtick(const std::string& path, const arg_vector& argv); - std::vector 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 +#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 +#endif +#include +#include +#include +#include // 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 operator [] (unsigned index) const throw(std::out_of_range); + std::pair 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 text(void) const; + }; + + std::vector backtick(const std::string& path, const arg_vector& argv); + std::vector 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 diff --git a/src/stlplus/portability/tcp.hpp b/src/stlplus/portability/tcp.hpp index 3af0d3f..023b174 100644 --- a/src/stlplus/portability/tcp.hpp +++ b/src/stlplus/portability/tcp.hpp @@ -1,17 +1,17 @@ -#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 +#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 diff --git a/src/stlplus/portability/tcp_sockets.cpp b/src/stlplus/portability/tcp_sockets.cpp index 84152f4..b15f638 100644 --- a/src/stlplus/portability/tcp_sockets.cpp +++ b/src/stlplus/portability/tcp_sockets.cpp @@ -1,119 +1,119 @@ -//////////////////////////////////////////////////////////////////////////////// - -// 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 +//////////////////////////////////////////////////////////////////////////////// + +// 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 diff --git a/src/stlplus/portability/tcp_sockets.hpp b/src/stlplus/portability/tcp_sockets.hpp index 94b98c5..df46f0b 100644 --- a/src/stlplus/portability/tcp_sockets.hpp +++ b/src/stlplus/portability/tcp_sockets.hpp @@ -1,385 +1,385 @@ -#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 - -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 +#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 + +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 diff --git a/src/stlplus/portability/time.cpp b/src/stlplus/portability/time.cpp index 250a825..3ceac35 100644 --- a/src/stlplus/portability/time.cpp +++ b/src/stlplus/portability/time.cpp @@ -1,129 +1,129 @@ -//////////////////////////////////////////////////////////////////////////////// - -// 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 -//////////////////////////////////////////////////////////////////////////////// - -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 +//////////////////////////////////////////////////////////////////////////////// + +// 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 +//////////////////////////////////////////////////////////////////////////////// + +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 diff --git a/src/stlplus/portability/time.hpp b/src/stlplus/portability/time.hpp index f8abe6c..d9907e6 100644 --- a/src/stlplus/portability/time.hpp +++ b/src/stlplus/portability/time.hpp @@ -1,55 +1,55 @@ -#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 -#include - -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 +#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 +#include + +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 diff --git a/src/stlplus/portability/udp_sockets.cpp b/src/stlplus/portability/udp_sockets.cpp index 137be3d..92f909d 100644 --- a/src/stlplus/portability/udp_sockets.cpp +++ b/src/stlplus/portability/udp_sockets.cpp @@ -1,167 +1,167 @@ -//////////////////////////////////////////////////////////////////////////////// - -// 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 +//////////////////////////////////////////////////////////////////////////////// + +// 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 diff --git a/src/stlplus/portability/udp_sockets.hpp b/src/stlplus/portability/udp_sockets.hpp index 5097d44..b6be81a 100644 --- a/src/stlplus/portability/udp_sockets.hpp +++ b/src/stlplus/portability/udp_sockets.hpp @@ -1,268 +1,268 @@ -#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 - -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 +#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 + +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 diff --git a/src/stlplus/portability/version.cpp b/src/stlplus/portability/version.cpp index 8a34c6e..a271b9f 100644 --- a/src/stlplus/portability/version.cpp +++ b/src/stlplus/portability/version.cpp @@ -1,20 +1,20 @@ -//////////////////////////////////////////////////////////////////////////////// - -// 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 +//////////////////////////////////////////////////////////////////////////////// + +// 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 diff --git a/src/stlplus/portability/version.hpp b/src/stlplus/portability/version.hpp index 027e131..852efce 100644 --- a/src/stlplus/portability/version.hpp +++ b/src/stlplus/portability/version.hpp @@ -1,24 +1,24 @@ -#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 - -#define STLPLUS_VERSION "3.5" - -namespace stlplus -{ - - std::string version(void); - -} -//////////////////////////////////////////////////////////////////////////////// -#endif +#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 + +#define STLPLUS_VERSION "3.5" + +namespace stlplus +{ + + std::string version(void); + +} +//////////////////////////////////////////////////////////////////////////////// +#endif diff --git a/src/stlplus/portability/wildcard.cpp b/src/stlplus/portability/wildcard.cpp index c15252e..2efdbca 100644 --- a/src/stlplus/portability/wildcard.cpp +++ b/src/stlplus/portability/wildcard.cpp @@ -1,164 +1,164 @@ -//////////////////////////////////////////////////////////////////////////////// - -// 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 +//////////////////////////////////////////////////////////////////////////////// + +// 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 diff --git a/src/stlplus/portability/wildcard.hpp b/src/stlplus/portability/wildcard.hpp index 398c3dd..585a786 100644 --- a/src/stlplus/portability/wildcard.hpp +++ b/src/stlplus/portability/wildcard.hpp @@ -1,35 +1,35 @@ -#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 - -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 +#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 + +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 diff --git a/src/stlplus/strings/format_types.hpp b/src/stlplus/strings/format_types.hpp index 5df7711..c9711c3 100644 --- a/src/stlplus/strings/format_types.hpp +++ b/src/stlplus/strings/format_types.hpp @@ -1,60 +1,60 @@ -#ifndef STLPLUS_FORMAT_TYPES -#define STLPLUS_FORMAT_TYPES -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// A Set of enumerations controlling the string formatting of numbers. - -//////////////////////////////////////////////////////////////////////////////// - -//////////////////////////////////////////////////////////////////////////////// -// Integer radix display representations: -// There are three ways in which the radix is represented: -// - none - the number is just printed as a number (e.g. 12345). Can be confusing for non-decimal radix -// - C style - for binary, octal and hex, the C-style prefices 0b, 0 and 0x are used -// note that this is an unsigned representation -// - Hash style - in the form radix#value - the value may be signed, e.g. 10#-9 - -enum radix_display_t -{ - radix_none, // just print the number with no radix indicated - radix_hash_style, // none for decimal, hash style for all others - radix_hash_style_all, // hash style for all radices including decimal - radix_c_style, // C style for hex and octal, none for others - radix_c_style_or_hash // C style for hex and octal, none for decimal, hash style for others -}; - -//////////////////////////////////////////////////////////////////////////////// -// Floating-point display representations: -// There are three formats for the printout: -// - fixed - formatted as a fixed-point number, so no mantissa is printed (equivalent to %f in printf) -// - floating - formatted as a normalised floating-point number (equivalent to %e in printf) -// - mixed - formatted as fixed-point if appropriate, otherwise the floating format (equivalent to %g in printf) - -enum real_display_t -{ - display_fixed, // %f - display_floating, // %e - display_mixed // %g -}; - -//////////////////////////////////////////////////////////////////////////////// -// Alignment: -// There are three field alignments: -// - left aligned - the value is to the left of the field which is padded to the right with spaces -// - right aligned - the value is to the right of the field which is padded to the left with spaces -// - centred - the value is in the centre of the field and spaces added to both left and right - -enum alignment_t -{ - align_left, - align_right, - align_centre -}; - -//////////////////////////////////////////////////////////////////////////////// -#endif +#ifndef STLPLUS_FORMAT_TYPES +#define STLPLUS_FORMAT_TYPES +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// A Set of enumerations controlling the string formatting of numbers. + +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +// Integer radix display representations: +// There are three ways in which the radix is represented: +// - none - the number is just printed as a number (e.g. 12345). Can be confusing for non-decimal radix +// - C style - for binary, octal and hex, the C-style prefices 0b, 0 and 0x are used +// note that this is an unsigned representation +// - Hash style - in the form radix#value - the value may be signed, e.g. 10#-9 + +enum radix_display_t +{ + radix_none, // just print the number with no radix indicated + radix_hash_style, // none for decimal, hash style for all others + radix_hash_style_all, // hash style for all radices including decimal + radix_c_style, // C style for hex and octal, none for others + radix_c_style_or_hash // C style for hex and octal, none for decimal, hash style for others +}; + +//////////////////////////////////////////////////////////////////////////////// +// Floating-point display representations: +// There are three formats for the printout: +// - fixed - formatted as a fixed-point number, so no mantissa is printed (equivalent to %f in printf) +// - floating - formatted as a normalised floating-point number (equivalent to %e in printf) +// - mixed - formatted as fixed-point if appropriate, otherwise the floating format (equivalent to %g in printf) + +enum real_display_t +{ + display_fixed, // %f + display_floating, // %e + display_mixed // %g +}; + +//////////////////////////////////////////////////////////////////////////////// +// Alignment: +// There are three field alignments: +// - left aligned - the value is to the left of the field which is padded to the right with spaces +// - right aligned - the value is to the right of the field which is padded to the left with spaces +// - centred - the value is in the centre of the field and spaces added to both left and right + +enum alignment_t +{ + align_left, + align_right, + align_centre +}; + +//////////////////////////////////////////////////////////////////////////////// +#endif diff --git a/src/stlplus/strings/print_address.cpp b/src/stlplus/strings/print_address.cpp index 3962557..74e416f 100644 --- a/src/stlplus/strings/print_address.cpp +++ b/src/stlplus/strings/print_address.cpp @@ -1,28 +1,28 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// use the unsigned long representation for pointers - -//////////////////////////////////////////////////////////////////////////////// -#include "print_address.hpp" -#include "print_int.hpp" -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - - void print_address(std::ostream& device, const void* i, unsigned radix, radix_display_t display, unsigned width) - throw(std::invalid_argument) - { - print_unsigned_long(device, (unsigned long)i, radix, display, width); - } - - //////////////////////////////////////////////////////////////////////////////// - -} // end namespace stlplus +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// use the unsigned long representation for pointers + +//////////////////////////////////////////////////////////////////////////////// +#include "print_address.hpp" +#include "print_int.hpp" +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + + void print_address(std::ostream& device, const void* i, unsigned radix, radix_display_t display, unsigned width) + throw(std::invalid_argument) + { + print_unsigned_long(device, (unsigned long)i, radix, display, width); + } + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus diff --git a/src/stlplus/strings/print_address.hpp b/src/stlplus/strings/print_address.hpp index f89bdae..6ba90bc 100644 --- a/src/stlplus/strings/print_address.hpp +++ b/src/stlplus/strings/print_address.hpp @@ -1,33 +1,33 @@ -#ifndef STLPLUS_PRINT_ADDRESS -#define STLPLUS_PRINT_ADDRESS -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// -#include "strings_fixes.hpp" -#include "format_types.hpp" -#include -#include -#include - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - - void print_address(std::ostream& device, - const void*, - unsigned radix = 16, - radix_display_t display = radix_c_style_or_hash, - unsigned width = 0) - throw(std::invalid_argument); - - //////////////////////////////////////////////////////////////////////////////// - -} // end namespace stlplus - -#endif +#ifndef STLPLUS_PRINT_ADDRESS +#define STLPLUS_PRINT_ADDRESS +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include "format_types.hpp" +#include +#include +#include + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + + void print_address(std::ostream& device, + const void*, + unsigned radix = 16, + radix_display_t display = radix_c_style_or_hash, + unsigned width = 0) + throw(std::invalid_argument); + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus + +#endif diff --git a/src/stlplus/strings/print_basic.hpp b/src/stlplus/strings/print_basic.hpp index 9d67862..f85b00e 100644 --- a/src/stlplus/strings/print_basic.hpp +++ b/src/stlplus/strings/print_basic.hpp @@ -1,21 +1,21 @@ -#ifndef STLPLUS_PRINT_BASIC -#define STLPLUS_PRINT_BASIC -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Utilities for converting printing basic C types - -//////////////////////////////////////////////////////////////////////////////// - -#include "print_address.hpp" -#include "print_bool.hpp" -#include "print_cstring.hpp" -#include "print_float.hpp" -#include "print_int.hpp" -#include "print_pointer.hpp" - -#endif +#ifndef STLPLUS_PRINT_BASIC +#define STLPLUS_PRINT_BASIC +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Utilities for converting printing basic C types + +//////////////////////////////////////////////////////////////////////////////// + +#include "print_address.hpp" +#include "print_bool.hpp" +#include "print_cstring.hpp" +#include "print_float.hpp" +#include "print_int.hpp" +#include "print_pointer.hpp" + +#endif diff --git a/src/stlplus/strings/print_bitset.hpp b/src/stlplus/strings/print_bitset.hpp index f7151dd..d87ee20 100644 --- a/src/stlplus/strings/print_bitset.hpp +++ b/src/stlplus/strings/print_bitset.hpp @@ -1,27 +1,27 @@ -#ifndef STLPLUS_PRINT_BITSET -#define STLPLUS_PRINT_BITSET -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Generate a string representation of a bitset - -//////////////////////////////////////////////////////////////////////////////// -#include "strings_fixes.hpp" -#include -#include -#include - -namespace stlplus -{ - - template - void print_bitset(std::ostream& device, const std::bitset& data); - -} // end namespace stlplus - -#include "print_bitset.tpp" -#endif +#ifndef STLPLUS_PRINT_BITSET +#define STLPLUS_PRINT_BITSET +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Generate a string representation of a bitset + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include +#include +#include + +namespace stlplus +{ + + template + void print_bitset(std::ostream& device, const std::bitset& data); + +} // end namespace stlplus + +#include "print_bitset.tpp" +#endif diff --git a/src/stlplus/strings/print_bitset.tpp b/src/stlplus/strings/print_bitset.tpp index 833103a..5514bea 100644 --- a/src/stlplus/strings/print_bitset.tpp +++ b/src/stlplus/strings/print_bitset.tpp @@ -1,20 +1,20 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// -#include "string_bitset.hpp" - -namespace stlplus -{ - - template - void print_bitset(std::ostream& device, const std::bitset& data) - { - device << bitset_to_string(data); - } - -} // end namespace stlplus +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "string_bitset.hpp" + +namespace stlplus +{ + + template + void print_bitset(std::ostream& device, const std::bitset& data) + { + device << bitset_to_string(data); + } + +} // end namespace stlplus diff --git a/src/stlplus/strings/print_bool.cpp b/src/stlplus/strings/print_bool.cpp index adcebc1..0d63deb 100644 --- a/src/stlplus/strings/print_bool.cpp +++ b/src/stlplus/strings/print_bool.cpp @@ -1,27 +1,27 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// use the unsigned short representation for bool - -//////////////////////////////////////////////////////////////////////////////// -#include "print_bool.hpp" -#include "print_int.hpp" - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - - void print_bool(std::ostream& device, bool i, unsigned radix, radix_display_t display, unsigned width) - throw(std::invalid_argument) - { - print_unsigned_short(device, (unsigned short)i, radix, display, width); - } - - //////////////////////////////////////////////////////////////////////////////// - -} // end namespace stlplus +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// use the unsigned short representation for bool + +//////////////////////////////////////////////////////////////////////////////// +#include "print_bool.hpp" +#include "print_int.hpp" + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + + void print_bool(std::ostream& device, bool i, unsigned radix, radix_display_t display, unsigned width) + throw(std::invalid_argument) + { + print_unsigned_short(device, (unsigned short)i, radix, display, width); + } + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus diff --git a/src/stlplus/strings/print_bool.hpp b/src/stlplus/strings/print_bool.hpp index 5e808d1..f22a1c0 100644 --- a/src/stlplus/strings/print_bool.hpp +++ b/src/stlplus/strings/print_bool.hpp @@ -1,34 +1,34 @@ -#ifndef STLPLUS_PRINT_BOOL -#define STLPLUS_PRINT_BOOL -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Conversion of string to/from bool - -//////////////////////////////////////////////////////////////////////////////// -#include "strings_fixes.hpp" -#include "format_types.hpp" -#include -#include -#include - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - - void print_bool(std::ostream& device, bool i, - unsigned radix = 10, - radix_display_t display = radix_c_style_or_hash, - unsigned width = 0) - throw(std::invalid_argument); - - //////////////////////////////////////////////////////////////////////////////// - -} // end namespace stlplus - -#endif +#ifndef STLPLUS_PRINT_BOOL +#define STLPLUS_PRINT_BOOL +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Conversion of string to/from bool + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include "format_types.hpp" +#include +#include +#include + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + + void print_bool(std::ostream& device, bool i, + unsigned radix = 10, + radix_display_t display = radix_c_style_or_hash, + unsigned width = 0) + throw(std::invalid_argument); + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus + +#endif diff --git a/src/stlplus/strings/print_cstring.cpp b/src/stlplus/strings/print_cstring.cpp index 423c741..50c733d 100644 --- a/src/stlplus/strings/print_cstring.cpp +++ b/src/stlplus/strings/print_cstring.cpp @@ -1,19 +1,19 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// -#include "print_cstring.hpp" - -namespace stlplus -{ - - void print_cstring(std::ostream& device, const char* value) - { - device << value; - } - -} // end namespace stlplus +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "print_cstring.hpp" + +namespace stlplus +{ + + void print_cstring(std::ostream& device, const char* value) + { + device << value; + } + +} // end namespace stlplus diff --git a/src/stlplus/strings/print_cstring.hpp b/src/stlplus/strings/print_cstring.hpp index 57cdbcd..d705015 100644 --- a/src/stlplus/strings/print_cstring.hpp +++ b/src/stlplus/strings/print_cstring.hpp @@ -1,27 +1,27 @@ -#ifndef STLPLUS_PRINT_CSTRING -#define STLPLUS_PRINT_CSTRING -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Functions for converting C/STL strings to string - -// This is necessary for completeness - -//////////////////////////////////////////////////////////////////////////////// -#include "strings_fixes.hpp" -#include -#include -#include - -namespace stlplus -{ - - void print_cstring(std::ostream& device, const char* value); - -} - -#endif +#ifndef STLPLUS_PRINT_CSTRING +#define STLPLUS_PRINT_CSTRING +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Functions for converting C/STL strings to string + +// This is necessary for completeness + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include +#include +#include + +namespace stlplus +{ + + void print_cstring(std::ostream& device, const char* value); + +} + +#endif diff --git a/src/stlplus/strings/print_digraph.hpp b/src/stlplus/strings/print_digraph.hpp index e5c2ff8..126b8a3 100644 --- a/src/stlplus/strings/print_digraph.hpp +++ b/src/stlplus/strings/print_digraph.hpp @@ -1,33 +1,33 @@ -#ifndef STLPLUS_PRINT_DIGRAPH -#define STLPLUS_PRINT_DIGRAPH -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Generate a string representation of a digraph - -//////////////////////////////////////////////////////////////////////////////// -#include "strings_fixes.hpp" -#include "digraph.hpp" -#include -#include - -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - - template - void print_digraph(std::ostream& device, - const digraph& values, - NS node_print_fn, - AS arc_print_fn, - const std::string& separator = ","); - -} // end namespace stlplus - -#include "print_digraph.tpp" -#endif +#ifndef STLPLUS_PRINT_DIGRAPH +#define STLPLUS_PRINT_DIGRAPH +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Generate a string representation of a digraph + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include "digraph.hpp" +#include +#include + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + template + void print_digraph(std::ostream& device, + const digraph& values, + NS node_print_fn, + AS arc_print_fn, + const std::string& separator = ","); + +} // end namespace stlplus + +#include "print_digraph.tpp" +#endif diff --git a/src/stlplus/strings/print_digraph.tpp b/src/stlplus/strings/print_digraph.tpp index 69e8018..af2818f 100644 --- a/src/stlplus/strings/print_digraph.tpp +++ b/src/stlplus/strings/print_digraph.tpp @@ -1,31 +1,31 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// -#include "print_sequence.hpp" - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - - template - void print_digraph(std::ostream& device, const digraph& values, - NS node_print_fn, - AS arc_print_fn, - const std::string& separator) - { - device << "nodes:"; - device << separator; - print_sequence(device, values.begin(), values.end(), node_print_fn, separator); - device << "arcs:"; - device << separator; - print_sequence(device, values.arc_begin(), values.arc_end(), arc_print_fn, separator); - } - -} // end namespace stlplus - +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "print_sequence.hpp" + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + + template + void print_digraph(std::ostream& device, const digraph& values, + NS node_print_fn, + AS arc_print_fn, + const std::string& separator) + { + device << "nodes:"; + device << separator; + print_sequence(device, values.begin(), values.end(), node_print_fn, separator); + device << "arcs:"; + device << separator; + print_sequence(device, values.arc_begin(), values.arc_end(), arc_print_fn, separator); + } + +} // end namespace stlplus + diff --git a/src/stlplus/strings/print_float.cpp b/src/stlplus/strings/print_float.cpp index a36c2c1..dc10b20 100644 --- a/src/stlplus/strings/print_float.cpp +++ b/src/stlplus/strings/print_float.cpp @@ -1,32 +1,32 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// -#include "print_float.hpp" -#include "string_float.hpp" - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - // floating-point types - - void print_float(std::ostream& device, float f, real_display_t display, unsigned width, unsigned precision) - throw(std::invalid_argument) - { - device << float_to_string(f, display, width, precision); - } - - void print_double(std::ostream& device, double f, real_display_t display, unsigned width, unsigned precision) - throw(std::invalid_argument) - { - device << double_to_string(f, display, width, precision); - } - - //////////////////////////////////////////////////////////////////////////////// - -} // end namespace stlplus +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "print_float.hpp" +#include "string_float.hpp" + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // floating-point types + + void print_float(std::ostream& device, float f, real_display_t display, unsigned width, unsigned precision) + throw(std::invalid_argument) + { + device << float_to_string(f, display, width, precision); + } + + void print_double(std::ostream& device, double f, real_display_t display, unsigned width, unsigned precision) + throw(std::invalid_argument) + { + device << double_to_string(f, display, width, precision); + } + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus diff --git a/src/stlplus/strings/print_float.hpp b/src/stlplus/strings/print_float.hpp index 2de8b8f..9ee54ee 100644 --- a/src/stlplus/strings/print_float.hpp +++ b/src/stlplus/strings/print_float.hpp @@ -1,47 +1,47 @@ -#ifndef STLPLUS_PRINT_FLOAT -#define STLPLUS_PRINT_FLOAT -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Convert a float/double to/from string - -//////////////////////////////////////////////////////////////////////////////// -#include "strings_fixes.hpp" -#include "format_types.hpp" -#include -#include -#include - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - // convert a real type to string - //////////////////////////////////////////////////////////////////////////////// - - // Only decimal radix is supported - - // The way in which the number is displayed is defined in radix_types.hpp - // Using any other value for the display type causes std::invalid_argument to be thrown - - void print_float(std::ostream& device, float f, - real_display_t display = display_mixed, - unsigned width = 0, - unsigned precision = 6) - throw(std::invalid_argument); - - void print_double(std::ostream& device, double f, - real_display_t display = display_mixed, - unsigned width = 0, - unsigned precision = 6) - throw(std::invalid_argument); - - //////////////////////////////////////////////////////////////////////////////// - -} // end namespace stlplus - -#endif +#ifndef STLPLUS_PRINT_FLOAT +#define STLPLUS_PRINT_FLOAT +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Convert a float/double to/from string + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include "format_types.hpp" +#include +#include +#include + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // convert a real type to string + //////////////////////////////////////////////////////////////////////////////// + + // Only decimal radix is supported + + // The way in which the number is displayed is defined in radix_types.hpp + // Using any other value for the display type causes std::invalid_argument to be thrown + + void print_float(std::ostream& device, float f, + real_display_t display = display_mixed, + unsigned width = 0, + unsigned precision = 6) + throw(std::invalid_argument); + + void print_double(std::ostream& device, double f, + real_display_t display = display_mixed, + unsigned width = 0, + unsigned precision = 6) + throw(std::invalid_argument); + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus + +#endif diff --git a/src/stlplus/strings/print_foursome.hpp b/src/stlplus/strings/print_foursome.hpp index 3ee3696..899915f 100644 --- a/src/stlplus/strings/print_foursome.hpp +++ b/src/stlplus/strings/print_foursome.hpp @@ -1,35 +1,35 @@ -#ifndef STLPLUS_PRINT_FOURSOME -#define STLPLUS_PRINT_FOURSOME -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Generate a string representation of a foursome - -//////////////////////////////////////////////////////////////////////////////// -#include "strings_fixes.hpp" -#include "foursome.hpp" -#include -#include - -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - - template - void print_foursome(std::ostream& device, - const foursome& values, - S1 print_fn1, - S2 print_fn2, - S3 print_fn3, - S4 print_fn4, - const std::string& separator = ":"); - -} // end namespace stlplus - -#include "print_foursome.tpp" -#endif +#ifndef STLPLUS_PRINT_FOURSOME +#define STLPLUS_PRINT_FOURSOME +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Generate a string representation of a foursome + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include "foursome.hpp" +#include +#include + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + template + void print_foursome(std::ostream& device, + const foursome& values, + S1 print_fn1, + S2 print_fn2, + S3 print_fn3, + S4 print_fn4, + const std::string& separator = ":"); + +} // end namespace stlplus + +#include "print_foursome.tpp" +#endif diff --git a/src/stlplus/strings/print_foursome.tpp b/src/stlplus/strings/print_foursome.tpp index 8b9f319..adec37d 100644 --- a/src/stlplus/strings/print_foursome.tpp +++ b/src/stlplus/strings/print_foursome.tpp @@ -1,33 +1,33 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - - template - void print_foursome(std::ostream& device, - const foursome& values, - S1 print_fn1, - S2 print_fn2, - S3 print_fn3, - S4 print_fn4, - const std::string& separator) - { - print_fn1(device, values.first); - device << separator; - print_fn2(device, values.second); - device << separator; - print_fn3(device, values.third); - device << separator; - print_fn4(device, values.fourth); - } - - -} // end namespace stlplus - +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + template + void print_foursome(std::ostream& device, + const foursome& values, + S1 print_fn1, + S2 print_fn2, + S3 print_fn3, + S4 print_fn4, + const std::string& separator) + { + print_fn1(device, values.first); + device << separator; + print_fn2(device, values.second); + device << separator; + print_fn3(device, values.third); + device << separator; + print_fn4(device, values.fourth); + } + + +} // end namespace stlplus + diff --git a/src/stlplus/strings/print_hash.hpp b/src/stlplus/strings/print_hash.hpp index 43a76f4..36915b5 100644 --- a/src/stlplus/strings/print_hash.hpp +++ b/src/stlplus/strings/print_hash.hpp @@ -1,34 +1,34 @@ -#ifndef STLPLUS_PRINT_HASH -#define STLPLUS_PRINT_HASH -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Generate a string representation of a hash - -//////////////////////////////////////////////////////////////////////////////// -#include "strings_fixes.hpp" -#include "hash.hpp" -#include -#include - -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - - template - void print_hash(std::ostream& device, - const hash& values, - KS key_print_fn, - TS value_print_fn, - const std::string& pair_separator = ":", - const std::string& separator = ","); - -} // end namespace stlplus - -#include "print_hash.tpp" -#endif +#ifndef STLPLUS_PRINT_HASH +#define STLPLUS_PRINT_HASH +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Generate a string representation of a hash + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include "hash.hpp" +#include +#include + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + template + void print_hash(std::ostream& device, + const hash& values, + KS key_print_fn, + TS value_print_fn, + const std::string& pair_separator = ":", + const std::string& separator = ","); + +} // end namespace stlplus + +#include "print_hash.tpp" +#endif diff --git a/src/stlplus/strings/print_hash.tpp b/src/stlplus/strings/print_hash.tpp index 5b3bd96..84d2fe4 100644 --- a/src/stlplus/strings/print_hash.tpp +++ b/src/stlplus/strings/print_hash.tpp @@ -1,29 +1,29 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// -#include "print_sequence.hpp" - -namespace stlplus -{ - - template - void print_hash(std::ostream& device, - const hash& values, - KS key_print_fn, - TS value_print_fn, - const std::string& pair_separator, - const std::string& separator) - { - print_pair_sequence(device, - values.begin(), values.end(), - key_print_fn, value_print_fn, - pair_separator, separator); - } - -} // end namespace stlplus - +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "print_sequence.hpp" + +namespace stlplus +{ + + template + void print_hash(std::ostream& device, + const hash& values, + KS key_print_fn, + TS value_print_fn, + const std::string& pair_separator, + const std::string& separator) + { + print_pair_sequence(device, + values.begin(), values.end(), + key_print_fn, value_print_fn, + pair_separator, separator); + } + +} // end namespace stlplus + diff --git a/src/stlplus/strings/print_inf.cpp b/src/stlplus/strings/print_inf.cpp index 8a12f40..fb55917 100644 --- a/src/stlplus/strings/print_inf.cpp +++ b/src/stlplus/strings/print_inf.cpp @@ -1,38 +1,38 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// String conversion functions for the infinite precision integer type inf - -//////////////////////////////////////////////////////////////////////////////// - -// can be excluded from the build to break the dependency on the portability library -#ifndef NO_STLPLUS_INF - -#include "print_inf.hpp" -#include "string_inf.hpp" - -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - - void print_inf(std::ostream& device, - const stlplus::inf& data, - unsigned radix, - radix_display_t display, - unsigned width) - throw(std::invalid_argument) - { - device << inf_to_string(data, radix, display, width); - } - - -//////////////////////////////////////////////////////////////////////////////// - -} // end namespace stlplus - -#endif +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// String conversion functions for the infinite precision integer type inf + +//////////////////////////////////////////////////////////////////////////////// + +// can be excluded from the build to break the dependency on the portability library +#ifndef NO_STLPLUS_INF + +#include "print_inf.hpp" +#include "string_inf.hpp" + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + void print_inf(std::ostream& device, + const stlplus::inf& data, + unsigned radix, + radix_display_t display, + unsigned width) + throw(std::invalid_argument) + { + device << inf_to_string(data, radix, display, width); + } + + +//////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus + +#endif diff --git a/src/stlplus/strings/print_inf.hpp b/src/stlplus/strings/print_inf.hpp index 51b0377..b3bfc27 100644 --- a/src/stlplus/strings/print_inf.hpp +++ b/src/stlplus/strings/print_inf.hpp @@ -1,40 +1,40 @@ -#ifndef STLPLUS_PRINT_INF -#define STLPLUS_PRINT_INF -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// String conversion functions for the infinite precision integer type inf - -// The conversion supports all the formatting modes defined on format_types - -//////////////////////////////////////////////////////////////////////////////// - -#include "strings_fixes.hpp" -#include "inf.hpp" -#include "format_types.hpp" -#include -#include - -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - - void print_inf(std::ostream& device, - const inf&, - unsigned radix = 10, - radix_display_t display = radix_c_style_or_hash, - unsigned width = 0) - throw(std::invalid_argument); - -//////////////////////////////////////////////////////////////////////////////// -} // end namespace stlplus - - -#endif +#ifndef STLPLUS_PRINT_INF +#define STLPLUS_PRINT_INF +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// String conversion functions for the infinite precision integer type inf + +// The conversion supports all the formatting modes defined on format_types + +//////////////////////////////////////////////////////////////////////////////// + +#include "strings_fixes.hpp" +#include "inf.hpp" +#include "format_types.hpp" +#include +#include + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + + void print_inf(std::ostream& device, + const inf&, + unsigned radix = 10, + radix_display_t display = radix_c_style_or_hash, + unsigned width = 0) + throw(std::invalid_argument); + +//////////////////////////////////////////////////////////////////////////////// +} // end namespace stlplus + + +#endif diff --git a/src/stlplus/strings/print_int.cpp b/src/stlplus/strings/print_int.cpp index e2b63be..cde00ee 100644 --- a/src/stlplus/strings/print_int.cpp +++ b/src/stlplus/strings/print_int.cpp @@ -1,55 +1,55 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// -#include "print_int.hpp" -#include "string_int.hpp" - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - - void print_short(std::ostream& device, short i, unsigned radix, radix_display_t display, unsigned width) - throw(std::invalid_argument) - { - device << short_to_string(i, radix, display, width); - } - - void print_unsigned_short(std::ostream& device, unsigned short i, unsigned radix, radix_display_t display, unsigned width) - throw(std::invalid_argument) - { - device << unsigned_short_to_string(i, radix, display, width); - } - - void print_int(std::ostream& device, int i, unsigned radix, radix_display_t display, unsigned width) - throw(std::invalid_argument) - { - device << int_to_string(i, radix, display, width); - } - - void print_unsigned(std::ostream& device, unsigned i, unsigned radix, radix_display_t display, unsigned width) - throw(std::invalid_argument) - { - device << unsigned_to_string(i, radix, display, width); - } - - void print_long(std::ostream& device, long i, unsigned radix, radix_display_t display, unsigned width) - throw(std::invalid_argument) - { - device << long_to_string(i, radix, display, width); - } - - void print_unsigned_long(std::ostream& device, unsigned long i, unsigned radix, radix_display_t display, unsigned width) - throw(std::invalid_argument) - { - device << unsigned_long_to_string(i, radix, display, width); - } - - //////////////////////////////////////////////////////////////////////////////// - -} // end namespace stlplus +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "print_int.hpp" +#include "string_int.hpp" + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + + void print_short(std::ostream& device, short i, unsigned radix, radix_display_t display, unsigned width) + throw(std::invalid_argument) + { + device << short_to_string(i, radix, display, width); + } + + void print_unsigned_short(std::ostream& device, unsigned short i, unsigned radix, radix_display_t display, unsigned width) + throw(std::invalid_argument) + { + device << unsigned_short_to_string(i, radix, display, width); + } + + void print_int(std::ostream& device, int i, unsigned radix, radix_display_t display, unsigned width) + throw(std::invalid_argument) + { + device << int_to_string(i, radix, display, width); + } + + void print_unsigned(std::ostream& device, unsigned i, unsigned radix, radix_display_t display, unsigned width) + throw(std::invalid_argument) + { + device << unsigned_to_string(i, radix, display, width); + } + + void print_long(std::ostream& device, long i, unsigned radix, radix_display_t display, unsigned width) + throw(std::invalid_argument) + { + device << long_to_string(i, radix, display, width); + } + + void print_unsigned_long(std::ostream& device, unsigned long i, unsigned radix, radix_display_t display, unsigned width) + throw(std::invalid_argument) + { + device << unsigned_long_to_string(i, radix, display, width); + } + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus diff --git a/src/stlplus/strings/print_int.hpp b/src/stlplus/strings/print_int.hpp index 4f97293..d09ef70 100644 --- a/src/stlplus/strings/print_int.hpp +++ b/src/stlplus/strings/print_int.hpp @@ -1,81 +1,81 @@ -#ifndef STLPLUS_PRINT_INT -#define STLPLUS_PRINT_INT -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Print integer types - -// This extends the formatting available from iostream - -//////////////////////////////////////////////////////////////////////////////// -#include "strings_fixes.hpp" -#include "format_types.hpp" -#include -#include - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - // Conversions of Integer types to string - //////////////////////////////////////////////////////////////////////////////// - - // The radix (i.e. base) for these conversions can be any value from base 2 to base 36 - // specifying any other radix causes std::invalid_argument to be thrown - - // The way in which the radix is displayed is defined in radix_types.hpp - // If any other value is used, std::invalid_argument is thrown - - // The width argument specifies the number of numerical digits to use in the result - // This is a minimum - if the value requires more digits then it will be wider than the width argument - // However, if it is smaller, then it will be extended to the specified width - // Then, the radix display prefix is added to this width - - // For example, using the hash representation of 0 in hex with width=4 gives: - // 16#0000 - so there's 4 digits in the number part - - void print_short(std::ostream& device, short i, - unsigned radix = 10, - radix_display_t display = radix_c_style_or_hash, - unsigned width = 0) - throw(std::invalid_argument); - - void print_unsigned_short(std::ostream& device, unsigned short i, - unsigned radix = 10, - radix_display_t display = radix_c_style_or_hash, - unsigned width = 0) - throw(std::invalid_argument); - - void print_int(std::ostream& device, int i, - unsigned radix = 10, - radix_display_t display = radix_c_style_or_hash, - unsigned width = 0) - throw(std::invalid_argument); - - void print_unsigned(std::ostream& device, unsigned i, - unsigned radix = 10, - radix_display_t display = radix_c_style_or_hash, - unsigned width = 0) - throw(std::invalid_argument); - - void print_long(std::ostream& device, long i, - unsigned radix = 10, - radix_display_t display = radix_c_style_or_hash, - unsigned width = 0) - throw(std::invalid_argument); - - void print_unsigned_long(std::ostream& device, unsigned long i, - unsigned radix = 10, - radix_display_t display = radix_c_style_or_hash, - unsigned width = 0) - throw(std::invalid_argument); - - //////////////////////////////////////////////////////////////////////////////// - -} // end namespace stlplus - -#endif +#ifndef STLPLUS_PRINT_INT +#define STLPLUS_PRINT_INT +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Print integer types + +// This extends the formatting available from iostream + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include "format_types.hpp" +#include +#include + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // Conversions of Integer types to string + //////////////////////////////////////////////////////////////////////////////// + + // The radix (i.e. base) for these conversions can be any value from base 2 to base 36 + // specifying any other radix causes std::invalid_argument to be thrown + + // The way in which the radix is displayed is defined in radix_types.hpp + // If any other value is used, std::invalid_argument is thrown + + // The width argument specifies the number of numerical digits to use in the result + // This is a minimum - if the value requires more digits then it will be wider than the width argument + // However, if it is smaller, then it will be extended to the specified width + // Then, the radix display prefix is added to this width + + // For example, using the hash representation of 0 in hex with width=4 gives: + // 16#0000 - so there's 4 digits in the number part + + void print_short(std::ostream& device, short i, + unsigned radix = 10, + radix_display_t display = radix_c_style_or_hash, + unsigned width = 0) + throw(std::invalid_argument); + + void print_unsigned_short(std::ostream& device, unsigned short i, + unsigned radix = 10, + radix_display_t display = radix_c_style_or_hash, + unsigned width = 0) + throw(std::invalid_argument); + + void print_int(std::ostream& device, int i, + unsigned radix = 10, + radix_display_t display = radix_c_style_or_hash, + unsigned width = 0) + throw(std::invalid_argument); + + void print_unsigned(std::ostream& device, unsigned i, + unsigned radix = 10, + radix_display_t display = radix_c_style_or_hash, + unsigned width = 0) + throw(std::invalid_argument); + + void print_long(std::ostream& device, long i, + unsigned radix = 10, + radix_display_t display = radix_c_style_or_hash, + unsigned width = 0) + throw(std::invalid_argument); + + void print_unsigned_long(std::ostream& device, unsigned long i, + unsigned radix = 10, + radix_display_t display = radix_c_style_or_hash, + unsigned width = 0) + throw(std::invalid_argument); + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus + +#endif diff --git a/src/stlplus/strings/print_list.hpp b/src/stlplus/strings/print_list.hpp index d1767b2..fd1027a 100644 --- a/src/stlplus/strings/print_list.hpp +++ b/src/stlplus/strings/print_list.hpp @@ -1,30 +1,30 @@ -#ifndef STLPLUS_PRINT_LIST -#define STLPLUS_PRINT_LIST -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Generate a string representation of a list - -//////////////////////////////////////////////////////////////////////////////// -#include "strings_fixes.hpp" -#include -#include -#include - -namespace stlplus -{ - - template - void print_list(std::ostream& device, - const std::list& values, - S print_fn, - const std::string& separator = ","); - -} // end namespace stlplus - -#include "print_list.tpp" -#endif +#ifndef STLPLUS_PRINT_LIST +#define STLPLUS_PRINT_LIST +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Generate a string representation of a list + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include +#include +#include + +namespace stlplus +{ + + template + void print_list(std::ostream& device, + const std::list& values, + S print_fn, + const std::string& separator = ","); + +} // end namespace stlplus + +#include "print_list.tpp" +#endif diff --git a/src/stlplus/strings/print_list.tpp b/src/stlplus/strings/print_list.tpp index e60c39a..a573175 100644 --- a/src/stlplus/strings/print_list.tpp +++ b/src/stlplus/strings/print_list.tpp @@ -1,25 +1,25 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// template implementations - -//////////////////////////////////////////////////////////////////////////////// -#include "print_sequence.hpp" - -namespace stlplus -{ - - template - void print_list(std::ostream& device, - const std::list& values, - S print_fn, - const std::string& separator) - { - print_sequence(device, values.begin(), values.end(), print_fn, separator); - } - -} // end namespace stlplus +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// template implementations + +//////////////////////////////////////////////////////////////////////////////// +#include "print_sequence.hpp" + +namespace stlplus +{ + + template + void print_list(std::ostream& device, + const std::list& values, + S print_fn, + const std::string& separator) + { + print_sequence(device, values.begin(), values.end(), print_fn, separator); + } + +} // end namespace stlplus diff --git a/src/stlplus/strings/print_map.hpp b/src/stlplus/strings/print_map.hpp index 6935037..83c0048 100644 --- a/src/stlplus/strings/print_map.hpp +++ b/src/stlplus/strings/print_map.hpp @@ -1,38 +1,38 @@ -#ifndef STLPLUS_PRINT_MAP -#define STLPLUS_PRINT_MAP -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Generate a string representation of a map/multimap - -//////////////////////////////////////////////////////////////////////////////// -#include "strings_fixes.hpp" -#include -#include -#include - -namespace stlplus -{ - - template - void print_map(std::ostream& device, const std::map& values, - SK key_print_fn, - ST value_print_fn, - const std::string& pair_separator = ":", - const std::string& separator = ","); - - template - void print_multimap(std::ostream& device, const std::multimap& values, - SK key_print_fn, - ST value_print_fn, - const std::string& pair_separator = ":", - const std::string& separator = ","); - -} // end namespace stlplus - -#include "print_map.tpp" -#endif +#ifndef STLPLUS_PRINT_MAP +#define STLPLUS_PRINT_MAP +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Generate a string representation of a map/multimap + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include +#include +#include + +namespace stlplus +{ + + template + void print_map(std::ostream& device, const std::map& values, + SK key_print_fn, + ST value_print_fn, + const std::string& pair_separator = ":", + const std::string& separator = ","); + + template + void print_multimap(std::ostream& device, const std::multimap& values, + SK key_print_fn, + ST value_print_fn, + const std::string& pair_separator = ":", + const std::string& separator = ","); + +} // end namespace stlplus + +#include "print_map.tpp" +#endif diff --git a/src/stlplus/strings/print_map.tpp b/src/stlplus/strings/print_map.tpp index b3a727a..f499e8f 100644 --- a/src/stlplus/strings/print_map.tpp +++ b/src/stlplus/strings/print_map.tpp @@ -1,46 +1,46 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// -#include "print_sequence.hpp" - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - // map - - template - void print_map(std::ostream& device, const std::map& values, - SK key_print_fn, - ST value_print_fn, - const std::string& pair_separator, - const std::string& separator) - { - print_pair_sequence(values.begin(), values.end(), - key_print_fn, value_print_fn, - pair_separator, separator); - } - - //////////////////////////////////////////////////////////////////////////////// - // multimap - - template - void print_multimap(std::ostream& device, const std::multimap& values, - SK key_print_fn, - ST value_print_fn, - const std::string& pair_separator, - const std::string& separator) - { - print_pair_sequence(device, - values.begin(), values.end(), - key_print_fn, value_print_fn, - pair_separator, separator); - } - - //////////////////////////////////////////////////////////////////////////////// -} // end namespace stlplus +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "print_sequence.hpp" + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // map + + template + void print_map(std::ostream& device, const std::map& values, + SK key_print_fn, + ST value_print_fn, + const std::string& pair_separator, + const std::string& separator) + { + print_pair_sequence(values.begin(), values.end(), + key_print_fn, value_print_fn, + pair_separator, separator); + } + + //////////////////////////////////////////////////////////////////////////////// + // multimap + + template + void print_multimap(std::ostream& device, const std::multimap& values, + SK key_print_fn, + ST value_print_fn, + const std::string& pair_separator, + const std::string& separator) + { + print_pair_sequence(device, + values.begin(), values.end(), + key_print_fn, value_print_fn, + pair_separator, separator); + } + + //////////////////////////////////////////////////////////////////////////////// +} // end namespace stlplus diff --git a/src/stlplus/strings/print_matrix.hpp b/src/stlplus/strings/print_matrix.hpp index f3fb8d5..b7f321a 100644 --- a/src/stlplus/strings/print_matrix.hpp +++ b/src/stlplus/strings/print_matrix.hpp @@ -1,33 +1,33 @@ -#ifndef STLPLUS_PRINT_MATRIX -#define STLPLUS_PRINT_MATRIX -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Generate a string representation of a matrix - -//////////////////////////////////////////////////////////////////////////////// -#include "strings_fixes.hpp" -#include "matrix.hpp" -#include -#include - -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - - template - void print_matrix(std::ostream& device, - const matrix& values, - S print_fn, - const std::string& column_separator = "|", - const std::string& row_separator = ","); - -} // end namespace stlplus - -#include "print_matrix.tpp" -#endif +#ifndef STLPLUS_PRINT_MATRIX +#define STLPLUS_PRINT_MATRIX +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Generate a string representation of a matrix + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include "matrix.hpp" +#include +#include + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + template + void print_matrix(std::ostream& device, + const matrix& values, + S print_fn, + const std::string& column_separator = "|", + const std::string& row_separator = ","); + +} // end namespace stlplus + +#include "print_matrix.tpp" +#endif diff --git a/src/stlplus/strings/print_matrix.tpp b/src/stlplus/strings/print_matrix.tpp index 406c69f..a31bd54 100644 --- a/src/stlplus/strings/print_matrix.tpp +++ b/src/stlplus/strings/print_matrix.tpp @@ -1,33 +1,33 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - - template - void print_matrix(std::ostream& device, const matrix& values, - S print_fn, - const std::string& column_separator, - const std::string& row_separator) - { - for (unsigned r = 0; r < values.rows(); r++) - { - if (r != 0) device << row_separator; - for (unsigned c = 0; c < values.columns(); c++) - { - if (c != 0) device << column_separator; - print_fn(device, values(r,c)); - } - } - } - -} // end namespace stlplus - +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + + template + void print_matrix(std::ostream& device, const matrix& values, + S print_fn, + const std::string& column_separator, + const std::string& row_separator) + { + for (unsigned r = 0; r < values.rows(); r++) + { + if (r != 0) device << row_separator; + for (unsigned c = 0; c < values.columns(); c++) + { + if (c != 0) device << column_separator; + print_fn(device, values(r,c)); + } + } + } + +} // end namespace stlplus + diff --git a/src/stlplus/strings/print_ntree.hpp b/src/stlplus/strings/print_ntree.hpp index 47c1a46..f09ea73 100644 --- a/src/stlplus/strings/print_ntree.hpp +++ b/src/stlplus/strings/print_ntree.hpp @@ -1,33 +1,33 @@ -#ifndef STLPLUS_PRINT_NTREE -#define STLPLUS_PRINT_NTREE -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Generate a string representation of an ntree - -//////////////////////////////////////////////////////////////////////////////// -#include "strings_fixes.hpp" -#include "ntree.hpp" -#include -#include - -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - - template - void print_ntree(std::ostream& device, - const ntree& values, - S print_fn, - const std::string& separator = "|", - const std::string& indent_string = " "); - -} // end namespace stlplus - -#include "print_ntree.tpp" -#endif +#ifndef STLPLUS_PRINT_NTREE +#define STLPLUS_PRINT_NTREE +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Generate a string representation of an ntree + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include "ntree.hpp" +#include +#include + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + template + void print_ntree(std::ostream& device, + const ntree& values, + S print_fn, + const std::string& separator = "|", + const std::string& indent_string = " "); + +} // end namespace stlplus + +#include "print_ntree.tpp" +#endif diff --git a/src/stlplus/strings/print_ntree.tpp b/src/stlplus/strings/print_ntree.tpp index ab85611..f78895a 100644 --- a/src/stlplus/strings/print_ntree.tpp +++ b/src/stlplus/strings/print_ntree.tpp @@ -1,30 +1,30 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - - template - void print_ntree(std::ostream& device, - const ntree& values, - S print_fn, - const std::string& separator, - const std::string& indent_string) - { - for (TYPENAME ntree::const_prefix_iterator i = values.prefix_begin(); i != values.prefix_end(); i++) - { - if (i != values.prefix_begin()) device << separator; - for (unsigned indent = values.depth(i.simplify()); --indent; ) - device << indent_string; - print_fn(device, *i); - } - } - -} // end namespace stlplus - +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + template + void print_ntree(std::ostream& device, + const ntree& values, + S print_fn, + const std::string& separator, + const std::string& indent_string) + { + for (TYPENAME ntree::const_prefix_iterator i = values.prefix_begin(); i != values.prefix_end(); i++) + { + if (i != values.prefix_begin()) device << separator; + for (unsigned indent = values.depth(i.simplify()); --indent; ) + device << indent_string; + print_fn(device, *i); + } + } + +} // end namespace stlplus + diff --git a/src/stlplus/strings/print_pair.hpp b/src/stlplus/strings/print_pair.hpp index 144461f..58dff79 100644 --- a/src/stlplus/strings/print_pair.hpp +++ b/src/stlplus/strings/print_pair.hpp @@ -1,31 +1,31 @@ -#ifndef STLPLUS_PRINT_PAIR -#define STLPLUS_PRINT_PAIR -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Generate a string representation of a pair - -//////////////////////////////////////////////////////////////////////////////// -#include "strings_fixes.hpp" -#include -#include -#include - -namespace stlplus -{ - - template - void print_pair(std::ostream& device, - const std::pair& values, - S1 print_fn1, - S2 print_fn2, - const std::string& separator = ":"); - -} // end namespace stlplus - -#include "print_pair.tpp" -#endif +#ifndef STLPLUS_PRINT_PAIR +#define STLPLUS_PRINT_PAIR +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Generate a string representation of a pair + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include +#include +#include + +namespace stlplus +{ + + template + void print_pair(std::ostream& device, + const std::pair& values, + S1 print_fn1, + S2 print_fn2, + const std::string& separator = ":"); + +} // end namespace stlplus + +#include "print_pair.tpp" +#endif diff --git a/src/stlplus/strings/print_pair.tpp b/src/stlplus/strings/print_pair.tpp index a93c0c0..1d98050 100644 --- a/src/stlplus/strings/print_pair.tpp +++ b/src/stlplus/strings/print_pair.tpp @@ -1,25 +1,25 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - - template - void print_pair(std::ostream& device, - const std::pair& values, - S1 print_fn1, - S2 print_fn2, - const std::string& separator) - { - print_fn1(device, values.first); - device << separator; - print__fn2(device, values.second); - } - -} // end namespace stlplus +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + template + void print_pair(std::ostream& device, + const std::pair& values, + S1 print_fn1, + S2 print_fn2, + const std::string& separator) + { + print_fn1(device, values.first); + device << separator; + print__fn2(device, values.second); + } + +} // end namespace stlplus diff --git a/src/stlplus/strings/print_pointer.hpp b/src/stlplus/strings/print_pointer.hpp index cb738cc..971c0ac 100644 --- a/src/stlplus/strings/print_pointer.hpp +++ b/src/stlplus/strings/print_pointer.hpp @@ -1,32 +1,32 @@ -#ifndef STLPLUS_PRINT_POINTER -#define STLPLUS_PRINT_POINTER -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Generate a string representation of an object pointed to - -//////////////////////////////////////////////////////////////////////////////// -#include "strings_fixes.hpp" -#include -#include - -namespace stlplus -{ - - template - void print_pointer(std::ostream& device, - const T* value, - S print_fn, - const std::string& null_string = "", - const std::string& prefix = "(", - const std::string& suffix = ")"); - - -} - -#include "print_pointer.tpp" -#endif +#ifndef STLPLUS_PRINT_POINTER +#define STLPLUS_PRINT_POINTER +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Generate a string representation of an object pointed to + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include +#include + +namespace stlplus +{ + + template + void print_pointer(std::ostream& device, + const T* value, + S print_fn, + const std::string& null_string = "", + const std::string& prefix = "(", + const std::string& suffix = ")"); + + +} + +#include "print_pointer.tpp" +#endif diff --git a/src/stlplus/strings/print_pointer.tpp b/src/stlplus/strings/print_pointer.tpp index be96d91..ade9273 100644 --- a/src/stlplus/strings/print_pointer.tpp +++ b/src/stlplus/strings/print_pointer.tpp @@ -1,34 +1,34 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// -#include - -namespace stlplus -{ - - template - void print_pointer(std::ostream& device, - const T* value, - S print_fn, - const std::string& null_string, - const std::string& prefix, - const std::string& suffix) - { - if (value) - { - device << prefix; - print_fn(device, *value); - device << suffix; - } - else - { - device << null_string; - } - } - -} // end namespace stlplus +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include + +namespace stlplus +{ + + template + void print_pointer(std::ostream& device, + const T* value, + S print_fn, + const std::string& null_string, + const std::string& prefix, + const std::string& suffix) + { + if (value) + { + device << prefix; + print_fn(device, *value); + device << suffix; + } + else + { + device << null_string; + } + } + +} // end namespace stlplus diff --git a/src/stlplus/strings/print_sequence.hpp b/src/stlplus/strings/print_sequence.hpp index 411ddd5..317fbf2 100644 --- a/src/stlplus/strings/print_sequence.hpp +++ b/src/stlplus/strings/print_sequence.hpp @@ -1,46 +1,46 @@ -#ifndef STLPLUS_PRINT_SEQUENCE -#define STLPLUS_PRINT_SEQUENCE -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Generate string representations of sequences represented by forward iterators - -//////////////////////////////////////////////////////////////////////////////// -#include "strings_fixes.hpp" -#include -#include - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - // any forward iterator sequence - - template - void print_sequence(std::ostream& device, - I begin, I end, - S print_fn, - const std::string& separator); - - - //////////////////////////////////////////////////////////////////////////////// - // any forward iterator sequence of pairs - - template - void print_pair_sequence(std::ostream& device, - I begin, I end, - S1 print_fn1, - S2 print_fn2, - const std::string& pair_separator, - const std::string& separator); - - //////////////////////////////////////////////////////////////////////////////// - -} // end namespace stlplus - -#include "print_sequence.tpp" -#endif +#ifndef STLPLUS_PRINT_SEQUENCE +#define STLPLUS_PRINT_SEQUENCE +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Generate string representations of sequences represented by forward iterators + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include +#include + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // any forward iterator sequence + + template + void print_sequence(std::ostream& device, + I begin, I end, + S print_fn, + const std::string& separator); + + + //////////////////////////////////////////////////////////////////////////////// + // any forward iterator sequence of pairs + + template + void print_pair_sequence(std::ostream& device, + I begin, I end, + S1 print_fn1, + S2 print_fn2, + const std::string& pair_separator, + const std::string& separator); + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus + +#include "print_sequence.tpp" +#endif diff --git a/src/stlplus/strings/print_sequence.tpp b/src/stlplus/strings/print_sequence.tpp index cea8056..8600ca8 100644 --- a/src/stlplus/strings/print_sequence.tpp +++ b/src/stlplus/strings/print_sequence.tpp @@ -1,50 +1,50 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// -#include "print_pair.hpp" - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - // any forward iterator sequence - - template - void print_sequence(std::ostream& device, - I begin, I end, - S print_fn, - const std::string& separator) - { - for (I i = begin; i != end; i++) - { - if (i != begin) device << separator; - print_fn(device, *i); - } - } - - //////////////////////////////////////////////////////////////////////////////// - // any sequence where the value is a pair - - template - void print_pair_sequence(std::ostream& device, - I begin, I end, - S1 print_fn1, - S2 print_fn2, - const std::string& pair_separator, - const std::string& separator) - { - for (I i = begin; i != end; i++) - { - if (i != begin) device << separator; - print_pair(device, *i, print_fn1, print_fn2, pair_separator); - } - } - - //////////////////////////////////////////////////////////////////////////////// - -} // end namespace stlplus +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "print_pair.hpp" + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // any forward iterator sequence + + template + void print_sequence(std::ostream& device, + I begin, I end, + S print_fn, + const std::string& separator) + { + for (I i = begin; i != end; i++) + { + if (i != begin) device << separator; + print_fn(device, *i); + } + } + + //////////////////////////////////////////////////////////////////////////////// + // any sequence where the value is a pair + + template + void print_pair_sequence(std::ostream& device, + I begin, I end, + S1 print_fn1, + S2 print_fn2, + const std::string& pair_separator, + const std::string& separator) + { + for (I i = begin; i != end; i++) + { + if (i != begin) device << separator; + print_pair(device, *i, print_fn1, print_fn2, pair_separator); + } + } + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus diff --git a/src/stlplus/strings/print_set.hpp b/src/stlplus/strings/print_set.hpp index 33c3835..a032ccf 100644 --- a/src/stlplus/strings/print_set.hpp +++ b/src/stlplus/strings/print_set.hpp @@ -1,36 +1,36 @@ -#ifndef STLPLUS_PRINT_SET -#define STLPLUS_PRINT_SET -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Generate a string representation of a set/multiset - -//////////////////////////////////////////////////////////////////////////////// -#include "strings_fixes.hpp" -#include -#include -#include - -namespace stlplus -{ - - template - void print_set(std::ostream& device, - const std::set& values, - S print_fn, - const std::string& separator = ","); - - template - void print_multiset(std::ostream& device, - const std::multiset& values, - S print_fn, - const std::string& separator = ","); - -} // end namespace stlplus - -#include "print_set.tpp" -#endif +#ifndef STLPLUS_PRINT_SET +#define STLPLUS_PRINT_SET +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Generate a string representation of a set/multiset + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include +#include +#include + +namespace stlplus +{ + + template + void print_set(std::ostream& device, + const std::set& values, + S print_fn, + const std::string& separator = ","); + + template + void print_multiset(std::ostream& device, + const std::multiset& values, + S print_fn, + const std::string& separator = ","); + +} // end namespace stlplus + +#include "print_set.tpp" +#endif diff --git a/src/stlplus/strings/print_set.tpp b/src/stlplus/strings/print_set.tpp index 25c88ef..ba29fd7 100644 --- a/src/stlplus/strings/print_set.tpp +++ b/src/stlplus/strings/print_set.tpp @@ -1,41 +1,41 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// template implementations - -//////////////////////////////////////////////////////////////////////////////// -#include "print_sequence.hpp" - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - // set - - template - void print_set(std::ostream& device, - const std::set& values, - S print_fn, - const std::string& separator) - { - print_sequence(device, values.begin(), values.end(), print_fn, separator); - } - - //////////////////////////////////////////////////////////////////////////////// - // multiset - - template - void print_multiset(std::ostream& device, - const std::multiset& values, - S print_fn, - const std::string& separator) - { - print_sequence(device, values.begin(), values.end(), print_fn, separator); - } - - //////////////////////////////////////////////////////////////////////////////// -} // end namespace stlplus +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// template implementations + +//////////////////////////////////////////////////////////////////////////////// +#include "print_sequence.hpp" + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // set + + template + void print_set(std::ostream& device, + const std::set& values, + S print_fn, + const std::string& separator) + { + print_sequence(device, values.begin(), values.end(), print_fn, separator); + } + + //////////////////////////////////////////////////////////////////////////////// + // multiset + + template + void print_multiset(std::ostream& device, + const std::multiset& values, + S print_fn, + const std::string& separator) + { + print_sequence(device, values.begin(), values.end(), print_fn, separator); + } + + //////////////////////////////////////////////////////////////////////////////// +} // end namespace stlplus diff --git a/src/stlplus/strings/print_simple_ptr.hpp b/src/stlplus/strings/print_simple_ptr.hpp index e34a8ec..de4c17d 100644 --- a/src/stlplus/strings/print_simple_ptr.hpp +++ b/src/stlplus/strings/print_simple_ptr.hpp @@ -1,51 +1,51 @@ -#ifndef STLPLUS_PRINT_SIMPLE_PTR -#define STLPLUS_PRINT_SIMPLE_PTR -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Generate a string representation of a smart pointer - -//////////////////////////////////////////////////////////////////////////////// -#include "strings_fixes.hpp" -#include "simple_ptr.hpp" -#include -#include - -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - - template - void print_simple_ptr(std::ostream& device, - const simple_ptr& value, - S print_fn, - const std::string& null_string = "", - const std::string& prefix = "(", - const std::string& suffix = ")"); - - template - void print_simple_ptr_clone(std::ostream& device, - const simple_ptr_clone& value, - S print_fn, - const std::string& null_string = "", - const std::string& prefix = "(", - const std::string& suffix = ")"); - - template - void print_simple_ptr_nocopy(std::ostream& device, - const simple_ptr_nocopy& value, - S print_fn, - const std::string& null_string = "", - const std::string& prefix = "(", - const std::string& suffix = ")"); - - -} // end namespace stlplus - -#include "print_simple_ptr.tpp" -#endif +#ifndef STLPLUS_PRINT_SIMPLE_PTR +#define STLPLUS_PRINT_SIMPLE_PTR +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Generate a string representation of a smart pointer + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include "simple_ptr.hpp" +#include +#include + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + template + void print_simple_ptr(std::ostream& device, + const simple_ptr& value, + S print_fn, + const std::string& null_string = "", + const std::string& prefix = "(", + const std::string& suffix = ")"); + + template + void print_simple_ptr_clone(std::ostream& device, + const simple_ptr_clone& value, + S print_fn, + const std::string& null_string = "", + const std::string& prefix = "(", + const std::string& suffix = ")"); + + template + void print_simple_ptr_nocopy(std::ostream& device, + const simple_ptr_nocopy& value, + S print_fn, + const std::string& null_string = "", + const std::string& prefix = "(", + const std::string& suffix = ")"); + + +} // end namespace stlplus + +#include "print_simple_ptr.tpp" +#endif diff --git a/src/stlplus/strings/print_simple_ptr.tpp b/src/stlplus/strings/print_simple_ptr.tpp index 30af07e..e4171f2 100644 --- a/src/stlplus/strings/print_simple_ptr.tpp +++ b/src/stlplus/strings/print_simple_ptr.tpp @@ -1,74 +1,74 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - - template - void print_simple_ptr(std::ostream& device, - const simple_ptr& value, - S print_fn, - const std::string& null_string, - const std::string& prefix, - const std::string& suffix) - { - if (value) - { - device << prefix; - print_fn(device, *value); - device << suffix; - } - else - { - device << null_string; - } - } - - template - void print_simple_ptr_clone(std::ostream& device, - const simple_ptr_clone& value, - S print_fn, - const std::string& null_string, - const std::string& prefix, - const std::string& suffix) - { - if (value) - { - device << prefix; - print_fn(device, *value); - device << suffix; - } - else - { - device << null_string; - } - } - - template - void print_simple_ptr_nocopy(std::ostream& device, - const simple_ptr_nocopy& value, - S print_fn, - const std::string& null_string, - const std::string& prefix, - const std::string& suffix) - { - if (value) - { - device << prefix; - print_fn(device, *value); - device << suffix; - } - else - { - device << null_string; - } - } - -} // end namespace stlplus - +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + template + void print_simple_ptr(std::ostream& device, + const simple_ptr& value, + S print_fn, + const std::string& null_string, + const std::string& prefix, + const std::string& suffix) + { + if (value) + { + device << prefix; + print_fn(device, *value); + device << suffix; + } + else + { + device << null_string; + } + } + + template + void print_simple_ptr_clone(std::ostream& device, + const simple_ptr_clone& value, + S print_fn, + const std::string& null_string, + const std::string& prefix, + const std::string& suffix) + { + if (value) + { + device << prefix; + print_fn(device, *value); + device << suffix; + } + else + { + device << null_string; + } + } + + template + void print_simple_ptr_nocopy(std::ostream& device, + const simple_ptr_nocopy& value, + S print_fn, + const std::string& null_string, + const std::string& prefix, + const std::string& suffix) + { + if (value) + { + device << prefix; + print_fn(device, *value); + device << suffix; + } + else + { + device << null_string; + } + } + +} // end namespace stlplus + diff --git a/src/stlplus/strings/print_smart_ptr.hpp b/src/stlplus/strings/print_smart_ptr.hpp index 031c814..da8a8b9 100644 --- a/src/stlplus/strings/print_smart_ptr.hpp +++ b/src/stlplus/strings/print_smart_ptr.hpp @@ -1,51 +1,51 @@ -#ifndef STLPLUS_PRINT_SMART_PTR -#define STLPLUS_PRINT_SMART_PTR -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Generate a string representation of a smart pointer - -//////////////////////////////////////////////////////////////////////////////// -#include "strings_fixes.hpp" -#include "smart_ptr.hpp" -#include -#include - -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - - template - void print_smart_ptr(std::ostream& device, - const smart_ptr& value, - S print_fn, - const std::string& null_string = "", - const std::string& prefix = "(", - const std::string& suffix = ")"); - - template - void print_smart_ptr_clone(std::ostream& device, - const smart_ptr_clone& value, - S print_fn, - const std::string& null_string = "", - const std::string& prefix = "(", - const std::string& suffix = ")"); - - template - void print_smart_ptr_nocopy(std::ostream& device, - const smart_ptr_nocopy& value, - S print_fn, - const std::string& null_string = "", - const std::string& prefix = "(", - const std::string& suffix = ")"); - - -} // end namespace stlplus - -#include "print_smart_ptr.tpp" -#endif +#ifndef STLPLUS_PRINT_SMART_PTR +#define STLPLUS_PRINT_SMART_PTR +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Generate a string representation of a smart pointer + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include "smart_ptr.hpp" +#include +#include + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + template + void print_smart_ptr(std::ostream& device, + const smart_ptr& value, + S print_fn, + const std::string& null_string = "", + const std::string& prefix = "(", + const std::string& suffix = ")"); + + template + void print_smart_ptr_clone(std::ostream& device, + const smart_ptr_clone& value, + S print_fn, + const std::string& null_string = "", + const std::string& prefix = "(", + const std::string& suffix = ")"); + + template + void print_smart_ptr_nocopy(std::ostream& device, + const smart_ptr_nocopy& value, + S print_fn, + const std::string& null_string = "", + const std::string& prefix = "(", + const std::string& suffix = ")"); + + +} // end namespace stlplus + +#include "print_smart_ptr.tpp" +#endif diff --git a/src/stlplus/strings/print_smart_ptr.tpp b/src/stlplus/strings/print_smart_ptr.tpp index 51877c7..e57fc98 100644 --- a/src/stlplus/strings/print_smart_ptr.tpp +++ b/src/stlplus/strings/print_smart_ptr.tpp @@ -1,74 +1,74 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - - template - void print_smart_ptr(std::ostream& device, - const smart_ptr& value, - S print_fn, - const std::string& null_string, - const std::string& prefix, - const std::string& suffix) - { - if (value) - { - device << prefix; - print_fn(device, *value); - device << suffix; - } - else - { - device << null_string; - } - } - - template - void print_smart_ptr_clone(std::ostream& device, - const smart_ptr_clone& value, - S print_fn, - const std::string& null_string, - const std::string& prefix, - const std::string& suffix) - { - if (value) - { - device << prefix; - print_fn(device, *value); - device << suffix; - } - else - { - device << null_string; - } - } - - template - void print_smart_ptr_nocopy(std::ostream& device, - const smart_ptr_nocopy& value, - S print_fn, - const std::string& null_string, - const std::string& prefix, - const std::string& suffix) - { - if (value) - { - device << prefix; - print_fn(device, *value); - device << suffix; - } - else - { - device << null_string; - } - } - -} // end namespace stlplus - +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + template + void print_smart_ptr(std::ostream& device, + const smart_ptr& value, + S print_fn, + const std::string& null_string, + const std::string& prefix, + const std::string& suffix) + { + if (value) + { + device << prefix; + print_fn(device, *value); + device << suffix; + } + else + { + device << null_string; + } + } + + template + void print_smart_ptr_clone(std::ostream& device, + const smart_ptr_clone& value, + S print_fn, + const std::string& null_string, + const std::string& prefix, + const std::string& suffix) + { + if (value) + { + device << prefix; + print_fn(device, *value); + device << suffix; + } + else + { + device << null_string; + } + } + + template + void print_smart_ptr_nocopy(std::ostream& device, + const smart_ptr_nocopy& value, + S print_fn, + const std::string& null_string, + const std::string& prefix, + const std::string& suffix) + { + if (value) + { + device << prefix; + print_fn(device, *value); + device << suffix; + } + else + { + device << null_string; + } + } + +} // end namespace stlplus + diff --git a/src/stlplus/strings/print_stl.hpp b/src/stlplus/strings/print_stl.hpp index fb2b553..0d02839 100644 --- a/src/stlplus/strings/print_stl.hpp +++ b/src/stlplus/strings/print_stl.hpp @@ -1,23 +1,23 @@ -#ifndef STLPLUS_PRINT_STL -#define STLPLUS_PRINT_STL -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Template string conversions for pointers and STL containers - -//////////////////////////////////////////////////////////////////////////////// - -#include "print_bitset.hpp" -#include "print_list.hpp" -#include "print_map.hpp" -#include "print_pair.hpp" -#include "print_sequence.hpp" -#include "print_set.hpp" -#include "print_string.hpp" -#include "print_vector.hpp" - -#endif +#ifndef STLPLUS_PRINT_STL +#define STLPLUS_PRINT_STL +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Template string conversions for pointers and STL containers + +//////////////////////////////////////////////////////////////////////////////// + +#include "print_bitset.hpp" +#include "print_list.hpp" +#include "print_map.hpp" +#include "print_pair.hpp" +#include "print_sequence.hpp" +#include "print_set.hpp" +#include "print_string.hpp" +#include "print_vector.hpp" + +#endif diff --git a/src/stlplus/strings/print_stlplus.hpp b/src/stlplus/strings/print_stlplus.hpp index 3f2cd46..cccec48 100644 --- a/src/stlplus/strings/print_stlplus.hpp +++ b/src/stlplus/strings/print_stlplus.hpp @@ -1,26 +1,26 @@ -#ifndef STLPLUS_PRINT_STLPLUS -#define STLPLUS_PRINT_STLPLUS -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Template string conversions for the STLplus containers - -//////////////////////////////////////////////////////////////////////////////// - -#ifndef NO_STLPLUS_CONTAINERS -#include "print_digraph.hpp" -#include "print_foursome.hpp" -#include "print_hash.hpp" -#include "print_matrix.hpp" -#include "print_ntree.hpp" -#include "print_smart_ptr.hpp" -#include "print_triple.hpp" -#endif - -#include "print_inf.hpp" - -#endif +#ifndef STLPLUS_PRINT_STLPLUS +#define STLPLUS_PRINT_STLPLUS +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Template string conversions for the STLplus containers + +//////////////////////////////////////////////////////////////////////////////// + +#ifndef NO_STLPLUS_CONTAINERS +#include "print_digraph.hpp" +#include "print_foursome.hpp" +#include "print_hash.hpp" +#include "print_matrix.hpp" +#include "print_ntree.hpp" +#include "print_smart_ptr.hpp" +#include "print_triple.hpp" +#endif + +#include "print_inf.hpp" + +#endif diff --git a/src/stlplus/strings/print_string.cpp b/src/stlplus/strings/print_string.cpp index 8cf3b5b..acd25b7 100644 --- a/src/stlplus/strings/print_string.cpp +++ b/src/stlplus/strings/print_string.cpp @@ -1,22 +1,22 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// -#include "print_string.hpp" - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - // strings - - void print_string(std::ostream& device, const std::string& value) - { - device << value; - } - -} // end namespace stlplus +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "print_string.hpp" + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // strings + + void print_string(std::ostream& device, const std::string& value) + { + device << value; + } + +} // end namespace stlplus diff --git a/src/stlplus/strings/print_string.hpp b/src/stlplus/strings/print_string.hpp index dd23f56..42ba9ab 100644 --- a/src/stlplus/strings/print_string.hpp +++ b/src/stlplus/strings/print_string.hpp @@ -1,26 +1,26 @@ -#ifndef STLPLUS_PRINT_STRING -#define STLPLUS_PRINT_STRING -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Functions for converting C/STL strings to string - -// This is necessary for completeness, e.g. for use in print_vector for vector - -//////////////////////////////////////////////////////////////////////////////// -#include "strings_fixes.hpp" -#include -#include -#include - -namespace stlplus -{ - - void print_string(std::ostream& device, const std::string& value); -} - -#endif +#ifndef STLPLUS_PRINT_STRING +#define STLPLUS_PRINT_STRING +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Functions for converting C/STL strings to string + +// This is necessary for completeness, e.g. for use in print_vector for vector + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include +#include +#include + +namespace stlplus +{ + + void print_string(std::ostream& device, const std::string& value); +} + +#endif diff --git a/src/stlplus/strings/print_triple.hpp b/src/stlplus/strings/print_triple.hpp index 37b7af9..c5867c8 100644 --- a/src/stlplus/strings/print_triple.hpp +++ b/src/stlplus/strings/print_triple.hpp @@ -1,34 +1,34 @@ -#ifndef STLPLUS_PRINT_TRIPLE -#define STLPLUS_PRINT_TRIPLE -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Generate a string representation of a triple - -//////////////////////////////////////////////////////////////////////////////// -#include "strings_fixes.hpp" -#include "triple.hpp" -#include -#include - -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - - template - void print_triple(std::ostream& device, - const triple& values, - S1 print_fn1, - S2 print_fn2, - S3 print_fn3, - const std::string& separator = ":"); - -} // end namespace stlplus - -#include "print_triple.tpp" -#endif +#ifndef STLPLUS_PRINT_TRIPLE +#define STLPLUS_PRINT_TRIPLE +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Generate a string representation of a triple + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include "triple.hpp" +#include +#include + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + template + void print_triple(std::ostream& device, + const triple& values, + S1 print_fn1, + S2 print_fn2, + S3 print_fn3, + const std::string& separator = ":"); + +} // end namespace stlplus + +#include "print_triple.tpp" +#endif diff --git a/src/stlplus/strings/print_triple.tpp b/src/stlplus/strings/print_triple.tpp index 079c592..08867bf 100644 --- a/src/stlplus/strings/print_triple.tpp +++ b/src/stlplus/strings/print_triple.tpp @@ -1,30 +1,30 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - - template - void print_triple(std::ostream& device, - const triple& values, - S1 print_fn1, - S2 print_fn2, - S3 print_fn3, - const std::string& separator) - { - - print_fn1(device, values.first); - device << separator; - print_fn2(device, values.second); - device << separator; - print_fn3(device, values.third); - } - -} // end namespace stlplus - +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + template + void print_triple(std::ostream& device, + const triple& values, + S1 print_fn1, + S2 print_fn2, + S3 print_fn3, + const std::string& separator) + { + + print_fn1(device, values.first); + device << separator; + print_fn2(device, values.second); + device << separator; + print_fn3(device, values.third); + } + +} // end namespace stlplus + diff --git a/src/stlplus/strings/print_vector.cpp b/src/stlplus/strings/print_vector.cpp index 41ea42e..c5b662f 100644 --- a/src/stlplus/strings/print_vector.cpp +++ b/src/stlplus/strings/print_vector.cpp @@ -1,25 +1,25 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// -#include "print_vector.hpp" -#include "string_vector.hpp" - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - // special case of vector - - void print_bool_vector(std::ostream& device, const std::vector& values) - { - device << bool_vector_to_string(values); - } - - //////////////////////////////////////////////////////////////////////////////// - -} // end namespace stlplus +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "print_vector.hpp" +#include "string_vector.hpp" + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // special case of vector + + void print_bool_vector(std::ostream& device, const std::vector& values) + { + device << bool_vector_to_string(values); + } + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus diff --git a/src/stlplus/strings/print_vector.hpp b/src/stlplus/strings/print_vector.hpp index 5916532..56d5aeb 100644 --- a/src/stlplus/strings/print_vector.hpp +++ b/src/stlplus/strings/print_vector.hpp @@ -1,38 +1,38 @@ -#ifndef STLPLUS_PRINT_VECTOR -#define STLPLUS_PRINT_VECTOR -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Generate a string representation of a vector - -//////////////////////////////////////////////////////////////////////////////// -#include "strings_fixes.hpp" -#include -#include -#include - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - // vector - - template - void print_vector(std::ostream& device, - const std::vector& values, - S print_fn, - const std::string& separator); - - // specialisation for vector which has a different implementation - void print_bool_vector(std::ostream& device, const std::vector& values); - - //////////////////////////////////////////////////////////////////////////////// - -} // end namespace stlplus - -#include "print_vector.tpp" -#endif +#ifndef STLPLUS_PRINT_VECTOR +#define STLPLUS_PRINT_VECTOR +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Generate a string representation of a vector + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include +#include +#include + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // vector + + template + void print_vector(std::ostream& device, + const std::vector& values, + S print_fn, + const std::string& separator); + + // specialisation for vector which has a different implementation + void print_bool_vector(std::ostream& device, const std::vector& values); + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus + +#include "print_vector.tpp" +#endif diff --git a/src/stlplus/strings/print_vector.tpp b/src/stlplus/strings/print_vector.tpp index 102cfd2..ef71bdc 100644 --- a/src/stlplus/strings/print_vector.tpp +++ b/src/stlplus/strings/print_vector.tpp @@ -1,22 +1,22 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// -#include "print_sequence.hpp" - -namespace stlplus -{ - - template - void print_vector(std::ostream& device, const std::vector& values, - S print_fn, - const std::string& separator) - { - print_sequence(device, values.begin(), values.end(), print_fn, separator); - } - -} // end namespace stlplus +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "print_sequence.hpp" + +namespace stlplus +{ + + template + void print_vector(std::ostream& device, const std::vector& values, + S print_fn, + const std::string& separator) + { + print_sequence(device, values.begin(), values.end(), print_fn, separator); + } + +} // end namespace stlplus diff --git a/src/stlplus/strings/string_address.cpp b/src/stlplus/strings/string_address.cpp index 74315e0..1ab934b 100644 --- a/src/stlplus/strings/string_address.cpp +++ b/src/stlplus/strings/string_address.cpp @@ -1,36 +1,36 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// use the unsigned long representation for pointers - -//////////////////////////////////////////////////////////////////////////////// -#include "string_address.hpp" -#include "string_int.hpp" -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - - std::string address_to_string(const void* i, unsigned radix, radix_display_t display, unsigned width) - throw(std::invalid_argument) - { - return unsigned_long_to_string((unsigned long)i, radix, display, width); - } - - //////////////////////////////////////////////////////////////////////////////// - - void* string_to_address(const std::string& str, unsigned radix) - throw(std::invalid_argument) - { - return (void*)string_to_unsigned_long(str, radix); - } - - //////////////////////////////////////////////////////////////////////////////// - -} // end namespace stlplus +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// use the unsigned long representation for pointers + +//////////////////////////////////////////////////////////////////////////////// +#include "string_address.hpp" +#include "string_int.hpp" +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + + std::string address_to_string(const void* i, unsigned radix, radix_display_t display, unsigned width) + throw(std::invalid_argument) + { + return unsigned_long_to_string((unsigned long)i, radix, display, width); + } + + //////////////////////////////////////////////////////////////////////////////// + + void* string_to_address(const std::string& str, unsigned radix) + throw(std::invalid_argument) + { + return (void*)string_to_unsigned_long(str, radix); + } + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus diff --git a/src/stlplus/strings/string_address.hpp b/src/stlplus/strings/string_address.hpp index f73d581..dea3a5f 100644 --- a/src/stlplus/strings/string_address.hpp +++ b/src/stlplus/strings/string_address.hpp @@ -1,39 +1,39 @@ -#ifndef STLPLUS_STRING_ADDRESS -#define STLPLUS_STRING_ADDRESS -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Functions for converting addresses to/from strings - -//////////////////////////////////////////////////////////////////////////////// -#include "strings_fixes.hpp" -#include "format_types.hpp" -#include -#include - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - - std::string address_to_string(const void*, - unsigned radix = 16, - radix_display_t display = radix_c_style_or_hash, - unsigned width = 0) - throw(std::invalid_argument); - - //////////////////////////////////////////////////////////////////////////////// - - void* string_to_address(const std::string& value, - unsigned radix = 0) - throw(std::invalid_argument); - - //////////////////////////////////////////////////////////////////////////////// - -} // end namespace stlplus - -#endif +#ifndef STLPLUS_STRING_ADDRESS +#define STLPLUS_STRING_ADDRESS +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Functions for converting addresses to/from strings + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include "format_types.hpp" +#include +#include + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + + std::string address_to_string(const void*, + unsigned radix = 16, + radix_display_t display = radix_c_style_or_hash, + unsigned width = 0) + throw(std::invalid_argument); + + //////////////////////////////////////////////////////////////////////////////// + + void* string_to_address(const std::string& value, + unsigned radix = 0) + throw(std::invalid_argument); + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus + +#endif diff --git a/src/stlplus/strings/string_basic.hpp b/src/stlplus/strings/string_basic.hpp index f88dbcd..d402dfd 100644 --- a/src/stlplus/strings/string_basic.hpp +++ b/src/stlplus/strings/string_basic.hpp @@ -1,21 +1,21 @@ -#ifndef STLPLUS_STRING_BASIC -#define STLPLUS_STRING_BASIC -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Utilities for converting basic C types to/from std::strings - -//////////////////////////////////////////////////////////////////////////////// - -#include "string_address.hpp" -#include "string_bool.hpp" -#include "string_cstring.hpp" -#include "string_float.hpp" -#include "string_int.hpp" -#include "string_pointer.hpp" - -#endif +#ifndef STLPLUS_STRING_BASIC +#define STLPLUS_STRING_BASIC +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Utilities for converting basic C types to/from std::strings + +//////////////////////////////////////////////////////////////////////////////// + +#include "string_address.hpp" +#include "string_bool.hpp" +#include "string_cstring.hpp" +#include "string_float.hpp" +#include "string_int.hpp" +#include "string_pointer.hpp" + +#endif diff --git a/src/stlplus/strings/string_bitset.hpp b/src/stlplus/strings/string_bitset.hpp index 8c96ef3..ec275cd 100644 --- a/src/stlplus/strings/string_bitset.hpp +++ b/src/stlplus/strings/string_bitset.hpp @@ -1,26 +1,26 @@ -#ifndef STLPLUS_STRING_BITSET -#define STLPLUS_STRING_BITSET -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Generate a string representation of a bitset - -//////////////////////////////////////////////////////////////////////////////// -#include "strings_fixes.hpp" -#include -#include - -namespace stlplus -{ - - template - std::string bitset_to_string(const std::bitset& data); - -} // end namespace stlplus - -#include "string_bitset.tpp" -#endif +#ifndef STLPLUS_STRING_BITSET +#define STLPLUS_STRING_BITSET +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Generate a string representation of a bitset + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include +#include + +namespace stlplus +{ + + template + std::string bitset_to_string(const std::bitset& data); + +} // end namespace stlplus + +#include "string_bitset.tpp" +#endif diff --git a/src/stlplus/strings/string_bitset.tpp b/src/stlplus/strings/string_bitset.tpp index 13b78a3..b86a612 100644 --- a/src/stlplus/strings/string_bitset.tpp +++ b/src/stlplus/strings/string_bitset.tpp @@ -1,22 +1,22 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - - template - std::string bitset_to_string(const std::bitset& data) - { - std::string result; - for (unsigned i = data.size(); i--; ) - result += data.test(i) ? '1' : '0'; - return result; - } - -} // end namespace stlplus +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + template + std::string bitset_to_string(const std::bitset& data) + { + std::string result; + for (unsigned i = data.size(); i--; ) + result += data.test(i) ? '1' : '0'; + return result; + } + +} // end namespace stlplus diff --git a/src/stlplus/strings/string_bool.cpp b/src/stlplus/strings/string_bool.cpp index ee25231..9c70090 100644 --- a/src/stlplus/strings/string_bool.cpp +++ b/src/stlplus/strings/string_bool.cpp @@ -1,35 +1,35 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// use the unsigned short representation for bool - -//////////////////////////////////////////////////////////////////////////////// -#include "string_bool.hpp" -#include "string_int.hpp" - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - - std::string bool_to_string(bool i, unsigned radix, radix_display_t display, unsigned width) - throw(std::invalid_argument) - { - return unsigned_short_to_string((unsigned short)i, radix, display, width); - } - - //////////////////////////////////////////////////////////////////////////////// - - bool string_to_bool(const std::string& str, unsigned radix) - throw(std::invalid_argument) - { - return string_to_unsigned_short(str, radix) != 0; - } - - //////////////////////////////////////////////////////////////////////////////// - -} // end namespace stlplus +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// use the unsigned short representation for bool + +//////////////////////////////////////////////////////////////////////////////// +#include "string_bool.hpp" +#include "string_int.hpp" + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + + std::string bool_to_string(bool i, unsigned radix, radix_display_t display, unsigned width) + throw(std::invalid_argument) + { + return unsigned_short_to_string((unsigned short)i, radix, display, width); + } + + //////////////////////////////////////////////////////////////////////////////// + + bool string_to_bool(const std::string& str, unsigned radix) + throw(std::invalid_argument) + { + return string_to_unsigned_short(str, radix) != 0; + } + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus diff --git a/src/stlplus/strings/string_bool.hpp b/src/stlplus/strings/string_bool.hpp index adadcc6..fea8a85 100644 --- a/src/stlplus/strings/string_bool.hpp +++ b/src/stlplus/strings/string_bool.hpp @@ -1,39 +1,39 @@ -#ifndef STLPLUS_STRING_BOOL -#define STLPLUS_STRING_BOOL -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Conversion of string to/from bool - -//////////////////////////////////////////////////////////////////////////////// -#include "strings_fixes.hpp" -#include "format_types.hpp" -#include -#include - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - - std::string bool_to_string(bool i, - unsigned radix = 10, - radix_display_t display = radix_c_style_or_hash, - unsigned width = 0) - throw(std::invalid_argument); - - //////////////////////////////////////////////////////////////////////////////// - - bool string_to_bool(const std::string& value, - unsigned radix = 0) - throw(std::invalid_argument); - - //////////////////////////////////////////////////////////////////////////////// - -} // end namespace stlplus - -#endif +#ifndef STLPLUS_STRING_BOOL +#define STLPLUS_STRING_BOOL +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Conversion of string to/from bool + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include "format_types.hpp" +#include +#include + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + + std::string bool_to_string(bool i, + unsigned radix = 10, + radix_display_t display = radix_c_style_or_hash, + unsigned width = 0) + throw(std::invalid_argument); + + //////////////////////////////////////////////////////////////////////////////// + + bool string_to_bool(const std::string& value, + unsigned radix = 0) + throw(std::invalid_argument); + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus + +#endif diff --git a/src/stlplus/strings/string_cstring.cpp b/src/stlplus/strings/string_cstring.cpp index 0745049..b1129a9 100644 --- a/src/stlplus/strings/string_cstring.cpp +++ b/src/stlplus/strings/string_cstring.cpp @@ -1,19 +1,19 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// -#include "string_cstring.hpp" - -namespace stlplus -{ - - std::string cstring_to_string(const char* value) - { - return std::string(value); - } - -} // end namespace stlplus +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "string_cstring.hpp" + +namespace stlplus +{ + + std::string cstring_to_string(const char* value) + { + return std::string(value); + } + +} // end namespace stlplus diff --git a/src/stlplus/strings/string_cstring.hpp b/src/stlplus/strings/string_cstring.hpp index 554fbe3..7d04940 100644 --- a/src/stlplus/strings/string_cstring.hpp +++ b/src/stlplus/strings/string_cstring.hpp @@ -1,26 +1,26 @@ -#ifndef STLPLUS_STRING_CSTRING -#define STLPLUS_STRING_CSTRING -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Functions for converting C strings to string - -// This is necessary for completeness - -//////////////////////////////////////////////////////////////////////////////// -#include "strings_fixes.hpp" -#include -#include - -namespace stlplus -{ - - std::string cstring_to_string(const char* value); - -} - -#endif +#ifndef STLPLUS_STRING_CSTRING +#define STLPLUS_STRING_CSTRING +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Functions for converting C strings to string + +// This is necessary for completeness + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include +#include + +namespace stlplus +{ + + std::string cstring_to_string(const char* value); + +} + +#endif diff --git a/src/stlplus/strings/string_digraph.hpp b/src/stlplus/strings/string_digraph.hpp index ea70cc7..f499934 100644 --- a/src/stlplus/strings/string_digraph.hpp +++ b/src/stlplus/strings/string_digraph.hpp @@ -1,31 +1,31 @@ -#ifndef STLPLUS_STRING_DIGRAPH -#define STLPLUS_STRING_DIGRAPH -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Generate a string representation of a digraph - -//////////////////////////////////////////////////////////////////////////////// -#include "strings_fixes.hpp" -#include "digraph.hpp" -#include - -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - - template - std::string digraph_to_string(const digraph& values, - NS node_to_string_fn, - AS arc_to_string_fn, - const std::string& separator = ","); - -} // end namespace stlplus - -#include "string_digraph.tpp" -#endif +#ifndef STLPLUS_STRING_DIGRAPH +#define STLPLUS_STRING_DIGRAPH +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Generate a string representation of a digraph + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include "digraph.hpp" +#include + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + template + std::string digraph_to_string(const digraph& values, + NS node_to_string_fn, + AS arc_to_string_fn, + const std::string& separator = ","); + +} // end namespace stlplus + +#include "string_digraph.tpp" +#endif diff --git a/src/stlplus/strings/string_digraph.tpp b/src/stlplus/strings/string_digraph.tpp index 9fd63d5..4b81a5c 100644 --- a/src/stlplus/strings/string_digraph.tpp +++ b/src/stlplus/strings/string_digraph.tpp @@ -1,33 +1,33 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// -#include "string_sequence.hpp" - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - - template - std::string digraph_to_string(const digraph& values, - NS node_to_string_fn, - AS arc_to_string_fn, - const std::string& separator) - { - std::string result; - result += "nodes:"; - result += separator; - result += sequence_to_string(values.begin(), values.end(), node_to_string_fn, separator); - result += "arcs:"; - result += separator; - result += sequence_to_string(values.arc_begin(), values.arc_end(), arc_to_string_fn, separator); - return result; - } - -} // end namespace stlplus - +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "string_sequence.hpp" + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + + template + std::string digraph_to_string(const digraph& values, + NS node_to_string_fn, + AS arc_to_string_fn, + const std::string& separator) + { + std::string result; + result += "nodes:"; + result += separator; + result += sequence_to_string(values.begin(), values.end(), node_to_string_fn, separator); + result += "arcs:"; + result += separator; + result += sequence_to_string(values.arc_begin(), values.arc_end(), arc_to_string_fn, separator); + return result; + } + +} // end namespace stlplus + diff --git a/src/stlplus/strings/string_float.cpp b/src/stlplus/strings/string_float.cpp index 0de056f..8ef2a94 100644 --- a/src/stlplus/strings/string_float.cpp +++ b/src/stlplus/strings/string_float.cpp @@ -1,96 +1,96 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// -#include "string_float.hpp" -#include -#include -#include -#include - -namespace stlplus -{ - - // added as a local copy to break the dependency on the portability library - static std::string local_dformat(const char* format, ...) throw(std::invalid_argument) - { - std::string formatted; - va_list args; - va_start(args, format); -#ifdef MSWINDOWS - int length = 0; - char* buffer = 0; - for(int buffer_length = 256; ; buffer_length*=2) - { - buffer = (char*)malloc(buffer_length); - if (!buffer) throw std::invalid_argument("string_float"); - length = _vsnprintf(buffer, buffer_length-1, format, args); - if (length >= 0) - { - buffer[length] = 0; - formatted += std::string(buffer); - free(buffer); - break; - } - free(buffer); - } -#else - char* buffer = 0; - int length = vasprintf(&buffer, format, args); - if (!buffer) throw std::invalid_argument("string_float"); - if (length >= 0) - formatted += std::string(buffer); - free(buffer); -#endif - va_end(args); - if (length < 0) throw std::invalid_argument("string_float"); - return formatted; - } - - //////////////////////////////////////////////////////////////////////////////// - // floating-point types - - std::string float_to_string(float f, real_display_t display, unsigned width, unsigned precision) - throw(std::invalid_argument) - { - return double_to_string((double)f, display, width, precision); - } - - std::string double_to_string(double f, real_display_t display, unsigned width, unsigned precision) - throw(std::invalid_argument) - { - switch(display) - { - case display_fixed: - return local_dformat("%*.*f", width, precision, f); - case display_floating: - return local_dformat("%*.*e", width, precision, f); - case display_mixed: - return local_dformat("%*.*g", width, precision, f); - default: - throw std::invalid_argument("invalid radix display value"); - } - } - - //////////////////////////////////////////////////////////////////////////////// - - float string_to_float(const std::string& value) - throw(std::invalid_argument) - { - return (float)string_to_double(value); - } - - double string_to_double(const std::string& value) - throw(std::invalid_argument) - { - // TODO - error checking - return strtod(value.c_str(), 0); - } - - //////////////////////////////////////////////////////////////////////////////// - -} // end namespace stlplus +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "string_float.hpp" +#include +#include +#include +#include + +namespace stlplus +{ + + // added as a local copy to break the dependency on the portability library + static std::string local_dformat(const char* format, ...) throw(std::invalid_argument) + { + std::string formatted; + va_list args; + va_start(args, format); +#ifdef MSWINDOWS + int length = 0; + char* buffer = 0; + for(int buffer_length = 256; ; buffer_length*=2) + { + buffer = (char*)malloc(buffer_length); + if (!buffer) throw std::invalid_argument("string_float"); + length = _vsnprintf(buffer, buffer_length-1, format, args); + if (length >= 0) + { + buffer[length] = 0; + formatted += std::string(buffer); + free(buffer); + break; + } + free(buffer); + } +#else + char* buffer = 0; + int length = vasprintf(&buffer, format, args); + if (!buffer) throw std::invalid_argument("string_float"); + if (length >= 0) + formatted += std::string(buffer); + free(buffer); +#endif + va_end(args); + if (length < 0) throw std::invalid_argument("string_float"); + return formatted; + } + + //////////////////////////////////////////////////////////////////////////////// + // floating-point types + + std::string float_to_string(float f, real_display_t display, unsigned width, unsigned precision) + throw(std::invalid_argument) + { + return double_to_string((double)f, display, width, precision); + } + + std::string double_to_string(double f, real_display_t display, unsigned width, unsigned precision) + throw(std::invalid_argument) + { + switch(display) + { + case display_fixed: + return local_dformat("%*.*f", width, precision, f); + case display_floating: + return local_dformat("%*.*e", width, precision, f); + case display_mixed: + return local_dformat("%*.*g", width, precision, f); + default: + throw std::invalid_argument("invalid radix display value"); + } + } + + //////////////////////////////////////////////////////////////////////////////// + + float string_to_float(const std::string& value) + throw(std::invalid_argument) + { + return (float)string_to_double(value); + } + + double string_to_double(const std::string& value) + throw(std::invalid_argument) + { + // TODO - error checking + return strtod(value.c_str(), 0); + } + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus diff --git a/src/stlplus/strings/string_float.hpp b/src/stlplus/strings/string_float.hpp index 797d4a4..578225f 100644 --- a/src/stlplus/strings/string_float.hpp +++ b/src/stlplus/strings/string_float.hpp @@ -1,55 +1,55 @@ -#ifndef STLPLUS_STRING_FLOAT -#define STLPLUS_STRING_FLOAT -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Convert a float/double to/from string - -//////////////////////////////////////////////////////////////////////////////// -#include "strings_fixes.hpp" -#include "format_types.hpp" -#include -#include - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - // convert a real type to string - //////////////////////////////////////////////////////////////////////////////// - - // Only decimal radix is supported - - // The way in which the number is displayed is defined in radix_types.hpp - // Using any other value for the display type causes std::invalid_argument to be thrown - - std::string float_to_string(float f, - real_display_t display = display_mixed, - unsigned width = 0, - unsigned precision = 6) - throw(std::invalid_argument); - - std::string double_to_string(double f, - real_display_t display = display_mixed, - unsigned width = 0, - unsigned precision = 6) - throw(std::invalid_argument); - - //////////////////////////////////////////////////////////////////////////////// - // Convert a string to a floating-point type - - float string_to_float(const std::string& value) - throw(std::invalid_argument); - - double string_to_double(const std::string& value) - throw(std::invalid_argument); - - //////////////////////////////////////////////////////////////////////////////// - -} // end namespace stlplus - -#endif +#ifndef STLPLUS_STRING_FLOAT +#define STLPLUS_STRING_FLOAT +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Convert a float/double to/from string + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include "format_types.hpp" +#include +#include + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // convert a real type to string + //////////////////////////////////////////////////////////////////////////////// + + // Only decimal radix is supported + + // The way in which the number is displayed is defined in radix_types.hpp + // Using any other value for the display type causes std::invalid_argument to be thrown + + std::string float_to_string(float f, + real_display_t display = display_mixed, + unsigned width = 0, + unsigned precision = 6) + throw(std::invalid_argument); + + std::string double_to_string(double f, + real_display_t display = display_mixed, + unsigned width = 0, + unsigned precision = 6) + throw(std::invalid_argument); + + //////////////////////////////////////////////////////////////////////////////// + // Convert a string to a floating-point type + + float string_to_float(const std::string& value) + throw(std::invalid_argument); + + double string_to_double(const std::string& value) + throw(std::invalid_argument); + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus + +#endif diff --git a/src/stlplus/strings/string_foursome.hpp b/src/stlplus/strings/string_foursome.hpp index 1cf3db7..7481dc7 100644 --- a/src/stlplus/strings/string_foursome.hpp +++ b/src/stlplus/strings/string_foursome.hpp @@ -1,33 +1,33 @@ -#ifndef STLPLUS_STRING_FOURSOME -#define STLPLUS_STRING_FOURSOME -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Generate a string representation of a foursome - -//////////////////////////////////////////////////////////////////////////////// -#include "strings_fixes.hpp" -#include "foursome.hpp" -#include - -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - - template - std::string foursome_to_string(const foursome& values, - S1 to_string_fn1, - S2 to_string_fn2, - S3 to_string_fn3, - S4 to_string_fn4, - const std::string& separator = ":"); - -} // end namespace stlplus - -#include "string_foursome.tpp" -#endif +#ifndef STLPLUS_STRING_FOURSOME +#define STLPLUS_STRING_FOURSOME +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Generate a string representation of a foursome + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include "foursome.hpp" +#include + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + template + std::string foursome_to_string(const foursome& values, + S1 to_string_fn1, + S2 to_string_fn2, + S3 to_string_fn3, + S4 to_string_fn4, + const std::string& separator = ":"); + +} // end namespace stlplus + +#include "string_foursome.tpp" +#endif diff --git a/src/stlplus/strings/string_foursome.tpp b/src/stlplus/strings/string_foursome.tpp index a3e1536..db761f4 100644 --- a/src/stlplus/strings/string_foursome.tpp +++ b/src/stlplus/strings/string_foursome.tpp @@ -1,33 +1,33 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - - template - std::string foursome_to_string(const foursome& values, - S1 to_string_fn1, - S2 to_string_fn2, - S3 to_string_fn3, - S4 to_string_fn4, - const std::string& separator) - { - return - to_string_fn1(values.first) + - separator + - to_string_fn2(values.second) + - separator + - to_string_fn3(values.third) + - separator + - to_string_fn4(values.fourth); - } - - -} // end namespace stlplus - +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + template + std::string foursome_to_string(const foursome& values, + S1 to_string_fn1, + S2 to_string_fn2, + S3 to_string_fn3, + S4 to_string_fn4, + const std::string& separator) + { + return + to_string_fn1(values.first) + + separator + + to_string_fn2(values.second) + + separator + + to_string_fn3(values.third) + + separator + + to_string_fn4(values.fourth); + } + + +} // end namespace stlplus + diff --git a/src/stlplus/strings/string_hash.hpp b/src/stlplus/strings/string_hash.hpp index a01f87b..5a94bb2 100644 --- a/src/stlplus/strings/string_hash.hpp +++ b/src/stlplus/strings/string_hash.hpp @@ -1,32 +1,32 @@ -#ifndef STLPLUS_STRING_HASH -#define STLPLUS_STRING_HASH -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Generate a string representation of a hash - -//////////////////////////////////////////////////////////////////////////////// -#include "strings_fixes.hpp" -#include "hash.hpp" -#include - -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - - template - std::string hash_to_string(const hash& values, - KS key_to_string_fn, - TS value_to_string_fn, - const std::string& pair_separator = ":", - const std::string& separator = ","); - -} // end namespace stlplus - -#include "string_hash.tpp" -#endif +#ifndef STLPLUS_STRING_HASH +#define STLPLUS_STRING_HASH +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Generate a string representation of a hash + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include "hash.hpp" +#include + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + template + std::string hash_to_string(const hash& values, + KS key_to_string_fn, + TS value_to_string_fn, + const std::string& pair_separator = ":", + const std::string& separator = ","); + +} // end namespace stlplus + +#include "string_hash.tpp" +#endif diff --git a/src/stlplus/strings/string_hash.tpp b/src/stlplus/strings/string_hash.tpp index b3b2a41..1d77f42 100644 --- a/src/stlplus/strings/string_hash.tpp +++ b/src/stlplus/strings/string_hash.tpp @@ -1,27 +1,27 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// -#include "string_sequence.hpp" - -namespace stlplus -{ - - template - std::string hash_to_string(const hash& values, - KS key_to_string_fn, - TS value_to_string_fn, - const std::string& pair_separator, - const std::string& separator) - { - return pair_sequence_to_string(values.begin(), values.end(), - key_to_string_fn, value_to_string_fn, - pair_separator, separator); - } - -} // end namespace stlplus - +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "string_sequence.hpp" + +namespace stlplus +{ + + template + std::string hash_to_string(const hash& values, + KS key_to_string_fn, + TS value_to_string_fn, + const std::string& pair_separator, + const std::string& separator) + { + return pair_sequence_to_string(values.begin(), values.end(), + key_to_string_fn, value_to_string_fn, + pair_separator, separator); + } + +} // end namespace stlplus + diff --git a/src/stlplus/strings/string_inf.cpp b/src/stlplus/strings/string_inf.cpp index 1734335..e1eb545 100644 --- a/src/stlplus/strings/string_inf.cpp +++ b/src/stlplus/strings/string_inf.cpp @@ -1,536 +1,536 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// String conversion functions for the infinite precision integer type inf - -//////////////////////////////////////////////////////////////////////////////// - -// can be excluded from the build to break the dependency on the portability library -#ifndef NO_STLPLUS_INF - -#include "string_inf.hpp" -#include "string_basic.hpp" -#include - -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - - 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 - }; - - //////////////////////////////////////////////////////////////////////////////// - - std::string inf_to_string(const stlplus::inf& data, unsigned radix, radix_display_t display, unsigned width) - throw(std::invalid_argument) - { - std::string result; - if (radix < 2 || radix > 36) - throw std::invalid_argument("invalid radix value"); - inf local_i = data; - // untangle all the options - bool hashed = false; - bool binary = false; - bool octal = false; - bool hex = false; - switch(display) - { - case radix_none: - break; - case radix_hash_style: - hashed = radix != 10; - break; - case radix_hash_style_all: - hashed = true; - break; - case radix_c_style: - if (radix == 16) - hex = true; - else if (radix == 8) - octal = true; - else if (radix == 2) - binary = true; - break; - case radix_c_style_or_hash: - if (radix == 16) - hex = true; - else if (radix == 8) - octal = true; - else if (radix == 2) - binary = true; - else if (radix != 10) - hashed = true; - break; - default: - throw std::invalid_argument("invalid radix display value"); - } - // create constants of the same type as the template parameter to avoid type mismatches - const inf t_zero(0); - const inf t_radix(radix); - // 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) - { - // the result is already binary - but the width may be wrong - // if this is still smaller than the width field, sign extend - // otherwise trim down to either the width or the smallest string that preserves the value - while (result.size() < width) - result.insert((std::string::size_type)0, 1, result[0]); - while (result.size() > width) - { - // 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 - but before converting get the width right - // the width is expressed in octal digits so make the binary 3 times this - // if this is still smaller than the width field, sign extend - // otherwise trim down to either the width or the smallest string that preserves the value - // also ensure that the binary is a multiple of 3 bits to make the conversion to octal easier - while (result.size() < 3*width) - result.insert((std::string::size_type)0, 1, result[0]); - while (result.size() > 3*width) - { - // 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); - } - 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 (result.size() < 4*width) - result.insert((std::string::size_type)0, 1, result[0]); - while (result.size() > 4*width) - { - // 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); - } - 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: - // [radix#][sign]magnitude - bool negative = local_i.negative(); - local_i.abs(); - // create a representation of the magnitude by successive division - do - { - std::pair divided = local_i.divide(t_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() || result.size() < width); - // add the prefixes - // add a sign only for negative values - if (negative) - result.insert((std::string::size_type)0, 1, '-'); - // then prefix everything with the radix if the hashed representation was requested - if (hashed) - result.insert((std::string::size_type)0, unsigned_to_string(radix) + "#"); - } - return result; - } - - //////////////////////////////////////////////////////////////////////////////// - // Conversions FROM string - - inf string_to_inf(const std::string& str, unsigned radix) throw(std::invalid_argument) - { - inf result; - if (radix != 0 && (radix < 2 || radix > 36)) - throw std::invalid_argument("invalid radix value " + unsigned_to_string(radix)); - unsigned i = 0; - // the radix passed as a parameter is just the default - it can be - // overridden by either the C prefix or the hash 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; - } - } - // now check for a hash-style prefix if a C-style prefix was not found - if (i == 0) - { - // scan for the sequence {digits}# - bool hash_found = false; - unsigned j = i; - for (; j < str.size(); j++) - { - if (!isdigit(str[j])) - { - if (str[j] == '#') - hash_found = true; - break; - } - } - if (hash_found) - { - // use the hash prefix to define the radix - // i points to the start of the radix and j points to the # character - std::string slice = str.substr(i, j-i); - radix = string_to_unsigned(slice); - i = j+1; - } - } - if (radix == 0) - radix = 10; - if (radix < 2 || radix > 36) - throw std::invalid_argument("invalid radix value"); - 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 - { - // 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); - int ch = from_char[(unsigned char)str[i]] ; - if (ch == -1) - throw std::invalid_argument("invalid character in string " + str + " for radix " + unsigned_to_string(radix)); - result += inf(ch); - } - if (negative) - result.negate(); - } - return result; - } - -//////////////////////////////////////////////////////////////////////////////// - -} // end namespace stlplus - -#endif +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// String conversion functions for the infinite precision integer type inf + +//////////////////////////////////////////////////////////////////////////////// + +// can be excluded from the build to break the dependency on the portability library +#ifndef NO_STLPLUS_INF + +#include "string_inf.hpp" +#include "string_basic.hpp" +#include + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + + 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 + }; + + //////////////////////////////////////////////////////////////////////////////// + + std::string inf_to_string(const stlplus::inf& data, unsigned radix, radix_display_t display, unsigned width) + throw(std::invalid_argument) + { + std::string result; + if (radix < 2 || radix > 36) + throw std::invalid_argument("invalid radix value"); + inf local_i = data; + // untangle all the options + bool hashed = false; + bool binary = false; + bool octal = false; + bool hex = false; + switch(display) + { + case radix_none: + break; + case radix_hash_style: + hashed = radix != 10; + break; + case radix_hash_style_all: + hashed = true; + break; + case radix_c_style: + if (radix == 16) + hex = true; + else if (radix == 8) + octal = true; + else if (radix == 2) + binary = true; + break; + case radix_c_style_or_hash: + if (radix == 16) + hex = true; + else if (radix == 8) + octal = true; + else if (radix == 2) + binary = true; + else if (radix != 10) + hashed = true; + break; + default: + throw std::invalid_argument("invalid radix display value"); + } + // create constants of the same type as the template parameter to avoid type mismatches + const inf t_zero(0); + const inf t_radix(radix); + // 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) + { + // the result is already binary - but the width may be wrong + // if this is still smaller than the width field, sign extend + // otherwise trim down to either the width or the smallest string that preserves the value + while (result.size() < width) + result.insert((std::string::size_type)0, 1, result[0]); + while (result.size() > width) + { + // 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 - but before converting get the width right + // the width is expressed in octal digits so make the binary 3 times this + // if this is still smaller than the width field, sign extend + // otherwise trim down to either the width or the smallest string that preserves the value + // also ensure that the binary is a multiple of 3 bits to make the conversion to octal easier + while (result.size() < 3*width) + result.insert((std::string::size_type)0, 1, result[0]); + while (result.size() > 3*width) + { + // 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); + } + 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 (result.size() < 4*width) + result.insert((std::string::size_type)0, 1, result[0]); + while (result.size() > 4*width) + { + // 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); + } + 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: + // [radix#][sign]magnitude + bool negative = local_i.negative(); + local_i.abs(); + // create a representation of the magnitude by successive division + do + { + std::pair divided = local_i.divide(t_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() || result.size() < width); + // add the prefixes + // add a sign only for negative values + if (negative) + result.insert((std::string::size_type)0, 1, '-'); + // then prefix everything with the radix if the hashed representation was requested + if (hashed) + result.insert((std::string::size_type)0, unsigned_to_string(radix) + "#"); + } + return result; + } + + //////////////////////////////////////////////////////////////////////////////// + // Conversions FROM string + + inf string_to_inf(const std::string& str, unsigned radix) throw(std::invalid_argument) + { + inf result; + if (radix != 0 && (radix < 2 || radix > 36)) + throw std::invalid_argument("invalid radix value " + unsigned_to_string(radix)); + unsigned i = 0; + // the radix passed as a parameter is just the default - it can be + // overridden by either the C prefix or the hash 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; + } + } + // now check for a hash-style prefix if a C-style prefix was not found + if (i == 0) + { + // scan for the sequence {digits}# + bool hash_found = false; + unsigned j = i; + for (; j < str.size(); j++) + { + if (!isdigit(str[j])) + { + if (str[j] == '#') + hash_found = true; + break; + } + } + if (hash_found) + { + // use the hash prefix to define the radix + // i points to the start of the radix and j points to the # character + std::string slice = str.substr(i, j-i); + radix = string_to_unsigned(slice); + i = j+1; + } + } + if (radix == 0) + radix = 10; + if (radix < 2 || radix > 36) + throw std::invalid_argument("invalid radix value"); + 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 + { + // 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); + int ch = from_char[(unsigned char)str[i]] ; + if (ch == -1) + throw std::invalid_argument("invalid character in string " + str + " for radix " + unsigned_to_string(radix)); + result += inf(ch); + } + if (negative) + result.negate(); + } + return result; + } + +//////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus + +#endif diff --git a/src/stlplus/strings/string_inf.hpp b/src/stlplus/strings/string_inf.hpp index 1543263..b651d72 100644 --- a/src/stlplus/strings/string_inf.hpp +++ b/src/stlplus/strings/string_inf.hpp @@ -1,40 +1,40 @@ -#ifndef STLPLUS_STRING_INF -#define STLPLUS_STRING_INF -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// String conversion functions for the infinite precision integer type inf - -// The conversion supports all the formatting modes defined on format_types - -//////////////////////////////////////////////////////////////////////////////// -#include "strings_fixes.hpp" -#include "inf.hpp" -#include "format_types.hpp" -#include -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - - // conversion TO string - std::string inf_to_string(const inf&, - unsigned radix = 10, - radix_display_t display = radix_c_style_or_hash, - unsigned width = 0) - throw(std::invalid_argument); - - // conversion FROM string - inf string_to_inf(const std::string&, - unsigned radix = 0) - throw(std::invalid_argument); - -//////////////////////////////////////////////////////////////////////////////// -} // end namespace stlplus -#endif +#ifndef STLPLUS_STRING_INF +#define STLPLUS_STRING_INF +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// String conversion functions for the infinite precision integer type inf + +// The conversion supports all the formatting modes defined on format_types + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include "inf.hpp" +#include "format_types.hpp" +#include +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + + // conversion TO string + std::string inf_to_string(const inf&, + unsigned radix = 10, + radix_display_t display = radix_c_style_or_hash, + unsigned width = 0) + throw(std::invalid_argument); + + // conversion FROM string + inf string_to_inf(const std::string&, + unsigned radix = 0) + throw(std::invalid_argument); + +//////////////////////////////////////////////////////////////////////////////// +} // end namespace stlplus +#endif diff --git a/src/stlplus/strings/string_int.cpp b/src/stlplus/strings/string_int.cpp index 178cf4c..ec5e10c 100644 --- a/src/stlplus/strings/string_int.cpp +++ b/src/stlplus/strings/string_int.cpp @@ -1,1111 +1,1111 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// -#include "string_int.hpp" -#include -#include - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - // character mappings - - 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 - }; - - //////////////////////////////////////////////////////////////////////////////// - // Conversions to string - // Local generic routines - - // signed version of the generic image generation function for all integer types - template - static std::string simage (T i, unsigned radix, radix_display_t display, unsigned width) - throw(std::invalid_argument) - { - if (radix < 2 || radix > 36) - throw std::invalid_argument("invalid radix value " + unsigned_to_string(radix)); - // untangle all the options - bool hashed = false; - bool binary = false; - bool octal = false; - bool hex = false; - switch(display) - { - case radix_none: - break; - case radix_hash_style: - hashed = radix != 10; - break; - case radix_hash_style_all: - hashed = true; - break; - case radix_c_style: - if (radix == 16) - hex = true; - else if (radix == 8) - octal = true; - else if (radix == 2) - binary = true; - break; - case radix_c_style_or_hash: - if (radix == 16) - hex = true; - else if (radix == 8) - octal = true; - else if (radix == 2) - binary = true; - else if (radix != 10) - hashed = true; - break; - default: - throw std::invalid_argument("invalid radix display value"); - } - // create constants of the same type as the template parameter to avoid type mismatches - const T t_zero(0); - const T t_radix(radix); - // the C representations for binary, octal and hex use 2's-complement representation - // all other represenations use sign-magnitude - std::string result; - 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 - // ensure that it has at least one bit! - for (T mask(1); ; mask <<= 1) - { - result.insert((std::string::size_type)0, 1, i & mask ? '1' : '0'); - if (mask == t_zero) break; - } - // 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) - { - // the result is already binary - but the width may be wrong - // if this is still smaller than the width field, sign extend - // otherwise trim down to either the width or the smallest string that preserves the value - while (result.size() < width) - result.insert((std::string::size_type)0, 1, result[0]); - while (result.size() > width) - { - // 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); - } - // add the prefix - result.insert((std::string::size_type)0, "0b"); - } - else if (octal) - { - // the result is currently binary - but before converting get the width right - // the width is expressed in octal digits so make the binary 3 times this - // if this is still smaller than the width field, sign extend - // otherwise trim down to either the width or the smallest string that preserves the value - // also ensure that the binary is a multiple of 3 bits to make the conversion to octal easier - while (result.size() < 3*width) - result.insert((std::string::size_type)0, 1, result[0]); - while (result.size() > 3*width) - { - // 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); - } - 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 - { - // hex - similar to octal - while (result.size() < 4*width) - result.insert((std::string::size_type)0, 1, result[0]); - while (result.size() > 4*width) - { - // 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); - } - 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: - // [radix#][sign]magnitude - bool negative = i < t_zero; - // create a representation of the magnitude by successive division - do - { - T ch = abs(i % t_radix); - i /= t_radix; - result.insert((std::string::size_type)0, 1, to_char[ch]); - } - while(i != t_zero || result.size() < width); - // add the prefixes - // add a sign only for negative values - if (negative) - result.insert((std::string::size_type)0, 1, '-'); - // then prefix everything with the radix if the hashed representation was requested - if (hashed) - result.insert((std::string::size_type)0, unsigned_to_string(radix) + "#"); - } - return result; - } - - // unsigned version - template - static std::string uimage (T i, unsigned radix, radix_display_t display, unsigned width) - throw(std::invalid_argument) - { - if (radix < 2 || radix > 36) - throw std::invalid_argument("invalid radix value " + unsigned_to_string(radix)); - // untangle all the options - bool hashed = false; - bool binary = false; - bool octal = false; - bool hex = false; - switch(display) - { - case radix_none: - break; - case radix_hash_style: - hashed = radix != 10; - break; - case radix_hash_style_all: - hashed = true; - break; - case radix_c_style: - if (radix == 16) - hex = true; - else if (radix == 8) - octal = true; - else if (radix == 2) - binary = true; - break; - case radix_c_style_or_hash: - if (radix == 16) - hex = true; - else if (radix == 8) - octal = true; - else if (radix == 2) - binary = true; - else if (radix != 10) - hashed = true; - break; - default: - throw std::invalid_argument("invalid radix display value"); - } - // create constants of the same type as the template parameter to avoid type mismatches - const T t_zero(0); - const T t_radix(radix); - // the C representations for binary, octal and hex use 2's-complement representation - // all other represenations use sign-magnitude - std::string result; - 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 - // ensure at least one bit - for (T mask(1); ; mask <<= 1) - { - result.insert((std::string::size_type)0, 1, i & mask ? '1' : '0'); - if (mask == t_zero) break; - } - // 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) - { - // the result is already binary - but the width may be wrong - // if this is still smaller than the width field, zero extend - // otherwise trim down to either the width or the smallest string that preserves the value - while (result.size() < width) - result.insert((std::string::size_type)0, 1, '0'); - while (result.size() > width) - { - // do not trim to less than 1 bit (1-bit magnitude) - if (result.size() <= 1) break; - // only trim if it doesn't change the sign and therefore the value - if (result[0] != '0') break; - result.erase(0,1); - } - // add the prefix - result.insert((std::string::size_type)0, "0b"); - } - else if (octal) - { - // the result is currently binary - but before converting get the width right - // the width is expressed in octal digits so make the binary 3 times this - // if this is still smaller than the width field, sign extend - // otherwise trim down to either the width or the smallest string that preserves the value - // also ensure that the binary is a multiple of 3 bits to make the conversion to octal easier - while (result.size() < 3*width) - result.insert((std::string::size_type)0, 1, '0'); - while (result.size() > 3*width) - { - // do not trim to less than 1 bit (1-bit magnitude) - if (result.size() <= 1) break; - // only trim if it doesn't change the sign and therefore the value - if (result[0] != '0') break; - result.erase(0,1); - } - while (result.size() % 3 != 0) - result.insert((std::string::size_type)0, 1, '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 if the leading digit is not already 0 - if (result.empty() || result[0] != '0') result.insert((std::string::size_type)0, "0"); - } - else - { - // similar to octal - while (result.size() < 4*width) - result.insert((std::string::size_type)0, 1, '0'); - while (result.size() > 4*width) - { - // do not trim to less than 1 bit (1-bit magnitude) - if (result.size() <= 1) break; - // only trim if it doesn't change the sign and therefore the value - if (result[0] != '0') break; - result.erase(0,1); - } - while (result.size() % 4 != 0) - result.insert((std::string::size_type)0, 1, '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: - // [radix#]magnitude - // create a representation of the magnitude by successive division - do - { - T ch = i % t_radix; - i /= t_radix; - result.insert((std::string::size_type)0, 1, to_char[(int)ch]); - } - while(i != t_zero || result.size() < width); - // prefix everything with the radix if the hashed representation was requested - if (hashed) - result.insert((std::string::size_type)0, unsigned_to_string(radix) + "#"); - } - return result; - } - - //////////////////////////////////////////////////////////////////////////////// - // exported conversions to string - - std::string short_to_string(short i, unsigned radix, radix_display_t display, unsigned width) - throw(std::invalid_argument) - { - return simage(i, radix, display, width); - } - - std::string unsigned_short_to_string(unsigned short i, unsigned radix, radix_display_t display, unsigned width) - throw(std::invalid_argument) - { - return uimage(i, radix, display, width); - } - - std::string int_to_string(int i, unsigned radix, radix_display_t display, unsigned width) - throw(std::invalid_argument) - { - return simage(i, radix, display, width); - } - - std::string unsigned_to_string(unsigned i, unsigned radix, radix_display_t display, unsigned width) - throw(std::invalid_argument) - { - return uimage(i, radix, display, width); - } - - std::string long_to_string(long i, unsigned radix, radix_display_t display, unsigned width) - throw(std::invalid_argument) - { - return simage(i, radix, display, width); - } - - std::string unsigned_long_to_string(unsigned long i, unsigned radix, radix_display_t display, unsigned width) - throw(std::invalid_argument) - { - return uimage(i, radix, display, width); - } - - //////////////////////////////////////////////////////////////////////////////// - // Conversions FROM string - // local template function - // Note: this has been copied and modified for the inf class - so any changes here must be made there too - - // signed version - template - static T svalue(const std::string& str, unsigned radix) - throw(std::invalid_argument) - { - if (radix != 0 && (radix < 2 || radix > 36)) - throw std::invalid_argument("invalid radix value " + unsigned_to_string(radix)); - std::string::size_type i = 0; - // the radix passed as a parameter is just the default - it can be - // overridden by either the C prefix or the hash prefix. Note: a leading zero - // is the C-style prefix for octal - I only make this override the default - // when the default prefix is not specified - // First check for a C-style prefix - bool c_style = false; - if (i < str.size() && str[i] == '0') - { - // octal, binary or hex - if (i+1 < str.size() && tolower(str[i+1]) == 'x') - { - radix = 16; - i += 2; - c_style = true; - } - else if (i+1 < str.size() && tolower(str[i+1]) == 'b') - { - radix = 2; - i += 2; - c_style = true; - } - else if (radix == 0) - { - radix = 8; - i += 1; - c_style = true; - } - } - // now check for a hash-style prefix if a C-style prefix was not found - if (i == 0) - { - // scan for the sequence {digits}# - bool hash_found = false; - std::string::size_type j = i; - for (; j < str.size(); j++) - { - if (!isdigit(str[j])) - { - if (str[j] == '#') - hash_found = true; - break; - } - } - if (hash_found) - { - // use the hash prefix to define the radix - // i points to the start of the radix and j points to the # character - std::string slice = str.substr(i, j-i); - radix = string_to_unsigned(slice); - i = j+1; - } - } - if (radix == 0) - radix = 10; - if (radix < 2 || radix > 36) - throw std::invalid_argument("invalid radix value " + unsigned_to_string(radix)); - T val(0); - 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 (std::string::size_type 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); - break; - } - } - } - else if (radix == 8) - { - for (std::string::size_type 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); - break; - } - } - } - else - { - for (std::string::size_type 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); - break; - } - } - } - // now sign-extend to the right number of bits for the type - while (binary.size() < sizeof(T)*8) - binary.insert((std::string::size_type)0, 1, binary.empty() ? '0' : binary[0]); - // now convert the value - for (std::string::size_type j = 0; j < binary.size(); j++) - { - val *= 2; - int ch = from_char[(unsigned char)binary[j]] ; - val += T(ch); - } - } - else - { - // 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++) - { - val *= T(radix); - int ch = from_char[(unsigned char)str[i]] ; - if (ch == -1 || (unsigned)ch >= radix) - throw std::invalid_argument("invalid character in string " + str); - val += T(ch); - } - if (negative) - val = -val; - } - return val; - } - - // unsigned version - template - static T uvalue(const std::string& str, unsigned radix) - throw(std::invalid_argument) - { - if (radix != 0 && (radix < 2 || radix > 36)) - throw std::invalid_argument("invalid radix value " + unsigned_to_string(radix)); - unsigned i = 0; - // the radix passed as a parameter is just the default - it can be - // overridden by either the C prefix or the hash prefix. Note: a leading - // zero is the C-style prefix for octal - I only make this override the - // default when the default prefix 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') - { - radix = 16; - i += 2; - c_style = true; - } - else if (i+1 < str.size() && tolower(str[i+1]) == 'b') - { - radix = 2; - i += 2; - c_style = true; - } - else if (radix == 0) - { - radix = 8; - i += 1; - c_style = true; - } - } - // now check for a hash-style prefix if a C-style prefix was not found - if (i == 0) - { - // scan for the sequence {digits}# - bool hash_found = false; - unsigned j = i; - for (; j < str.size(); j++) - { - if (!isdigit(str[j])) - { - if (str[j] == '#') - hash_found = true; - break; - } - } - if (hash_found) - { - // use the hash prefix to define the radix - // i points to the start of the radix and j points to the # character - std::string slice = str.substr(i, j-i); - radix = string_to_unsigned(slice); - i = j+1; - } - } - if (radix == 0) - radix = 10; - if (radix < 2 || radix > 36) - throw std::invalid_argument("invalid radix value " + unsigned_to_string(radix)); - T val(0); - 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 hex character in string " + str); - break; - } - } - } - 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); - break; - } - } - } - 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); - break; - } - } - } - // now zero-extend to the right number of bits for the type - while (binary.size() < sizeof(T)*8) - binary.insert((std::string::size_type)0, 1, '0'); - // now convert the value - for (unsigned j = 0; j < binary.size(); j++) - { - val *= 2; - int ch = from_char[(unsigned char)binary[j]] ; - val += T(ch); - } - } - else - { - // now scan for a sign and find whether this is a negative number - if (i < str.size()) - { - switch (str[i]) - { - case '-': - throw std::invalid_argument("invalid sign character in string " + str + " for unsigned value"); - i++; - break; - case '+': - i++; - break; - } - } - for (; i < str.size(); i++) - { - val *= T(radix); - int ch = from_char[(unsigned char)str[i]] ; - if (ch == -1 || (unsigned)ch >= radix) - { - throw std::invalid_argument("invalid character in string " + str); - } - val += T(ch); - } - } - return val; - } - - //////////////////////////////////////////////////////////////////////////////// - // exported functions - - short string_to_short(const std::string& str, unsigned radix) - throw(std::invalid_argument) - { - return svalue(str, radix); - } - - unsigned short string_to_unsigned_short(const std::string& str, unsigned radix) - throw(std::invalid_argument) - { - return uvalue(str, radix); - } - - int string_to_int(const std::string& str, unsigned radix) - throw(std::invalid_argument) - { - return svalue(str, radix); - } - - unsigned string_to_unsigned(const std::string& str, unsigned radix) - throw(std::invalid_argument) - { - return uvalue(str, radix); - } - - long string_to_long(const std::string& str, unsigned radix) - throw(std::invalid_argument) - { - return svalue(str, radix); - } - - unsigned long string_to_unsigned_long(const std::string& str, unsigned radix) - throw(std::invalid_argument) - { - return uvalue(str, radix); - } - - //////////////////////////////////////////////////////////////////////////////// - -} // end namespace stlplus +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "string_int.hpp" +#include +#include + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // character mappings + + 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 + }; + + //////////////////////////////////////////////////////////////////////////////// + // Conversions to string + // Local generic routines + + // signed version of the generic image generation function for all integer types + template + static std::string simage (T i, unsigned radix, radix_display_t display, unsigned width) + throw(std::invalid_argument) + { + if (radix < 2 || radix > 36) + throw std::invalid_argument("invalid radix value " + unsigned_to_string(radix)); + // untangle all the options + bool hashed = false; + bool binary = false; + bool octal = false; + bool hex = false; + switch(display) + { + case radix_none: + break; + case radix_hash_style: + hashed = radix != 10; + break; + case radix_hash_style_all: + hashed = true; + break; + case radix_c_style: + if (radix == 16) + hex = true; + else if (radix == 8) + octal = true; + else if (radix == 2) + binary = true; + break; + case radix_c_style_or_hash: + if (radix == 16) + hex = true; + else if (radix == 8) + octal = true; + else if (radix == 2) + binary = true; + else if (radix != 10) + hashed = true; + break; + default: + throw std::invalid_argument("invalid radix display value"); + } + // create constants of the same type as the template parameter to avoid type mismatches + const T t_zero(0); + const T t_radix(radix); + // the C representations for binary, octal and hex use 2's-complement representation + // all other represenations use sign-magnitude + std::string result; + 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 + // ensure that it has at least one bit! + for (T mask(1); ; mask <<= 1) + { + result.insert((std::string::size_type)0, 1, i & mask ? '1' : '0'); + if (mask == t_zero) break; + } + // 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) + { + // the result is already binary - but the width may be wrong + // if this is still smaller than the width field, sign extend + // otherwise trim down to either the width or the smallest string that preserves the value + while (result.size() < width) + result.insert((std::string::size_type)0, 1, result[0]); + while (result.size() > width) + { + // 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); + } + // add the prefix + result.insert((std::string::size_type)0, "0b"); + } + else if (octal) + { + // the result is currently binary - but before converting get the width right + // the width is expressed in octal digits so make the binary 3 times this + // if this is still smaller than the width field, sign extend + // otherwise trim down to either the width or the smallest string that preserves the value + // also ensure that the binary is a multiple of 3 bits to make the conversion to octal easier + while (result.size() < 3*width) + result.insert((std::string::size_type)0, 1, result[0]); + while (result.size() > 3*width) + { + // 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); + } + 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 + { + // hex - similar to octal + while (result.size() < 4*width) + result.insert((std::string::size_type)0, 1, result[0]); + while (result.size() > 4*width) + { + // 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); + } + 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: + // [radix#][sign]magnitude + bool negative = i < t_zero; + // create a representation of the magnitude by successive division + do + { + T ch = abs(i % t_radix); + i /= t_radix; + result.insert((std::string::size_type)0, 1, to_char[ch]); + } + while(i != t_zero || result.size() < width); + // add the prefixes + // add a sign only for negative values + if (negative) + result.insert((std::string::size_type)0, 1, '-'); + // then prefix everything with the radix if the hashed representation was requested + if (hashed) + result.insert((std::string::size_type)0, unsigned_to_string(radix) + "#"); + } + return result; + } + + // unsigned version + template + static std::string uimage (T i, unsigned radix, radix_display_t display, unsigned width) + throw(std::invalid_argument) + { + if (radix < 2 || radix > 36) + throw std::invalid_argument("invalid radix value " + unsigned_to_string(radix)); + // untangle all the options + bool hashed = false; + bool binary = false; + bool octal = false; + bool hex = false; + switch(display) + { + case radix_none: + break; + case radix_hash_style: + hashed = radix != 10; + break; + case radix_hash_style_all: + hashed = true; + break; + case radix_c_style: + if (radix == 16) + hex = true; + else if (radix == 8) + octal = true; + else if (radix == 2) + binary = true; + break; + case radix_c_style_or_hash: + if (radix == 16) + hex = true; + else if (radix == 8) + octal = true; + else if (radix == 2) + binary = true; + else if (radix != 10) + hashed = true; + break; + default: + throw std::invalid_argument("invalid radix display value"); + } + // create constants of the same type as the template parameter to avoid type mismatches + const T t_zero(0); + const T t_radix(radix); + // the C representations for binary, octal and hex use 2's-complement representation + // all other represenations use sign-magnitude + std::string result; + 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 + // ensure at least one bit + for (T mask(1); ; mask <<= 1) + { + result.insert((std::string::size_type)0, 1, i & mask ? '1' : '0'); + if (mask == t_zero) break; + } + // 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) + { + // the result is already binary - but the width may be wrong + // if this is still smaller than the width field, zero extend + // otherwise trim down to either the width or the smallest string that preserves the value + while (result.size() < width) + result.insert((std::string::size_type)0, 1, '0'); + while (result.size() > width) + { + // do not trim to less than 1 bit (1-bit magnitude) + if (result.size() <= 1) break; + // only trim if it doesn't change the sign and therefore the value + if (result[0] != '0') break; + result.erase(0,1); + } + // add the prefix + result.insert((std::string::size_type)0, "0b"); + } + else if (octal) + { + // the result is currently binary - but before converting get the width right + // the width is expressed in octal digits so make the binary 3 times this + // if this is still smaller than the width field, sign extend + // otherwise trim down to either the width or the smallest string that preserves the value + // also ensure that the binary is a multiple of 3 bits to make the conversion to octal easier + while (result.size() < 3*width) + result.insert((std::string::size_type)0, 1, '0'); + while (result.size() > 3*width) + { + // do not trim to less than 1 bit (1-bit magnitude) + if (result.size() <= 1) break; + // only trim if it doesn't change the sign and therefore the value + if (result[0] != '0') break; + result.erase(0,1); + } + while (result.size() % 3 != 0) + result.insert((std::string::size_type)0, 1, '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 if the leading digit is not already 0 + if (result.empty() || result[0] != '0') result.insert((std::string::size_type)0, "0"); + } + else + { + // similar to octal + while (result.size() < 4*width) + result.insert((std::string::size_type)0, 1, '0'); + while (result.size() > 4*width) + { + // do not trim to less than 1 bit (1-bit magnitude) + if (result.size() <= 1) break; + // only trim if it doesn't change the sign and therefore the value + if (result[0] != '0') break; + result.erase(0,1); + } + while (result.size() % 4 != 0) + result.insert((std::string::size_type)0, 1, '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: + // [radix#]magnitude + // create a representation of the magnitude by successive division + do + { + T ch = i % t_radix; + i /= t_radix; + result.insert((std::string::size_type)0, 1, to_char[(int)ch]); + } + while(i != t_zero || result.size() < width); + // prefix everything with the radix if the hashed representation was requested + if (hashed) + result.insert((std::string::size_type)0, unsigned_to_string(radix) + "#"); + } + return result; + } + + //////////////////////////////////////////////////////////////////////////////// + // exported conversions to string + + std::string short_to_string(short i, unsigned radix, radix_display_t display, unsigned width) + throw(std::invalid_argument) + { + return simage(i, radix, display, width); + } + + std::string unsigned_short_to_string(unsigned short i, unsigned radix, radix_display_t display, unsigned width) + throw(std::invalid_argument) + { + return uimage(i, radix, display, width); + } + + std::string int_to_string(int i, unsigned radix, radix_display_t display, unsigned width) + throw(std::invalid_argument) + { + return simage(i, radix, display, width); + } + + std::string unsigned_to_string(unsigned i, unsigned radix, radix_display_t display, unsigned width) + throw(std::invalid_argument) + { + return uimage(i, radix, display, width); + } + + std::string long_to_string(long i, unsigned radix, radix_display_t display, unsigned width) + throw(std::invalid_argument) + { + return simage(i, radix, display, width); + } + + std::string unsigned_long_to_string(unsigned long i, unsigned radix, radix_display_t display, unsigned width) + throw(std::invalid_argument) + { + return uimage(i, radix, display, width); + } + + //////////////////////////////////////////////////////////////////////////////// + // Conversions FROM string + // local template function + // Note: this has been copied and modified for the inf class - so any changes here must be made there too + + // signed version + template + static T svalue(const std::string& str, unsigned radix) + throw(std::invalid_argument) + { + if (radix != 0 && (radix < 2 || radix > 36)) + throw std::invalid_argument("invalid radix value " + unsigned_to_string(radix)); + std::string::size_type i = 0; + // the radix passed as a parameter is just the default - it can be + // overridden by either the C prefix or the hash prefix. Note: a leading zero + // is the C-style prefix for octal - I only make this override the default + // when the default prefix is not specified + // First check for a C-style prefix + bool c_style = false; + if (i < str.size() && str[i] == '0') + { + // octal, binary or hex + if (i+1 < str.size() && tolower(str[i+1]) == 'x') + { + radix = 16; + i += 2; + c_style = true; + } + else if (i+1 < str.size() && tolower(str[i+1]) == 'b') + { + radix = 2; + i += 2; + c_style = true; + } + else if (radix == 0) + { + radix = 8; + i += 1; + c_style = true; + } + } + // now check for a hash-style prefix if a C-style prefix was not found + if (i == 0) + { + // scan for the sequence {digits}# + bool hash_found = false; + std::string::size_type j = i; + for (; j < str.size(); j++) + { + if (!isdigit(str[j])) + { + if (str[j] == '#') + hash_found = true; + break; + } + } + if (hash_found) + { + // use the hash prefix to define the radix + // i points to the start of the radix and j points to the # character + std::string slice = str.substr(i, j-i); + radix = string_to_unsigned(slice); + i = j+1; + } + } + if (radix == 0) + radix = 10; + if (radix < 2 || radix > 36) + throw std::invalid_argument("invalid radix value " + unsigned_to_string(radix)); + T val(0); + 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 (std::string::size_type 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); + break; + } + } + } + else if (radix == 8) + { + for (std::string::size_type 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); + break; + } + } + } + else + { + for (std::string::size_type 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); + break; + } + } + } + // now sign-extend to the right number of bits for the type + while (binary.size() < sizeof(T)*8) + binary.insert((std::string::size_type)0, 1, binary.empty() ? '0' : binary[0]); + // now convert the value + for (std::string::size_type j = 0; j < binary.size(); j++) + { + val *= 2; + int ch = from_char[(unsigned char)binary[j]] ; + val += T(ch); + } + } + else + { + // 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++) + { + val *= T(radix); + int ch = from_char[(unsigned char)str[i]] ; + if (ch == -1 || (unsigned)ch >= radix) + throw std::invalid_argument("invalid character in string " + str); + val += T(ch); + } + if (negative) + val = -val; + } + return val; + } + + // unsigned version + template + static T uvalue(const std::string& str, unsigned radix) + throw(std::invalid_argument) + { + if (radix != 0 && (radix < 2 || radix > 36)) + throw std::invalid_argument("invalid radix value " + unsigned_to_string(radix)); + unsigned i = 0; + // the radix passed as a parameter is just the default - it can be + // overridden by either the C prefix or the hash prefix. Note: a leading + // zero is the C-style prefix for octal - I only make this override the + // default when the default prefix 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') + { + radix = 16; + i += 2; + c_style = true; + } + else if (i+1 < str.size() && tolower(str[i+1]) == 'b') + { + radix = 2; + i += 2; + c_style = true; + } + else if (radix == 0) + { + radix = 8; + i += 1; + c_style = true; + } + } + // now check for a hash-style prefix if a C-style prefix was not found + if (i == 0) + { + // scan for the sequence {digits}# + bool hash_found = false; + unsigned j = i; + for (; j < str.size(); j++) + { + if (!isdigit(str[j])) + { + if (str[j] == '#') + hash_found = true; + break; + } + } + if (hash_found) + { + // use the hash prefix to define the radix + // i points to the start of the radix and j points to the # character + std::string slice = str.substr(i, j-i); + radix = string_to_unsigned(slice); + i = j+1; + } + } + if (radix == 0) + radix = 10; + if (radix < 2 || radix > 36) + throw std::invalid_argument("invalid radix value " + unsigned_to_string(radix)); + T val(0); + 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 hex character in string " + str); + break; + } + } + } + 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); + break; + } + } + } + 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); + break; + } + } + } + // now zero-extend to the right number of bits for the type + while (binary.size() < sizeof(T)*8) + binary.insert((std::string::size_type)0, 1, '0'); + // now convert the value + for (unsigned j = 0; j < binary.size(); j++) + { + val *= 2; + int ch = from_char[(unsigned char)binary[j]] ; + val += T(ch); + } + } + else + { + // now scan for a sign and find whether this is a negative number + if (i < str.size()) + { + switch (str[i]) + { + case '-': + throw std::invalid_argument("invalid sign character in string " + str + " for unsigned value"); + i++; + break; + case '+': + i++; + break; + } + } + for (; i < str.size(); i++) + { + val *= T(radix); + int ch = from_char[(unsigned char)str[i]] ; + if (ch == -1 || (unsigned)ch >= radix) + { + throw std::invalid_argument("invalid character in string " + str); + } + val += T(ch); + } + } + return val; + } + + //////////////////////////////////////////////////////////////////////////////// + // exported functions + + short string_to_short(const std::string& str, unsigned radix) + throw(std::invalid_argument) + { + return svalue(str, radix); + } + + unsigned short string_to_unsigned_short(const std::string& str, unsigned radix) + throw(std::invalid_argument) + { + return uvalue(str, radix); + } + + int string_to_int(const std::string& str, unsigned radix) + throw(std::invalid_argument) + { + return svalue(str, radix); + } + + unsigned string_to_unsigned(const std::string& str, unsigned radix) + throw(std::invalid_argument) + { + return uvalue(str, radix); + } + + long string_to_long(const std::string& str, unsigned radix) + throw(std::invalid_argument) + { + return svalue(str, radix); + } + + unsigned long string_to_unsigned_long(const std::string& str, unsigned radix) + throw(std::invalid_argument) + { + return uvalue(str, radix); + } + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus diff --git a/src/stlplus/strings/string_int.hpp b/src/stlplus/strings/string_int.hpp index a45b982..af79098 100644 --- a/src/stlplus/strings/string_int.hpp +++ b/src/stlplus/strings/string_int.hpp @@ -1,118 +1,118 @@ -#ifndef STLPLUS_STRING_INT -#define STLPLUS_STRING_INT -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Convert integer types to/from string - -//////////////////////////////////////////////////////////////////////////////// -#include "strings_fixes.hpp" -#include "format_types.hpp" -#include -#include - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - // Conversions of Integer types to string - //////////////////////////////////////////////////////////////////////////////// - - // The radix (i.e. base) for these conversions can be any value from base 2 to base 36 - // specifying any other radix causes std::invalid_argument to be thrown - - // The way in which the radix is displayed is defined in radix_types.hpp - // If any other value is used, std::invalid_argument is thrown - - // The width argument specifies the number of numerical digits to use in the result - // This is a minimum - if the value requires more digits then it will be wider than the width argument - // However, if it is smaller, then it will be extended to the specified width - // Then, the radix display prefix is added to this width - - // For example, using the hash representation of 0 in hex with width=4 gives: - // 16#0000 - so there's 4 digits in the number part - - std::string short_to_string(short i, - unsigned radix = 10, - radix_display_t display = radix_c_style_or_hash, - unsigned width = 0) - throw(std::invalid_argument); - - std::string unsigned_short_to_string(unsigned short i, - unsigned radix = 10, - radix_display_t display = radix_c_style_or_hash, - unsigned width = 0) - throw(std::invalid_argument); - - std::string int_to_string(int i, - unsigned radix = 10, - radix_display_t display = radix_c_style_or_hash, - unsigned width = 0) - throw(std::invalid_argument); - - std::string unsigned_to_string(unsigned i, - unsigned radix = 10, - radix_display_t display = radix_c_style_or_hash, - unsigned width = 0) - throw(std::invalid_argument); - - std::string long_to_string(long i, - unsigned radix = 10, - radix_display_t display = radix_c_style_or_hash, - unsigned width = 0) - throw(std::invalid_argument); - - std::string unsigned_long_to_string(unsigned long i, - unsigned radix = 10, - radix_display_t display = radix_c_style_or_hash, - unsigned width = 0) - throw(std::invalid_argument); - - //////////////////////////////////////////////////////////////////////////////// - // Convert a string to an integer type - //////////////////////////////////////////////////////////////////////////////// - // supports all the formats described above for the reverse conversion - // If the radix is set to zero, the conversions deduce the radix from the string representation - // So, - // 0b prefix is binary, - // 0 prefix is octal, - // 0x is hex - // # prefix is my hash format - // The radix must be either zero as explained above, or in the range 2 to 16 - // A non-zero radix should be used when the string value has no radix information and is non-decimal - // e.g. the hex value FEDCBA has no indication that it is hex, so specify radix 16 - // Any other value of radix will cause std::invalid_argument to be thrown - - short string_to_short(const std::string& value, - unsigned radix = 0) - throw(std::invalid_argument); - - unsigned short string_to_unsigned_short(const std::string& value, - unsigned radix = 0) - throw(std::invalid_argument); - - int string_to_int(const std::string& value, - unsigned radix = 0) - throw(std::invalid_argument); - - unsigned string_to_unsigned(const std::string& value, - unsigned radix = 0) - throw(std::invalid_argument); - - long string_to_long(const std::string& value, - unsigned radix = 0) - throw(std::invalid_argument); - - unsigned long string_to_unsigned_long(const std::string& value, - unsigned radix = 0) - throw(std::invalid_argument); - - //////////////////////////////////////////////////////////////////////////////// - -} // end namespace stlplus - -#endif +#ifndef STLPLUS_STRING_INT +#define STLPLUS_STRING_INT +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Convert integer types to/from string + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include "format_types.hpp" +#include +#include + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // Conversions of Integer types to string + //////////////////////////////////////////////////////////////////////////////// + + // The radix (i.e. base) for these conversions can be any value from base 2 to base 36 + // specifying any other radix causes std::invalid_argument to be thrown + + // The way in which the radix is displayed is defined in radix_types.hpp + // If any other value is used, std::invalid_argument is thrown + + // The width argument specifies the number of numerical digits to use in the result + // This is a minimum - if the value requires more digits then it will be wider than the width argument + // However, if it is smaller, then it will be extended to the specified width + // Then, the radix display prefix is added to this width + + // For example, using the hash representation of 0 in hex with width=4 gives: + // 16#0000 - so there's 4 digits in the number part + + std::string short_to_string(short i, + unsigned radix = 10, + radix_display_t display = radix_c_style_or_hash, + unsigned width = 0) + throw(std::invalid_argument); + + std::string unsigned_short_to_string(unsigned short i, + unsigned radix = 10, + radix_display_t display = radix_c_style_or_hash, + unsigned width = 0) + throw(std::invalid_argument); + + std::string int_to_string(int i, + unsigned radix = 10, + radix_display_t display = radix_c_style_or_hash, + unsigned width = 0) + throw(std::invalid_argument); + + std::string unsigned_to_string(unsigned i, + unsigned radix = 10, + radix_display_t display = radix_c_style_or_hash, + unsigned width = 0) + throw(std::invalid_argument); + + std::string long_to_string(long i, + unsigned radix = 10, + radix_display_t display = radix_c_style_or_hash, + unsigned width = 0) + throw(std::invalid_argument); + + std::string unsigned_long_to_string(unsigned long i, + unsigned radix = 10, + radix_display_t display = radix_c_style_or_hash, + unsigned width = 0) + throw(std::invalid_argument); + + //////////////////////////////////////////////////////////////////////////////// + // Convert a string to an integer type + //////////////////////////////////////////////////////////////////////////////// + // supports all the formats described above for the reverse conversion + // If the radix is set to zero, the conversions deduce the radix from the string representation + // So, + // 0b prefix is binary, + // 0 prefix is octal, + // 0x is hex + // # prefix is my hash format + // The radix must be either zero as explained above, or in the range 2 to 16 + // A non-zero radix should be used when the string value has no radix information and is non-decimal + // e.g. the hex value FEDCBA has no indication that it is hex, so specify radix 16 + // Any other value of radix will cause std::invalid_argument to be thrown + + short string_to_short(const std::string& value, + unsigned radix = 0) + throw(std::invalid_argument); + + unsigned short string_to_unsigned_short(const std::string& value, + unsigned radix = 0) + throw(std::invalid_argument); + + int string_to_int(const std::string& value, + unsigned radix = 0) + throw(std::invalid_argument); + + unsigned string_to_unsigned(const std::string& value, + unsigned radix = 0) + throw(std::invalid_argument); + + long string_to_long(const std::string& value, + unsigned radix = 0) + throw(std::invalid_argument); + + unsigned long string_to_unsigned_long(const std::string& value, + unsigned radix = 0) + throw(std::invalid_argument); + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus + +#endif diff --git a/src/stlplus/strings/string_list.hpp b/src/stlplus/strings/string_list.hpp index 6728690..beaef76 100644 --- a/src/stlplus/strings/string_list.hpp +++ b/src/stlplus/strings/string_list.hpp @@ -1,28 +1,28 @@ -#ifndef STLPLUS_STRING_LIST -#define STLPLUS_STRING_LIST -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Generate a string representation of a list - -//////////////////////////////////////////////////////////////////////////////// -#include "strings_fixes.hpp" -#include -#include - -namespace stlplus -{ - - template - std::string list_to_string(const std::list& values, - S to_string_fn, - const std::string& separator = ","); - -} // end namespace stlplus - -#include "string_list.tpp" -#endif +#ifndef STLPLUS_STRING_LIST +#define STLPLUS_STRING_LIST +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Generate a string representation of a list + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include +#include + +namespace stlplus +{ + + template + std::string list_to_string(const std::list& values, + S to_string_fn, + const std::string& separator = ","); + +} // end namespace stlplus + +#include "string_list.tpp" +#endif diff --git a/src/stlplus/strings/string_list.tpp b/src/stlplus/strings/string_list.tpp index 061f427..eebba42 100644 --- a/src/stlplus/strings/string_list.tpp +++ b/src/stlplus/strings/string_list.tpp @@ -1,24 +1,24 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// template implementations - -//////////////////////////////////////////////////////////////////////////////// -#include "string_sequence.hpp" - -namespace stlplus -{ - - template - std::string list_to_string(const std::list& values, - S to_string_fn, - const std::string& separator) - { - return sequence_to_string(values.begin(), values.end(), to_string_fn, separator); - } - -} // end namespace stlplus +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// template implementations + +//////////////////////////////////////////////////////////////////////////////// +#include "string_sequence.hpp" + +namespace stlplus +{ + + template + std::string list_to_string(const std::list& values, + S to_string_fn, + const std::string& separator) + { + return sequence_to_string(values.begin(), values.end(), to_string_fn, separator); + } + +} // end namespace stlplus diff --git a/src/stlplus/strings/string_map.hpp b/src/stlplus/strings/string_map.hpp index 8bf7e8b..64a6b22 100644 --- a/src/stlplus/strings/string_map.hpp +++ b/src/stlplus/strings/string_map.hpp @@ -1,37 +1,37 @@ -#ifndef STLPLUS_STRING_MAP -#define STLPLUS_STRING_MAP -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Generate a string representation of a map/multimap - -//////////////////////////////////////////////////////////////////////////////// -#include "strings_fixes.hpp" -#include -#include - -namespace stlplus -{ - - template - std::string map_to_string(const std::map& values, - SK key_to_string_fn, - ST value_to_string_fn, - const std::string& pair_separator = ":", - const std::string& separator = ","); - - template - std::string multimap_to_string(const std::multimap& values, - SK key_to_string_fn, - ST value_to_string_fn, - const std::string& pair_separator = ":", - const std::string& separator = ","); - -} // end namespace stlplus - -#include "string_map.tpp" -#endif +#ifndef STLPLUS_STRING_MAP +#define STLPLUS_STRING_MAP +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Generate a string representation of a map/multimap + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include +#include + +namespace stlplus +{ + + template + std::string map_to_string(const std::map& values, + SK key_to_string_fn, + ST value_to_string_fn, + const std::string& pair_separator = ":", + const std::string& separator = ","); + + template + std::string multimap_to_string(const std::multimap& values, + SK key_to_string_fn, + ST value_to_string_fn, + const std::string& pair_separator = ":", + const std::string& separator = ","); + +} // end namespace stlplus + +#include "string_map.tpp" +#endif diff --git a/src/stlplus/strings/string_map.tpp b/src/stlplus/strings/string_map.tpp index 49cb51f..ce7d2f0 100644 --- a/src/stlplus/strings/string_map.tpp +++ b/src/stlplus/strings/string_map.tpp @@ -1,45 +1,45 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// -#include "string_sequence.hpp" - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - // map - - template - std::string map_to_string(const std::map& values, - SK key_to_string_fn, - ST value_to_string_fn, - const std::string& pair_separator, - const std::string& separator) - { - return pair_sequence_to_string(values.begin(), values.end(), - key_to_string_fn, value_to_string_fn, - pair_separator, separator); - } - - //////////////////////////////////////////////////////////////////////////////// - // multimap - - template - std::string multimap_to_string(const std::multimap& values, - SK key_to_string_fn, - ST value_to_string_fn, - const std::string& pair_separator, - const std::string& separator) - { - return pair_sequence_to_string(values.begin(), values.end(), - key_to_string_fn, value_to_string_fn, - pair_separator, separator); - } - - //////////////////////////////////////////////////////////////////////////////// -} // end namespace stlplus +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "string_sequence.hpp" + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // map + + template + std::string map_to_string(const std::map& values, + SK key_to_string_fn, + ST value_to_string_fn, + const std::string& pair_separator, + const std::string& separator) + { + return pair_sequence_to_string(values.begin(), values.end(), + key_to_string_fn, value_to_string_fn, + pair_separator, separator); + } + + //////////////////////////////////////////////////////////////////////////////// + // multimap + + template + std::string multimap_to_string(const std::multimap& values, + SK key_to_string_fn, + ST value_to_string_fn, + const std::string& pair_separator, + const std::string& separator) + { + return pair_sequence_to_string(values.begin(), values.end(), + key_to_string_fn, value_to_string_fn, + pair_separator, separator); + } + + //////////////////////////////////////////////////////////////////////////////// +} // end namespace stlplus diff --git a/src/stlplus/strings/string_matrix.hpp b/src/stlplus/strings/string_matrix.hpp index dd1fa2b..1ab0e82 100644 --- a/src/stlplus/strings/string_matrix.hpp +++ b/src/stlplus/strings/string_matrix.hpp @@ -1,31 +1,31 @@ -#ifndef STLPLUS_STRING_MATRIX -#define STLPLUS_STRING_MATRIX -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Generate a string representation of a matrix - -//////////////////////////////////////////////////////////////////////////////// -#include "strings_fixes.hpp" -#include "matrix.hpp" -#include - -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - - template - std::string matrix_to_string(const matrix& values, - S to_string_fn, - const std::string& column_separator = "|", - const std::string& row_separator = ","); - -} // end namespace stlplus - -#include "string_matrix.tpp" -#endif +#ifndef STLPLUS_STRING_MATRIX +#define STLPLUS_STRING_MATRIX +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Generate a string representation of a matrix + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include "matrix.hpp" +#include + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + template + std::string matrix_to_string(const matrix& values, + S to_string_fn, + const std::string& column_separator = "|", + const std::string& row_separator = ","); + +} // end namespace stlplus + +#include "string_matrix.tpp" +#endif diff --git a/src/stlplus/strings/string_matrix.tpp b/src/stlplus/strings/string_matrix.tpp index 345d15c..4185999 100644 --- a/src/stlplus/strings/string_matrix.tpp +++ b/src/stlplus/strings/string_matrix.tpp @@ -1,35 +1,35 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - - template - std::string matrix_to_string(const matrix& values, - S to_string_fn, - const std::string& column_separator, - const std::string& row_separator) - { - std::string result; - for (unsigned r = 0; r < values.rows(); r++) - { - if (r != 0) result += row_separator; - for (unsigned c = 0; c < values.columns(); c++) - { - if (c != 0) result += column_separator; - result += to_string_fn(values(r,c)); - } - } - return result; - } - -} // end namespace stlplus - +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + + template + std::string matrix_to_string(const matrix& values, + S to_string_fn, + const std::string& column_separator, + const std::string& row_separator) + { + std::string result; + for (unsigned r = 0; r < values.rows(); r++) + { + if (r != 0) result += row_separator; + for (unsigned c = 0; c < values.columns(); c++) + { + if (c != 0) result += column_separator; + result += to_string_fn(values(r,c)); + } + } + return result; + } + +} // end namespace stlplus + diff --git a/src/stlplus/strings/string_ntree.hpp b/src/stlplus/strings/string_ntree.hpp index 177b53d..e7b092d 100644 --- a/src/stlplus/strings/string_ntree.hpp +++ b/src/stlplus/strings/string_ntree.hpp @@ -1,31 +1,31 @@ -#ifndef STLPLUS_STRING_NTREE -#define STLPLUS_STRING_NTREE -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Generate a string representation of an ntree - -//////////////////////////////////////////////////////////////////////////////// -#include "strings_fixes.hpp" -#include "ntree.hpp" -#include - -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - - template - std::string ntree_to_string(const ntree& values, - S to_string_fn, - const std::string& separator = "|", - const std::string& indent_string = " "); - -} // end namespace stlplus - -#include "string_ntree.tpp" -#endif +#ifndef STLPLUS_STRING_NTREE +#define STLPLUS_STRING_NTREE +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Generate a string representation of an ntree + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include "ntree.hpp" +#include + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + template + std::string ntree_to_string(const ntree& values, + S to_string_fn, + const std::string& separator = "|", + const std::string& indent_string = " "); + +} // end namespace stlplus + +#include "string_ntree.tpp" +#endif diff --git a/src/stlplus/strings/string_ntree.tpp b/src/stlplus/strings/string_ntree.tpp index 040dad3..1c62a4c 100644 --- a/src/stlplus/strings/string_ntree.tpp +++ b/src/stlplus/strings/string_ntree.tpp @@ -1,31 +1,31 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - - template - std::string ntree_to_string(const ntree& values, - S to_string_fn, - const std::string& separator, - const std::string& indent_string) - { - std::string result; - for (TYPENAME ntree::const_prefix_iterator i = values.prefix_begin(); i != values.prefix_end(); i++) - { - if (i != values.prefix_begin()) result += separator; - for (unsigned indent = values.depth(i.simplify()); --indent; ) - result += indent_string; - result += to_string_fn(*i); - } - return result; - } - -} // end namespace stlplus - +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + template + std::string ntree_to_string(const ntree& values, + S to_string_fn, + const std::string& separator, + const std::string& indent_string) + { + std::string result; + for (TYPENAME ntree::const_prefix_iterator i = values.prefix_begin(); i != values.prefix_end(); i++) + { + if (i != values.prefix_begin()) result += separator; + for (unsigned indent = values.depth(i.simplify()); --indent; ) + result += indent_string; + result += to_string_fn(*i); + } + return result; + } + +} // end namespace stlplus + diff --git a/src/stlplus/strings/string_pair.hpp b/src/stlplus/strings/string_pair.hpp index 483a0b1..c55f51c 100644 --- a/src/stlplus/strings/string_pair.hpp +++ b/src/stlplus/strings/string_pair.hpp @@ -1,29 +1,29 @@ -#ifndef STLPLUS_STRING_PAIR -#define STLPLUS_STRING_PAIR -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Generate a string representation of a pair - -//////////////////////////////////////////////////////////////////////////////// -#include "strings_fixes.hpp" -#include -#include - -namespace stlplus -{ - - template - std::string pair_to_string(const std::pair& values, - S1 to_string_fn1, - S2 to_string_fn2, - const std::string& separator = ":"); - -} // end namespace stlplus - -#include "string_pair.tpp" -#endif +#ifndef STLPLUS_STRING_PAIR +#define STLPLUS_STRING_PAIR +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Generate a string representation of a pair + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include +#include + +namespace stlplus +{ + + template + std::string pair_to_string(const std::pair& values, + S1 to_string_fn1, + S2 to_string_fn2, + const std::string& separator = ":"); + +} // end namespace stlplus + +#include "string_pair.tpp" +#endif diff --git a/src/stlplus/strings/string_pair.tpp b/src/stlplus/strings/string_pair.tpp index f458836..cdeda5f 100644 --- a/src/stlplus/strings/string_pair.tpp +++ b/src/stlplus/strings/string_pair.tpp @@ -1,22 +1,22 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - - template - std::string pair_to_string(const std::pair& values, - S1 to_string_fn1, - S2 to_string_fn2, - const std::string& separator) - { - return to_string_fn1(values.first) + separator + to_string_fn2(values.second); - } - -} // end namespace stlplus +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + template + std::string pair_to_string(const std::pair& values, + S1 to_string_fn1, + S2 to_string_fn2, + const std::string& separator) + { + return to_string_fn1(values.first) + separator + to_string_fn2(values.second); + } + +} // end namespace stlplus diff --git a/src/stlplus/strings/string_pointer.hpp b/src/stlplus/strings/string_pointer.hpp index 2170000..abdc825 100644 --- a/src/stlplus/strings/string_pointer.hpp +++ b/src/stlplus/strings/string_pointer.hpp @@ -1,30 +1,30 @@ -#ifndef STLPLUS_STRING_POINTER -#define STLPLUS_STRING_POINTER -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Generate a string representation of an object pointed to - -//////////////////////////////////////////////////////////////////////////////// -#include "strings_fixes.hpp" -#include - -namespace stlplus -{ - - template - std::string pointer_to_string(const T* value, - S to_string_fn, - const std::string& null_string = "", - const std::string& prefix = "(", - const std::string& suffix = ")"); - - -} - -#include "string_pointer.tpp" -#endif +#ifndef STLPLUS_STRING_POINTER +#define STLPLUS_STRING_POINTER +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Generate a string representation of an object pointed to + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include + +namespace stlplus +{ + + template + std::string pointer_to_string(const T* value, + S to_string_fn, + const std::string& null_string = "", + const std::string& prefix = "(", + const std::string& suffix = ")"); + + +} + +#include "string_pointer.tpp" +#endif diff --git a/src/stlplus/strings/string_pointer.tpp b/src/stlplus/strings/string_pointer.tpp index d77a902..cd3e5b2 100644 --- a/src/stlplus/strings/string_pointer.tpp +++ b/src/stlplus/strings/string_pointer.tpp @@ -1,24 +1,24 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// -#include - -namespace stlplus -{ - - template - std::string pointer_to_string(const T* value, - S to_string_fn, - const std::string& null_string, - const std::string& prefix, - const std::string& suffix) - { - return value ? (prefix + to_string_fn(*value) + suffix) : null_string; - } - -} // end namespace stlplus +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include + +namespace stlplus +{ + + template + std::string pointer_to_string(const T* value, + S to_string_fn, + const std::string& null_string, + const std::string& prefix, + const std::string& suffix) + { + return value ? (prefix + to_string_fn(*value) + suffix) : null_string; + } + +} // end namespace stlplus diff --git a/src/stlplus/strings/string_sequence.hpp b/src/stlplus/strings/string_sequence.hpp index f029031..591692e 100644 --- a/src/stlplus/strings/string_sequence.hpp +++ b/src/stlplus/strings/string_sequence.hpp @@ -1,45 +1,45 @@ -#ifndef STLPLUS_STRING_SEQUENCE -#define STLPLUS_STRING_SEQUENCE -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Generate string representations of sequences represented by forward iterators - -//////////////////////////////////////////////////////////////////////////////// -#include "strings_fixes.hpp" -#include - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - // any forward iterator sequence - - template - std::string sequence_to_string(I begin, - I end, - S to_string, - const std::string& separator); - - - //////////////////////////////////////////////////////////////////////////////// - // any forward iterator sequence of pairs - - template - std::string pair_sequence_to_string(I begin, - I end, - S1 to_string_fn1, - S2 to_string_fn2, - const std::string& pair_separator, - const std::string& separator); - - //////////////////////////////////////////////////////////////////////////////// - -} // end namespace stlplus - -#include "string_sequence.tpp" -#endif +#ifndef STLPLUS_STRING_SEQUENCE +#define STLPLUS_STRING_SEQUENCE +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Generate string representations of sequences represented by forward iterators + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // any forward iterator sequence + + template + std::string sequence_to_string(I begin, + I end, + S to_string, + const std::string& separator); + + + //////////////////////////////////////////////////////////////////////////////// + // any forward iterator sequence of pairs + + template + std::string pair_sequence_to_string(I begin, + I end, + S1 to_string_fn1, + S2 to_string_fn2, + const std::string& pair_separator, + const std::string& separator); + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus + +#include "string_sequence.tpp" +#endif diff --git a/src/stlplus/strings/string_sequence.tpp b/src/stlplus/strings/string_sequence.tpp index 0bc522e..7c9c827 100644 --- a/src/stlplus/strings/string_sequence.tpp +++ b/src/stlplus/strings/string_sequence.tpp @@ -1,54 +1,54 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// -#include "string_pair.hpp" - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - // any forward iterator sequence - - template - std::string sequence_to_string(I begin, - I end, - S to_string, - const std::string& separator) - { - std::string result; - for (I i = begin; i != end; i++) - { - if (i != begin) result += separator; - result += to_string(*i); - } - return result; - } - - //////////////////////////////////////////////////////////////////////////////// - // any sequence where the value is a pair - - template - std::string pair_sequence_to_string(I begin, - I end, - S1 to_string_fn1, - S2 to_string_fn2, - const std::string& pair_separator, - const std::string& separator) - { - std::string result; - for (I i = begin; i != end; i++) - { - if (i != begin) result += separator; - result += pair_to_string(*i, to_string_fn1, to_string_fn2, pair_separator); - } - return result; - } - - //////////////////////////////////////////////////////////////////////////////// - -} // end namespace stlplus +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "string_pair.hpp" + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // any forward iterator sequence + + template + std::string sequence_to_string(I begin, + I end, + S to_string, + const std::string& separator) + { + std::string result; + for (I i = begin; i != end; i++) + { + if (i != begin) result += separator; + result += to_string(*i); + } + return result; + } + + //////////////////////////////////////////////////////////////////////////////// + // any sequence where the value is a pair + + template + std::string pair_sequence_to_string(I begin, + I end, + S1 to_string_fn1, + S2 to_string_fn2, + const std::string& pair_separator, + const std::string& separator) + { + std::string result; + for (I i = begin; i != end; i++) + { + if (i != begin) result += separator; + result += pair_to_string(*i, to_string_fn1, to_string_fn2, pair_separator); + } + return result; + } + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus diff --git a/src/stlplus/strings/string_set.hpp b/src/stlplus/strings/string_set.hpp index ce06768..8c15543 100644 --- a/src/stlplus/strings/string_set.hpp +++ b/src/stlplus/strings/string_set.hpp @@ -1,33 +1,33 @@ -#ifndef STLPLUS_STRING_SET -#define STLPLUS_STRING_SET -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Generate a string representation of a set/multiset - -//////////////////////////////////////////////////////////////////////////////// -#include "strings_fixes.hpp" -#include -#include - -namespace stlplus -{ - - template - std::string set_to_string(const std::set& values, - S to_string_fn, - const std::string& separator = ","); - - template - std::string multiset_to_string(const std::multiset& values, - S to_string_fn, - const std::string& separator = ","); - -} // end namespace stlplus - -#include "string_set.tpp" -#endif +#ifndef STLPLUS_STRING_SET +#define STLPLUS_STRING_SET +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Generate a string representation of a set/multiset + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include +#include + +namespace stlplus +{ + + template + std::string set_to_string(const std::set& values, + S to_string_fn, + const std::string& separator = ","); + + template + std::string multiset_to_string(const std::multiset& values, + S to_string_fn, + const std::string& separator = ","); + +} // end namespace stlplus + +#include "string_set.tpp" +#endif diff --git a/src/stlplus/strings/string_set.tpp b/src/stlplus/strings/string_set.tpp index d7c7e80..8dd065b 100644 --- a/src/stlplus/strings/string_set.tpp +++ b/src/stlplus/strings/string_set.tpp @@ -1,39 +1,39 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// template implementations - -//////////////////////////////////////////////////////////////////////////////// -#include "string_sequence.hpp" - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - // set - - template - std::string set_to_string(const std::set& values, - S to_string_fn, - const std::string& separator) - { - return sequence_to_string(values.begin(), values.end(), to_string_fn, separator); - } - - //////////////////////////////////////////////////////////////////////////////// - // multiset - - template - std::string multiset_to_string(const std::multiset& values, - S to_string_fn, - const std::string& separator) - { - return sequence_to_string(values.begin(), values.end(), to_string_fn, separator); - } - - //////////////////////////////////////////////////////////////////////////////// -} // end namespace stlplus +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// template implementations + +//////////////////////////////////////////////////////////////////////////////// +#include "string_sequence.hpp" + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // set + + template + std::string set_to_string(const std::set& values, + S to_string_fn, + const std::string& separator) + { + return sequence_to_string(values.begin(), values.end(), to_string_fn, separator); + } + + //////////////////////////////////////////////////////////////////////////////// + // multiset + + template + std::string multiset_to_string(const std::multiset& values, + S to_string_fn, + const std::string& separator) + { + return sequence_to_string(values.begin(), values.end(), to_string_fn, separator); + } + + //////////////////////////////////////////////////////////////////////////////// +} // end namespace stlplus diff --git a/src/stlplus/strings/string_simple_ptr.hpp b/src/stlplus/strings/string_simple_ptr.hpp index 813432c..b8d07d8 100644 --- a/src/stlplus/strings/string_simple_ptr.hpp +++ b/src/stlplus/strings/string_simple_ptr.hpp @@ -1,47 +1,47 @@ -#ifndef STLPLUS_STRING_SIMPLE_PTR -#define STLPLUS_STRING_SIMPLE_PTR -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Generate a string representation of a smart pointer - -//////////////////////////////////////////////////////////////////////////////// -#include "strings_fixes.hpp" -#include "simple_ptr.hpp" -#include - -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - - template - std::string simple_ptr_to_string(const simple_ptr& value, - S to_string_fn, - const std::string& null_string = "", - const std::string& prefix = "(", - const std::string& suffix = ")"); - - template - std::string simple_ptr_clone_to_string(const simple_ptr_clone& value, - S to_string_fn, - const std::string& null_string = "", - const std::string& prefix = "(", - const std::string& suffix = ")"); - - template - std::string simple_ptr__nocopy_to_string(const simple_ptr_nocopy& value, - S to_string_fn, - const std::string& null_string = "", - const std::string& prefix = "(", - const std::string& suffix = ")"); - - -} // end namespace stlplus - -#include "string_simple_ptr.tpp" -#endif +#ifndef STLPLUS_STRING_SIMPLE_PTR +#define STLPLUS_STRING_SIMPLE_PTR +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Generate a string representation of a smart pointer + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include "simple_ptr.hpp" +#include + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + template + std::string simple_ptr_to_string(const simple_ptr& value, + S to_string_fn, + const std::string& null_string = "", + const std::string& prefix = "(", + const std::string& suffix = ")"); + + template + std::string simple_ptr_clone_to_string(const simple_ptr_clone& value, + S to_string_fn, + const std::string& null_string = "", + const std::string& prefix = "(", + const std::string& suffix = ")"); + + template + std::string simple_ptr__nocopy_to_string(const simple_ptr_nocopy& value, + S to_string_fn, + const std::string& null_string = "", + const std::string& prefix = "(", + const std::string& suffix = ")"); + + +} // end namespace stlplus + +#include "string_simple_ptr.tpp" +#endif diff --git a/src/stlplus/strings/string_simple_ptr.tpp b/src/stlplus/strings/string_simple_ptr.tpp index 7da84af..2334b7f 100644 --- a/src/stlplus/strings/string_simple_ptr.tpp +++ b/src/stlplus/strings/string_simple_ptr.tpp @@ -1,44 +1,44 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - - template - std::string simple_ptr_to_string(const simple_ptr& value, - S to_string_fn, - const std::string& null_string, - const std::string& prefix, - const std::string& suffix) - { - return value ? (prefix + to_string_fn(*value) + suffix) : null_string; - } - - template - std::string simple_ptr_clone_to_string(const simple_ptr_clone& value, - S to_string_fn, - const std::string& null_string, - const std::string& prefix, - const std::string& suffix) - { - return value ? (prefix + to_string_fn(*value) + suffix) : null_string; - } - - template - std::string simple_ptr_nocopy_to_string(const simple_ptr_nocopy& value, - S to_string_fn, - const std::string& null_string, - const std::string& prefix, - const std::string& suffix) - { - return value ? (prefix + to_string_fn(*value) + suffix) : null_string; - } - -} // end namespace stlplus - +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + template + std::string simple_ptr_to_string(const simple_ptr& value, + S to_string_fn, + const std::string& null_string, + const std::string& prefix, + const std::string& suffix) + { + return value ? (prefix + to_string_fn(*value) + suffix) : null_string; + } + + template + std::string simple_ptr_clone_to_string(const simple_ptr_clone& value, + S to_string_fn, + const std::string& null_string, + const std::string& prefix, + const std::string& suffix) + { + return value ? (prefix + to_string_fn(*value) + suffix) : null_string; + } + + template + std::string simple_ptr_nocopy_to_string(const simple_ptr_nocopy& value, + S to_string_fn, + const std::string& null_string, + const std::string& prefix, + const std::string& suffix) + { + return value ? (prefix + to_string_fn(*value) + suffix) : null_string; + } + +} // end namespace stlplus + diff --git a/src/stlplus/strings/string_smart_ptr.hpp b/src/stlplus/strings/string_smart_ptr.hpp index 3a5e253..ea8cdd2 100644 --- a/src/stlplus/strings/string_smart_ptr.hpp +++ b/src/stlplus/strings/string_smart_ptr.hpp @@ -1,47 +1,47 @@ -#ifndef STLPLUS_STRING_SMART_PTR -#define STLPLUS_STRING_SMART_PTR -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Generate a string representation of a smart pointer - -//////////////////////////////////////////////////////////////////////////////// -#include "strings_fixes.hpp" -#include "smart_ptr.hpp" -#include - -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - - template - std::string smart_ptr_to_string(const smart_ptr& value, - S to_string_fn, - const std::string& null_string = "", - const std::string& prefix = "(", - const std::string& suffix = ")"); - - template - std::string smart_ptr_clone_to_string(const smart_ptr_clone& value, - S to_string_fn, - const std::string& null_string = "", - const std::string& prefix = "(", - const std::string& suffix = ")"); - - template - std::string smart_ptr__nocopy_to_string(const smart_ptr_nocopy& value, - S to_string_fn, - const std::string& null_string = "", - const std::string& prefix = "(", - const std::string& suffix = ")"); - - -} // end namespace stlplus - -#include "string_smart_ptr.tpp" -#endif +#ifndef STLPLUS_STRING_SMART_PTR +#define STLPLUS_STRING_SMART_PTR +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Generate a string representation of a smart pointer + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include "smart_ptr.hpp" +#include + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + template + std::string smart_ptr_to_string(const smart_ptr& value, + S to_string_fn, + const std::string& null_string = "", + const std::string& prefix = "(", + const std::string& suffix = ")"); + + template + std::string smart_ptr_clone_to_string(const smart_ptr_clone& value, + S to_string_fn, + const std::string& null_string = "", + const std::string& prefix = "(", + const std::string& suffix = ")"); + + template + std::string smart_ptr__nocopy_to_string(const smart_ptr_nocopy& value, + S to_string_fn, + const std::string& null_string = "", + const std::string& prefix = "(", + const std::string& suffix = ")"); + + +} // end namespace stlplus + +#include "string_smart_ptr.tpp" +#endif diff --git a/src/stlplus/strings/string_smart_ptr.tpp b/src/stlplus/strings/string_smart_ptr.tpp index 7953fbf..994b363 100644 --- a/src/stlplus/strings/string_smart_ptr.tpp +++ b/src/stlplus/strings/string_smart_ptr.tpp @@ -1,44 +1,44 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - - template - std::string smart_ptr_to_string(const smart_ptr& value, - S to_string_fn, - const std::string& null_string, - const std::string& prefix, - const std::string& suffix) - { - return value ? (prefix + to_string_fn(*value) + suffix) : null_string; - } - - template - std::string smart_ptr_clone_to_string(const smart_ptr_clone& value, - S to_string_fn, - const std::string& null_string, - const std::string& prefix, - const std::string& suffix) - { - return value ? (prefix + to_string_fn(*value) + suffix) : null_string; - } - - template - std::string smart_ptr_nocopy_to_string(const smart_ptr_nocopy& value, - S to_string_fn, - const std::string& null_string, - const std::string& prefix, - const std::string& suffix) - { - return value ? (prefix + to_string_fn(*value) + suffix) : null_string; - } - -} // end namespace stlplus - +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + template + std::string smart_ptr_to_string(const smart_ptr& value, + S to_string_fn, + const std::string& null_string, + const std::string& prefix, + const std::string& suffix) + { + return value ? (prefix + to_string_fn(*value) + suffix) : null_string; + } + + template + std::string smart_ptr_clone_to_string(const smart_ptr_clone& value, + S to_string_fn, + const std::string& null_string, + const std::string& prefix, + const std::string& suffix) + { + return value ? (prefix + to_string_fn(*value) + suffix) : null_string; + } + + template + std::string smart_ptr_nocopy_to_string(const smart_ptr_nocopy& value, + S to_string_fn, + const std::string& null_string, + const std::string& prefix, + const std::string& suffix) + { + return value ? (prefix + to_string_fn(*value) + suffix) : null_string; + } + +} // end namespace stlplus + diff --git a/src/stlplus/strings/string_stl.hpp b/src/stlplus/strings/string_stl.hpp index 11e05fb..ef93497 100644 --- a/src/stlplus/strings/string_stl.hpp +++ b/src/stlplus/strings/string_stl.hpp @@ -1,23 +1,23 @@ -#ifndef STLPLUS_STRING_STL -#define STLPLUS_STRING_STL -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Template string conversions for pointers and STL containers - -//////////////////////////////////////////////////////////////////////////////// - -#include "string_bitset.hpp" -#include "string_list.hpp" -#include "string_map.hpp" -#include "string_pair.hpp" -#include "string_sequence.hpp" -#include "string_set.hpp" -#include "string_string.hpp" -#include "string_vector.hpp" - -#endif +#ifndef STLPLUS_STRING_STL +#define STLPLUS_STRING_STL +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Template string conversions for pointers and STL containers + +//////////////////////////////////////////////////////////////////////////////// + +#include "string_bitset.hpp" +#include "string_list.hpp" +#include "string_map.hpp" +#include "string_pair.hpp" +#include "string_sequence.hpp" +#include "string_set.hpp" +#include "string_string.hpp" +#include "string_vector.hpp" + +#endif diff --git a/src/stlplus/strings/string_stlplus.hpp b/src/stlplus/strings/string_stlplus.hpp index bdcad7b..23c4bca 100644 --- a/src/stlplus/strings/string_stlplus.hpp +++ b/src/stlplus/strings/string_stlplus.hpp @@ -1,30 +1,30 @@ -#ifndef STLPLUS_STRING_STLPLUS -#define STLPLUS_STRING_STLPLUS -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Template string conversions for the STLplus containers - -//////////////////////////////////////////////////////////////////////////////// - -// can be excluded to break the dependency on the containers library -#ifndef NO_STLPLUS_CONTAINERS -#include "string_digraph.hpp" -#include "string_foursome.hpp" -#include "string_hash.hpp" -#include "string_matrix.hpp" -#include "string_ntree.hpp" -#include "string_smart_ptr.hpp" -#include "string_triple.hpp" -#endif - -// can be excluded to break the dependency on the portability library -#ifndef NO_STLPLUS_INF -#include "string_inf.hpp" -#endif - -#endif +#ifndef STLPLUS_STRING_STLPLUS +#define STLPLUS_STRING_STLPLUS +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Template string conversions for the STLplus containers + +//////////////////////////////////////////////////////////////////////////////// + +// can be excluded to break the dependency on the containers library +#ifndef NO_STLPLUS_CONTAINERS +#include "string_digraph.hpp" +#include "string_foursome.hpp" +#include "string_hash.hpp" +#include "string_matrix.hpp" +#include "string_ntree.hpp" +#include "string_smart_ptr.hpp" +#include "string_triple.hpp" +#endif + +// can be excluded to break the dependency on the portability library +#ifndef NO_STLPLUS_INF +#include "string_inf.hpp" +#endif + +#endif diff --git a/src/stlplus/strings/string_string.cpp b/src/stlplus/strings/string_string.cpp index 53e8189..27d5f89 100644 --- a/src/stlplus/strings/string_string.cpp +++ b/src/stlplus/strings/string_string.cpp @@ -1,22 +1,22 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// -#include "string_string.hpp" - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - // strings - - std::string string_to_string(const std::string& value) - { - return value; - } - -} // end namespace stlplus +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "string_string.hpp" + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // strings + + std::string string_to_string(const std::string& value) + { + return value; + } + +} // end namespace stlplus diff --git a/src/stlplus/strings/string_string.hpp b/src/stlplus/strings/string_string.hpp index 5f5b044..522f698 100644 --- a/src/stlplus/strings/string_string.hpp +++ b/src/stlplus/strings/string_string.hpp @@ -1,26 +1,26 @@ -#ifndef STLPLUS_STRING_STRING -#define STLPLUS_STRING_STRING -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Functions for converting C/STL strings to string - -// This is necessary for completeness, e.g. for use in vector_to_string for vector - -//////////////////////////////////////////////////////////////////////////////// -#include "strings_fixes.hpp" -#include -#include - -namespace stlplus -{ - - std::string string_to_string(const std::string& value); - -} - -#endif +#ifndef STLPLUS_STRING_STRING +#define STLPLUS_STRING_STRING +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Functions for converting C/STL strings to string + +// This is necessary for completeness, e.g. for use in vector_to_string for vector + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include +#include + +namespace stlplus +{ + + std::string string_to_string(const std::string& value); + +} + +#endif diff --git a/src/stlplus/strings/string_triple.hpp b/src/stlplus/strings/string_triple.hpp index c812789..030cf0a 100644 --- a/src/stlplus/strings/string_triple.hpp +++ b/src/stlplus/strings/string_triple.hpp @@ -1,32 +1,32 @@ -#ifndef STLPLUS_STRING_TRIPLE -#define STLPLUS_STRING_TRIPLE -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Generate a string representation of a triple - -//////////////////////////////////////////////////////////////////////////////// -#include "strings_fixes.hpp" -#include "triple.hpp" -#include - -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - - template - std::string triple_to_string(const triple& values, - S1 to_string_fn1, - S2 to_string_fn2, - S3 to_string_fn3, - const std::string& separator = ":"); - -} // end namespace stlplus - -#include "string_triple.tpp" -#endif +#ifndef STLPLUS_STRING_TRIPLE +#define STLPLUS_STRING_TRIPLE +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Generate a string representation of a triple + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include "triple.hpp" +#include + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + template + std::string triple_to_string(const triple& values, + S1 to_string_fn1, + S2 to_string_fn2, + S3 to_string_fn3, + const std::string& separator = ":"); + +} // end namespace stlplus + +#include "string_triple.tpp" +#endif diff --git a/src/stlplus/strings/string_triple.tpp b/src/stlplus/strings/string_triple.tpp index ddd574a..8cd1d93 100644 --- a/src/stlplus/strings/string_triple.tpp +++ b/src/stlplus/strings/string_triple.tpp @@ -1,29 +1,29 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - - template - std::string triple_to_string(const triple& values, - S1 to_string_fn1, - S2 to_string_fn2, - S3 to_string_fn3, - const std::string& separator) - { - return - to_string_fn1(values.first) + - separator + - to_string_fn2(values.second) + - separator + - to_string_fn3(values.third); - } - -} // end namespace stlplus - +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + template + std::string triple_to_string(const triple& values, + S1 to_string_fn1, + S2 to_string_fn2, + S3 to_string_fn3, + const std::string& separator) + { + return + to_string_fn1(values.first) + + separator + + to_string_fn2(values.second) + + separator + + to_string_fn3(values.third); + } + +} // end namespace stlplus + diff --git a/src/stlplus/strings/string_utilities.cpp b/src/stlplus/strings/string_utilities.cpp index 9ff5345..7e04824 100644 --- a/src/stlplus/strings/string_utilities.cpp +++ b/src/stlplus/strings/string_utilities.cpp @@ -1,442 +1,442 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// -#include "string_utilities.hpp" -#include "string_basic.hpp" -#include -#include -#include -#include - -namespace stlplus -{ - - // added as a local copy to break the dependency on the portability library - static std::string local_dformat(const char* format, ...) throw(std::invalid_argument) - { - std::string formatted; - va_list args; - va_start(args, format); -#ifdef MSWINDOWS - int length = 0; - char* buffer = 0; - for(int buffer_length = 256; ; buffer_length*=2) - { - buffer = (char*)malloc(buffer_length); - if (!buffer) throw std::invalid_argument("string_utilities"); - length = _vsnprintf(buffer, buffer_length-1, format, args); - if (length >= 0) - { - buffer[length] = 0; - formatted += std::string(buffer); - free(buffer); - break; - } - free(buffer); - } -#else - char* buffer = 0; - int length = vasprintf(&buffer, format, args); - if (!buffer) throw std::invalid_argument("string_utilities"); - if (length >= 0) - formatted += std::string(buffer); - free(buffer); -#endif - va_end(args); - if (length < 0) throw std::invalid_argument("string_utilities"); - return formatted; - } - - //////////////////////////////////////////////////////////////////////////////// - - std::string pad(const std::string& str, alignment_t alignment, unsigned width, char padch) - throw(std::invalid_argument) - { - std::string result = str; - switch(alignment) - { - case align_left: - { - unsigned padding = width>str.size() ? width - str.size() : 0; - unsigned i = 0; - while (i++ < padding) - result.insert(result.end(), padch); - break; - } - case align_right: - { - unsigned padding = width>str.size() ? width - str.size() : 0; - unsigned i = 0; - while (i++ < padding) - result.insert(result.begin(), padch); - break; - } - case align_centre: - { - unsigned padding = width>str.size() ? width - str.size() : 0; - unsigned i = 0; - while (i++ < padding/2) - result.insert(result.end(), padch); - i--; - while (i++ < padding) - result.insert(result.begin(), padch); - break; - } - default: - throw std::invalid_argument("invalid alignment value"); - } - return result; - } - - //////////////////////////////////////////////////////////////////////////////// - - std::string trim_left(const std::string& val) - { - std::string result = val; - while (!result.empty() && isspace(result[0])) - result.erase(result.begin()); - return result; - } - - std::string trim_right(const std::string& val) - { - std::string result = val; - while (!result.empty() && isspace(result[result.size()-1])) - result.erase(result.end()-1); - return result; - } - - std::string trim(const std::string& val) - { - std::string result = val; - while (!result.empty() && isspace(result[0])) - result.erase(result.begin()); - while (!result.empty() && isspace(result[result.size()-1])) - result.erase(result.end()-1); - return result; - } - - //////////////////////////////////////////////////////////////////////////////// - - 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; - } - - std::string uppercase(const std::string& val) - { - std::string text = val; - for (unsigned i = 0; i < text.size(); i++) - text[i] = toupper(text[i]); - return text; - } - - //////////////////////////////////////////////////////////////////////////////// - - std::string translate(const std::string& input, const std::string& from_set, const std::string& to_set) - { - std::string result; - for (unsigned i = 0; i < input.size(); i++) - { - char ch = input[i]; - // check to see if the character is in the from set - std::string::size_type found = from_set.find(ch); - if (found == std::string::npos) - { - // not found so just copy across - result += ch; - } - else if (found < to_set.size()) - { - // found and in range so translate - result += to_set[found]; - } - } - return result; - } - - //////////////////////////////////////////////////////////////////////////////// - // 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???????? - // 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 - - // 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 - - static bool match_set (const std::string& set, char match) - { - // first expand any ranges and remove escape characters to make life more palatable - 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 match_wildcard(const std::string& wild, const std::string& match) - { - return match_remainder(wild, wild.begin(), match, match.begin()); - } - - //////////////////////////////////////////////////////////////////////////////// - - std::vector split(const std::string& str, const std::string& splitter) - { - std::vector result; - if (!str.empty()) - { - for(std::string::size_type offset = 0;;) - { - std::string::size_type found = str.find(splitter, offset); - if (found != std::string::npos) - { - result.push_back(str.substr(offset, found-offset)); - offset = found + splitter.size(); - } - else - { - result.push_back(str.substr(offset, str.size()-offset)); - break; - } - } - } - return result; - } - - std::string join (const std::vector& str, - const std::string& joiner, - const std::string& prefix, - const std::string& suffix) - { - std::string result = prefix; - for (unsigned i = 0; i < str.size(); i++) - { - if (i) result += joiner; - result += str[i]; - } - result += suffix; - return result; - } - - //////////////////////////////////////////////////////////////////////////////// - - std::string display_bytes(long bytes) - { - std::string result; - if (bytes < 0) - { - result += '-'; - bytes = -bytes; - } - static const long kB = 1024l; - static const long MB = kB * kB; - static const long GB = MB * kB; - if (bytes < kB) - result += local_dformat("%i", bytes); - else if (bytes < (10l * kB)) - result += local_dformat("%.2fk", ((float)bytes / (float)kB)); - else if (bytes < (100l * kB)) - result += local_dformat("%.1fk", ((float)bytes / (float)kB)); - else if (bytes < MB) - result += local_dformat("%.0fk", ((float)bytes / (float)kB)); - else if (bytes < (10l * MB)) - result += local_dformat("%.2fM", ((float)bytes / (float)MB)); - else if (bytes < (100l * MB)) - result += local_dformat("%.1fM", ((float)bytes / (float)MB)); - else if (bytes < GB) - result += local_dformat("%.0fM", ((float)bytes / (float)MB)); - else - result += local_dformat("%.2fG", ((float)bytes / (float)GB)); - return result; - } - - std::string display_time(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 += unsigned_to_string(weeks, 10, radix_none, 1); - result += "w "; - } - if (!result.empty() || days > 0) - { - result += unsigned_to_string(days, 10, radix_none, 1); - result += "d "; - } - if (!result.empty() || hours > 0) - { - result += unsigned_to_string(hours, 10, radix_none, 1); - result += ":"; - } - if (!result.empty() || minutes > 0) - { - if (!result.empty()) - result += unsigned_to_string(minutes, 10, radix_none, 2); - else - result += unsigned_to_string(minutes, 10, radix_none, 1); - result += ":"; - } - if (!result.empty()) - result += unsigned_to_string((unsigned)seconds, 10, radix_none, 2); - else - { - result += unsigned_to_string((unsigned)seconds, 10, radix_none, 1); - result += "s"; - } - return result; - } - - //////////////////////////////////////////////////////////////////////////////// - -} // end namespace stlplus +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "string_utilities.hpp" +#include "string_basic.hpp" +#include +#include +#include +#include + +namespace stlplus +{ + + // added as a local copy to break the dependency on the portability library + static std::string local_dformat(const char* format, ...) throw(std::invalid_argument) + { + std::string formatted; + va_list args; + va_start(args, format); +#ifdef MSWINDOWS + int length = 0; + char* buffer = 0; + for(int buffer_length = 256; ; buffer_length*=2) + { + buffer = (char*)malloc(buffer_length); + if (!buffer) throw std::invalid_argument("string_utilities"); + length = _vsnprintf(buffer, buffer_length-1, format, args); + if (length >= 0) + { + buffer[length] = 0; + formatted += std::string(buffer); + free(buffer); + break; + } + free(buffer); + } +#else + char* buffer = 0; + int length = vasprintf(&buffer, format, args); + if (!buffer) throw std::invalid_argument("string_utilities"); + if (length >= 0) + formatted += std::string(buffer); + free(buffer); +#endif + va_end(args); + if (length < 0) throw std::invalid_argument("string_utilities"); + return formatted; + } + + //////////////////////////////////////////////////////////////////////////////// + + std::string pad(const std::string& str, alignment_t alignment, unsigned width, char padch) + throw(std::invalid_argument) + { + std::string result = str; + switch(alignment) + { + case align_left: + { + unsigned padding = width>str.size() ? width - str.size() : 0; + unsigned i = 0; + while (i++ < padding) + result.insert(result.end(), padch); + break; + } + case align_right: + { + unsigned padding = width>str.size() ? width - str.size() : 0; + unsigned i = 0; + while (i++ < padding) + result.insert(result.begin(), padch); + break; + } + case align_centre: + { + unsigned padding = width>str.size() ? width - str.size() : 0; + unsigned i = 0; + while (i++ < padding/2) + result.insert(result.end(), padch); + i--; + while (i++ < padding) + result.insert(result.begin(), padch); + break; + } + default: + throw std::invalid_argument("invalid alignment value"); + } + return result; + } + + //////////////////////////////////////////////////////////////////////////////// + + std::string trim_left(const std::string& val) + { + std::string result = val; + while (!result.empty() && isspace(result[0])) + result.erase(result.begin()); + return result; + } + + std::string trim_right(const std::string& val) + { + std::string result = val; + while (!result.empty() && isspace(result[result.size()-1])) + result.erase(result.end()-1); + return result; + } + + std::string trim(const std::string& val) + { + std::string result = val; + while (!result.empty() && isspace(result[0])) + result.erase(result.begin()); + while (!result.empty() && isspace(result[result.size()-1])) + result.erase(result.end()-1); + return result; + } + + //////////////////////////////////////////////////////////////////////////////// + + 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; + } + + std::string uppercase(const std::string& val) + { + std::string text = val; + for (unsigned i = 0; i < text.size(); i++) + text[i] = toupper(text[i]); + return text; + } + + //////////////////////////////////////////////////////////////////////////////// + + std::string translate(const std::string& input, const std::string& from_set, const std::string& to_set) + { + std::string result; + for (unsigned i = 0; i < input.size(); i++) + { + char ch = input[i]; + // check to see if the character is in the from set + std::string::size_type found = from_set.find(ch); + if (found == std::string::npos) + { + // not found so just copy across + result += ch; + } + else if (found < to_set.size()) + { + // found and in range so translate + result += to_set[found]; + } + } + return result; + } + + //////////////////////////////////////////////////////////////////////////////// + // 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???????? + // 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 + + // 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 + + static bool match_set (const std::string& set, char match) + { + // first expand any ranges and remove escape characters to make life more palatable + 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 match_wildcard(const std::string& wild, const std::string& match) + { + return match_remainder(wild, wild.begin(), match, match.begin()); + } + + //////////////////////////////////////////////////////////////////////////////// + + std::vector split(const std::string& str, const std::string& splitter) + { + std::vector result; + if (!str.empty()) + { + for(std::string::size_type offset = 0;;) + { + std::string::size_type found = str.find(splitter, offset); + if (found != std::string::npos) + { + result.push_back(str.substr(offset, found-offset)); + offset = found + splitter.size(); + } + else + { + result.push_back(str.substr(offset, str.size()-offset)); + break; + } + } + } + return result; + } + + std::string join (const std::vector& str, + const std::string& joiner, + const std::string& prefix, + const std::string& suffix) + { + std::string result = prefix; + for (unsigned i = 0; i < str.size(); i++) + { + if (i) result += joiner; + result += str[i]; + } + result += suffix; + return result; + } + + //////////////////////////////////////////////////////////////////////////////// + + std::string display_bytes(long bytes) + { + std::string result; + if (bytes < 0) + { + result += '-'; + bytes = -bytes; + } + static const long kB = 1024l; + static const long MB = kB * kB; + static const long GB = MB * kB; + if (bytes < kB) + result += local_dformat("%i", bytes); + else if (bytes < (10l * kB)) + result += local_dformat("%.2fk", ((float)bytes / (float)kB)); + else if (bytes < (100l * kB)) + result += local_dformat("%.1fk", ((float)bytes / (float)kB)); + else if (bytes < MB) + result += local_dformat("%.0fk", ((float)bytes / (float)kB)); + else if (bytes < (10l * MB)) + result += local_dformat("%.2fM", ((float)bytes / (float)MB)); + else if (bytes < (100l * MB)) + result += local_dformat("%.1fM", ((float)bytes / (float)MB)); + else if (bytes < GB) + result += local_dformat("%.0fM", ((float)bytes / (float)MB)); + else + result += local_dformat("%.2fG", ((float)bytes / (float)GB)); + return result; + } + + std::string display_time(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 += unsigned_to_string(weeks, 10, radix_none, 1); + result += "w "; + } + if (!result.empty() || days > 0) + { + result += unsigned_to_string(days, 10, radix_none, 1); + result += "d "; + } + if (!result.empty() || hours > 0) + { + result += unsigned_to_string(hours, 10, radix_none, 1); + result += ":"; + } + if (!result.empty() || minutes > 0) + { + if (!result.empty()) + result += unsigned_to_string(minutes, 10, radix_none, 2); + else + result += unsigned_to_string(minutes, 10, radix_none, 1); + result += ":"; + } + if (!result.empty()) + result += unsigned_to_string((unsigned)seconds, 10, radix_none, 2); + else + { + result += unsigned_to_string((unsigned)seconds, 10, radix_none, 1); + result += "s"; + } + return result; + } + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus diff --git a/src/stlplus/strings/string_utilities.hpp b/src/stlplus/strings/string_utilities.hpp index d8d39c4..369477d 100644 --- a/src/stlplus/strings/string_utilities.hpp +++ b/src/stlplus/strings/string_utilities.hpp @@ -1,120 +1,120 @@ -#ifndef STLPLUS_STRING_UTILITIES -#define STLPLUS_STRING_UTILITIES -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Utilities for manipulating std::strings - -//////////////////////////////////////////////////////////////////////////////// -#include "strings_fixes.hpp" -#include "format_types.hpp" -#include -#include -#include -#include - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - // Padding function allows a string to be printed in a fixed-width field - //////////////////////////////////////////////////////////////////////////////// - - // The definitions for the alignment are declared in format_types.hpp - // Any other value will cause std::invalid_argument to be thrown - - std::string pad(const std::string& str, - alignment_t alignment, - unsigned width, - char padch = ' ') - throw(std::invalid_argument); - - //////////////////////////////////////////////////////////////////////////////// - // whitespace trimming - //////////////////////////////////////////////////////////////////////////////// - - std::string trim_left(const std::string& val); - std::string trim_right(const std::string& val); - std::string trim(const std::string& val); - - //////////////////////////////////////////////////////////////////////////////// - // case conversion for std::strings - //////////////////////////////////////////////////////////////////////////////// - - std::string lowercase(const std::string& val); - std::string uppercase(const std::string& val); - - //////////////////////////////////////////////////////////////////////////////// - // character translation - inspired by Unix 'tr' command - //////////////////////////////////////////////////////////////////////////////// - - // convert characters represented in from_set to the characters in the same position in to_set - // for example: - // filename = translate(filename,"abcdefghijklmnopqrstuvwxyz","ABCDEFGHIJKLMNOPQRSTUVWXYZ"); - // converts the filename to uppercase and returns the result (Note that the - // uppercase function does this more easily). If the from_set is longer than - // the to_set, then the overlap represents characters to delete (i.e. they map - // to nothing) - - std::string translate(const std::string& input, - const std::string& from_set, - const std::string& to_set = std::string()); - - //////////////////////////////////////////////////////////////////////////////// - // wildcard matching - //////////////////////////////////////////////////////////////////////////////// - - // this function does wildcard matching of the wildcard expression against the candidate std::string - // wildcards are NOT regular expressions - // the wildcard characters are * and ? where * matches 1 or more characters and ? matches only one - // there are also character sets [a-z] [qwertyuiop] etc. which match 1 character - // TODO: character sets like [:alpha:] - // TODO eventually: regular expression matching and substitution (3rd party library?) - - bool match_wildcard(const std::string& wild, - const std::string& match); - - //////////////////////////////////////////////////////////////////////////////// - // Perl-inspired split/join functions - //////////////////////////////////////////////////////////////////////////////// - - // splits the string at every occurance of splitter and adds it as a separate string to the return value - // the splitter is removed - // a string with no splitter in it will give a single-value vector - // an empty string gives an empty vector - - std::vector split (const std::string& str, - const std::string& splitter = "\n"); - - // the reverse of the above - // joins the string vector to create a single string with the joiner inserted between the joins - // Note: the joiner will not be added at the beginning or the end - // However, there are optional fields to add such prefix and suffix strings - - std::string join (const std::vector&, - const std::string& joiner = "\n", - const std::string& prefix = "", - const std::string& suffix = ""); - - //////////////////////////////////////////////////////////////////////////////// - // special displays - //////////////////////////////////////////////////////////////////////////////// - - // display the parameter as a number in bytes, kbytes, Mbytes, Gbytes depending on range - - std::string display_bytes(long bytes); - - // display the parameter in seconds as a string representation in weeks, days, hours, minutes, seconds - // e.g. "1d 1:01:01" means 1 day, 1 hour, 1 minute and 1 second - - std::string display_time(time_t seconds); - - //////////////////////////////////////////////////////////////////////////////// - -} // end namespace stlplus - -#endif +#ifndef STLPLUS_STRING_UTILITIES +#define STLPLUS_STRING_UTILITIES +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Utilities for manipulating std::strings + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include "format_types.hpp" +#include +#include +#include +#include + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // Padding function allows a string to be printed in a fixed-width field + //////////////////////////////////////////////////////////////////////////////// + + // The definitions for the alignment are declared in format_types.hpp + // Any other value will cause std::invalid_argument to be thrown + + std::string pad(const std::string& str, + alignment_t alignment, + unsigned width, + char padch = ' ') + throw(std::invalid_argument); + + //////////////////////////////////////////////////////////////////////////////// + // whitespace trimming + //////////////////////////////////////////////////////////////////////////////// + + std::string trim_left(const std::string& val); + std::string trim_right(const std::string& val); + std::string trim(const std::string& val); + + //////////////////////////////////////////////////////////////////////////////// + // case conversion for std::strings + //////////////////////////////////////////////////////////////////////////////// + + std::string lowercase(const std::string& val); + std::string uppercase(const std::string& val); + + //////////////////////////////////////////////////////////////////////////////// + // character translation - inspired by Unix 'tr' command + //////////////////////////////////////////////////////////////////////////////// + + // convert characters represented in from_set to the characters in the same position in to_set + // for example: + // filename = translate(filename,"abcdefghijklmnopqrstuvwxyz","ABCDEFGHIJKLMNOPQRSTUVWXYZ"); + // converts the filename to uppercase and returns the result (Note that the + // uppercase function does this more easily). If the from_set is longer than + // the to_set, then the overlap represents characters to delete (i.e. they map + // to nothing) + + std::string translate(const std::string& input, + const std::string& from_set, + const std::string& to_set = std::string()); + + //////////////////////////////////////////////////////////////////////////////// + // wildcard matching + //////////////////////////////////////////////////////////////////////////////// + + // this function does wildcard matching of the wildcard expression against the candidate std::string + // wildcards are NOT regular expressions + // the wildcard characters are * and ? where * matches 1 or more characters and ? matches only one + // there are also character sets [a-z] [qwertyuiop] etc. which match 1 character + // TODO: character sets like [:alpha:] + // TODO eventually: regular expression matching and substitution (3rd party library?) + + bool match_wildcard(const std::string& wild, + const std::string& match); + + //////////////////////////////////////////////////////////////////////////////// + // Perl-inspired split/join functions + //////////////////////////////////////////////////////////////////////////////// + + // splits the string at every occurance of splitter and adds it as a separate string to the return value + // the splitter is removed + // a string with no splitter in it will give a single-value vector + // an empty string gives an empty vector + + std::vector split (const std::string& str, + const std::string& splitter = "\n"); + + // the reverse of the above + // joins the string vector to create a single string with the joiner inserted between the joins + // Note: the joiner will not be added at the beginning or the end + // However, there are optional fields to add such prefix and suffix strings + + std::string join (const std::vector&, + const std::string& joiner = "\n", + const std::string& prefix = "", + const std::string& suffix = ""); + + //////////////////////////////////////////////////////////////////////////////// + // special displays + //////////////////////////////////////////////////////////////////////////////// + + // display the parameter as a number in bytes, kbytes, Mbytes, Gbytes depending on range + + std::string display_bytes(long bytes); + + // display the parameter in seconds as a string representation in weeks, days, hours, minutes, seconds + // e.g. "1d 1:01:01" means 1 day, 1 hour, 1 minute and 1 second + + std::string display_time(time_t seconds); + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus + +#endif diff --git a/src/stlplus/strings/string_vector.cpp b/src/stlplus/strings/string_vector.cpp index bee9b88..66868bc 100644 --- a/src/stlplus/strings/string_vector.cpp +++ b/src/stlplus/strings/string_vector.cpp @@ -1,27 +1,27 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// -#include "string_vector.hpp" - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - // special case of vector - - std::string bool_vector_to_string(const std::vector& values) - { - std::string result; - for (size_t i = 0; i < values.size(); i++) - result.append(1, values[i] ? '1' : '0'); - return result; - } - - //////////////////////////////////////////////////////////////////////////////// - -} // end namespace stlplus +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "string_vector.hpp" + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // special case of vector + + std::string bool_vector_to_string(const std::vector& values) + { + std::string result; + for (size_t i = 0; i < values.size(); i++) + result.append(1, values[i] ? '1' : '0'); + return result; + } + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus diff --git a/src/stlplus/strings/string_vector.hpp b/src/stlplus/strings/string_vector.hpp index 6842a98..204eea9 100644 --- a/src/stlplus/strings/string_vector.hpp +++ b/src/stlplus/strings/string_vector.hpp @@ -1,36 +1,36 @@ -#ifndef STLPLUS_STRING_VECTOR -#define STLPLUS_STRING_VECTOR -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Generate a string representation of a vector - -//////////////////////////////////////////////////////////////////////////////// -#include "strings_fixes.hpp" -#include -#include - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - // vector - - template - std::string vector_to_string(const std::vector& values, - S to_string_fn, - const std::string& separator = ","); - - // specialisation for vector which has a different implementation - std::string bool_vector_to_string(const std::vector& values); - - //////////////////////////////////////////////////////////////////////////////// - -} // end namespace stlplus - -#include "string_vector.tpp" -#endif +#ifndef STLPLUS_STRING_VECTOR +#define STLPLUS_STRING_VECTOR +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Generate a string representation of a vector + +//////////////////////////////////////////////////////////////////////////////// +#include "strings_fixes.hpp" +#include +#include + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // vector + + template + std::string vector_to_string(const std::vector& values, + S to_string_fn, + const std::string& separator = ","); + + // specialisation for vector which has a different implementation + std::string bool_vector_to_string(const std::vector& values); + + //////////////////////////////////////////////////////////////////////////////// + +} // end namespace stlplus + +#include "string_vector.tpp" +#endif diff --git a/src/stlplus/strings/string_vector.tpp b/src/stlplus/strings/string_vector.tpp index de535ee..9e2f385 100644 --- a/src/stlplus/strings/string_vector.tpp +++ b/src/stlplus/strings/string_vector.tpp @@ -1,22 +1,22 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// -#include "string_sequence.hpp" - -namespace stlplus -{ - - template - std::string vector_to_string(const std::vector& values, - S to_string_fn, - const std::string& separator) - { - return sequence_to_string(values.begin(), values.end(), to_string_fn, separator); - } - -} // end namespace stlplus +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +//////////////////////////////////////////////////////////////////////////////// +#include "string_sequence.hpp" + +namespace stlplus +{ + + template + std::string vector_to_string(const std::vector& values, + S to_string_fn, + const std::string& separator) + { + return sequence_to_string(values.begin(), values.end(), to_string_fn, separator); + } + +} // end namespace stlplus diff --git a/src/stlplus/strings/strings.hpp b/src/stlplus/strings/strings.hpp index 06a6e92..dba420e 100644 --- a/src/stlplus/strings/strings.hpp +++ b/src/stlplus/strings/strings.hpp @@ -1,30 +1,30 @@ -#ifndef STLPLUS_STRINGS -#define STLPLUS_STRINGS -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Header for including the whole strings library in one go - -// The strings library prints C and C++ types into a std::string - -// - it extends the numeric presentation of integers to include any radix from 2 - 36 -// - it allows the printing of data structures - useful for diagnostic dumps - -//////////////////////////////////////////////////////////////////////////////// - -#include "string_utilities.hpp" - -#include "string_basic.hpp" -#include "string_stl.hpp" -#include "string_stlplus.hpp" - -#include "print_basic.hpp" -#include "print_stl.hpp" -#include "print_stlplus.hpp" - -//////////////////////////////////////////////////////////////////////////////// -#endif +#ifndef STLPLUS_STRINGS +#define STLPLUS_STRINGS +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// Header for including the whole strings library in one go + +// The strings library prints C and C++ types into a std::string + +// - it extends the numeric presentation of integers to include any radix from 2 - 36 +// - it allows the printing of data structures - useful for diagnostic dumps + +//////////////////////////////////////////////////////////////////////////////// + +#include "string_utilities.hpp" + +#include "string_basic.hpp" +#include "string_stl.hpp" +#include "string_stlplus.hpp" + +#include "print_basic.hpp" +#include "print_stl.hpp" +#include "print_stlplus.hpp" + +//////////////////////////////////////////////////////////////////////////////// +#endif diff --git a/src/stlplus/strings/strings_fixes.hpp b/src/stlplus/strings/strings_fixes.hpp index 2ed1dc5..c6368ce 100644 --- a/src/stlplus/strings/strings_fixes.hpp +++ b/src/stlplus/strings/strings_fixes.hpp @@ -1,56 +1,56 @@ -#ifndef STLPLUS_STRINGS_FIXES -#define STLPLUS_STRINGS_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 - -//////////////////////////////////////////////////////////////////////////////// - -//////////////////////////////////////////////////////////////////////////////// -// Problem with MicroSoft defining two different macros to identify Windows -//////////////////////////////////////////////////////////////////////////////// - -#if defined(_WIN32) || defined(_WIN32_WCE) -#define MSWINDOWS -#endif - -//////////////////////////////////////////////////////////////////////////////// -// 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) -// 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 -// 8066 - Unreachable code. -// A break, continue, goto, or return statement was not followed by a -// label or the end of a loop or function. The compiler checks while, -// do, and for loops with a constant test condition, and attempts to -// recognize loops that can't fall through. -#pragma warn -8026 -#pragma warn -8027 -#pragma warn -8066 -#endif - -//////////////////////////////////////////////////////////////////////////////// -#endif +#ifndef STLPLUS_STRINGS_FIXES +#define STLPLUS_STRINGS_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 + +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +// Problem with MicroSoft defining two different macros to identify Windows +//////////////////////////////////////////////////////////////////////////////// + +#if defined(_WIN32) || defined(_WIN32_WCE) +#define MSWINDOWS +#endif + +//////////////////////////////////////////////////////////////////////////////// +// 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) +// 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 +// 8066 - Unreachable code. +// A break, continue, goto, or return statement was not followed by a +// label or the end of a loop or function. The compiler checks while, +// do, and for loops with a constant test condition, and attempts to +// recognize loops that can't fall through. +#pragma warn -8026 +#pragma warn -8027 +#pragma warn -8066 +#endif + +//////////////////////////////////////////////////////////////////////////////// +#endif diff --git a/src/stlplus/subsystems/cli_parser.cpp b/src/stlplus/subsystems/cli_parser.cpp deleted file mode 100644 index 1088f38..0000000 --- a/src/stlplus/subsystems/cli_parser.cpp +++ /dev/null @@ -1,637 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// -#include "cli_parser.hpp" -#include "file_system.hpp" -#include "dprintf.hpp" -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - // cli_definition internals - - const std::string& stlplus::cli_definition::name(void) const - { - return m_name; - } - - stlplus::cli_kind_t stlplus::cli_definition::kind(void) const - { - return m_kind; - } - - stlplus::cli_mode_t stlplus::cli_definition::mode(void) const - { - return m_mode; - } - - const std::string& stlplus::cli_definition::message(void) const - { - return m_message; - } - - const std::string& stlplus::cli_definition::default_value(void) const - { - return m_default; - } - - //////////////////////////////////////////////////////////////////////////////// - // internal data structures - - class cli_value - { - public: - unsigned m_definition; - std::string m_value; - unsigned m_level; - std::string m_source; - - cli_value(unsigned definition, const std::string& value, unsigned level, const std::string& source) : - m_definition(definition), m_value(value), m_level(level), m_source(source) - { - } - }; - - //////////////////////////////////////////////////////////////////////////////// - - class cli_parser_data - { - public: - message_handler& m_messages; - std::string m_executable; - cli_parser::definitions m_definitions; - std::vector m_values; - unsigned m_level; - bool m_valid; - std::vector m_ini_files; - - public: - - cli_parser_data(message_handler& messages) : - m_messages(messages), m_level(1), m_valid(true) - { - // ensure that the CLI's messages are in the message handler - these - // can be overridden from a file later - see message_handler - if (!m_messages.message_present("CLI_VALUE_MISSING")) - m_messages.add_message("CLI_VALUE_MISSING", "option @0 requires a value - end of line was reached instead"); - if (!m_messages.message_present("CLI_UNRECOGNISED_OPTION")) - m_messages.add_message("CLI_UNRECOGNISED_OPTION", "@0 is not a recognised option for this command"); - if (!m_messages.message_present("CLI_NO_VALUES")) - m_messages.add_message("CLI_NO_VALUES", "argument @0 is invalid - this program doesn't take command-line arguments"); - if (!m_messages.message_present("CLI_USAGE_PROGRAM")) - m_messages.add_message("CLI_USAGE_PROGRAM", "usage:\n\t@0 { arguments }"); - if (!m_messages.message_present("CLI_USAGE_DEFINITIONS")) - m_messages.add_message("CLI_USAGE_DEFINITIONS", "arguments:"); - if (!m_messages.message_present("CLI_USAGE_VALUES")) - m_messages.add_message("CLI_USAGE_VALUES", "values:"); - if (!m_messages.message_present("CLI_USAGE_VALUE_RESULT")) - m_messages.add_message("CLI_USAGE_VALUE_RESULT", "\t@0 : from @1"); - if (!m_messages.message_present("CLI_USAGE_SWITCH_RESULT")) - m_messages.add_message("CLI_USAGE_SWITCH_RESULT", "\t-@0 : from @1"); - if (!m_messages.message_present("CLI_USAGE_OPTION_RESULT")) - m_messages.add_message("CLI_USAGE_OPTION_RESULT", "\t-@0 @1 : from @2"); - if (!m_messages.message_present("CLI_INI_HEADER")) - m_messages.add_message("CLI_INI_HEADER", "configuration files:"); - if (!m_messages.message_present("CLI_INI_FILE_PRESENT")) - m_messages.add_message("CLI_INI_FILE_PRESENT", "\t@0"); - if (!m_messages.message_present("CLI_INI_FILE_ABSENT")) - m_messages.add_message("CLI_INI_FILE_ABSENT", "\t@0 (not found)"); - if (!m_messages.message_present("CLI_INI_VARIABLE")) - m_messages.add_message("CLI_INI_VARIABLE", "unknown variable \"@0\" found in Ini file"); - } - - unsigned add_definition(const cli_parser::definition& definition) - { - m_definitions.push_back(definition); - return m_definitions.size()-1; - } - - std::string name(unsigned i) const throw(cli_index_error) - { - if (i >= m_values.size()) throw cli_index_error("Index " + dformat("%u",i) + " out of range"); - return m_definitions[m_values[i].m_definition].name(); - } - - unsigned id(unsigned i) const throw(cli_index_error) - { - if (i >= m_values.size()) throw cli_index_error("Index " + dformat("%u",i) + " out of range"); - return m_values[i].m_definition; - } - - cli_parser::kind_t kind(unsigned i) const throw(cli_index_error) - { - if (i >= m_values.size()) throw cli_index_error("Index " + dformat("%u",i) + " out of range"); - return m_definitions[m_values[i].m_definition].kind(); - } - - cli_parser::mode_t mode(unsigned i) const throw(cli_index_error) - { - if (i >= m_values.size()) throw cli_index_error("Index " + dformat("%u",i) + " out of range"); - return m_definitions[m_values[i].m_definition].mode(); - } - -// unsigned add_definition(const std::string& name, -// cli_parser::kind_t kind, -// cli_parser::mode_t mode, -// const std::string& message) -// { -// return add_definition(cli_parser::definition(name, kind, mode, message)); -// } - - unsigned find_definition(const std::string& name) - { - // this does substring matching on the definitions and returns the first - // match - however, it requires at least one character in the substring so - // that the "" convention for command line arguments doesn't match with - // anything. It returns size() if it fails - for (unsigned i = 0; i < m_definitions.size(); i++) - { - std::string candidate = m_definitions[i].name(); - if ((candidate.empty() && name.empty()) || - (!name.empty() && candidate.size() >= name.size() && candidate.substr(0,name.size()) == name)) - return i; - } - return m_definitions.size(); - } - - void clear_definitions(void) - { - m_definitions.clear(); - m_values.clear(); - reset_level(); - set_valid(); - } - - void reset_level(void) - { - // the starting level is 1 so that later functions can call clear_level with - // a value of m_level-1 without causing underflow - m_level = 1; - } - - void increase_level(void) - { - m_level++; - } - - void clear_level(unsigned definition, unsigned level) - { - // clears all values with this definition at the specified level or below - for (std::vector::iterator i = m_values.begin(); i != m_values.end(); ) - { - if (i->m_definition == definition && i->m_level <= level) - i = m_values.erase(i); - else - i++; - } - } - - void set_valid(void) - { - m_valid = true; - } - - void set_invalid(void) - { - m_valid = false; - } - - bool valid(void) const - { - return m_valid; - } - - unsigned add_value(unsigned definition, const std::string& value, const std::string& source) - { - // behaviour depends on mode: - // - single: erase all previous values - // - multiple: erase values at a lower level than current - // - cumulative: erase no previous values - switch (m_definitions[definition].mode()) - { - case cli_single_mode: - clear_level(definition, m_level); - break; - case cli_multiple_mode: - clear_level(definition, m_level-1); - break; - case cli_cumulative_mode: - break; - } - m_values.push_back(cli_value(definition,value,m_level,source)); - return m_values.size()-1; - } - - unsigned add_switch(unsigned definition, bool value, const std::string& source) - { - return add_value(definition, value ? "on" : "off", source); - } - - void erase_value(unsigned definition) - { - // this simply erases all previous values - clear_level(definition, m_level); - } - - void add_ini_file(const std::string& file) - { - m_ini_files.push_back(file); - } - - unsigned ini_file_size(void) const - { - return m_ini_files.size(); - } - - const std::string& ini_file(unsigned i) const - { - return m_ini_files[i]; - } - - unsigned add_checked_definition(const cli_parser::definition& definition) throw(cli_mode_error) - { - // check for stupid combinations - // at this stage the only really stupid one is to declare command line arguments to be switch mode - if (definition.name().empty() && definition.kind() == cli_switch_kind) - { - set_invalid(); - throw cli_mode_error("CLI arguments cannot be switch kind"); - } - // add the definition to the set of all definitions - unsigned i = add_definition(definition); - // also add it to the list of values, but only if it has a default value - if (!definition.default_value().empty()) - add_value(i, definition.default_value(), "builtin default"); - return i; - } - - bool switch_value(unsigned i) const throw(cli_mode_error,cli_index_error) - { - if (i >= m_values.size()) throw cli_index_error("Index " + dformat("%u",i) + " out of range"); - if (kind(i) != cli_switch_kind) throw cli_mode_error(name(i) + " is not a switch kind"); - std::string value = m_values[i].m_value; - return value == "on" || value == "true" || value == "1"; - } - - std::string string_value(unsigned i) const throw(cli_mode_error,cli_index_error) - { - if (i >= m_values.size()) throw cli_index_error("Index " + dformat("%u",i) + " out of range"); - if (kind(i) != cli_value_kind) throw cli_mode_error(name(i) + " is not a value kind"); - return m_values[i].m_value; - } - - void set_defaults(const ini_manager& defaults, const std::string& ini_section) throw() - { - // import default values from the Ini Manager - increase_level(); - // get the set of all names from the Ini manager so that illegal names generate meaningful error messages - std::vector names = defaults.variable_names(ini_section); - for (unsigned i = 0; i < names.size(); i++) - { - std::string name = names[i]; - unsigned definition = find_definition(name); - if (definition == (unsigned)-1) - { - // not found - give an error report - message_position position(defaults.variable_filename(ini_section,name), - defaults.variable_linenumber(ini_section,name), - 0); - m_messages.error(position,"CLI_INI_VARIABLE", name); - } - else - { - // found - so add the value - // multi-valued variables are entered as a comma-separated list and this is then turned into a vector - // the vector is empty if the value was empty - std::vector values = defaults.variable_values(ini_section, name); - // an empty string is used to negate the value - if (values.empty()) - erase_value(definition); - else - { - std::string source = filespec_to_relative_path(defaults.variable_filename(ini_section, name)); - for (unsigned j = 0; j < values.size(); j++) - add_value(definition, values[j], source); - } - } - } - // add the set of ini files to the list for usage reports - for (unsigned j = 0; j < defaults.size(); j++) - add_ini_file(defaults.filename(j)); - } - - bool parse(char* argv[]) throw(cli_argument_error,message_handler_id_error,message_handler_format_error) - { - bool result = true; - if (!argv) throw cli_argument_error("Argument vector cannot be null"); - increase_level(); - if (argv[0]) - m_executable = argv[0]; - for (unsigned i = 1; argv[i]; i++) - { - std::string name = argv[i]; - if (!name.empty() && name[0] == '-') - { - // we have a command line option - unsigned found = find_definition(name.substr(1, name.size()-1)); - if (found < m_definitions.size()) - { - // found it in its positive form - switch (m_definitions[found].kind()) - { - case cli_switch_kind: - add_switch(found, true, "command line"); - break; - case cli_value_kind: - // get the next argument in argv as the value of this option - // first check that there is a next value - if (!argv[i+1]) - result &= m_messages.error("CLI_VALUE_MISSING", name); - else - add_value(found, argv[++i], "command line"); - break; - } - } - else if (name.size() > 3 && name.substr(1,2) == "no") - { - found = find_definition(name.substr(3, name.size()-3)); - if (found < m_definitions.size()) - { - // found it in its negated form - switch (m_definitions[found].kind()) - { - case cli_switch_kind: - add_switch(found, false, "command line"); - break; - case cli_value_kind: - erase_value(found); - break; - } - } - else - { - // could not find this option in either its true or negated form - result &= m_messages.error("CLI_UNRECOGNISED_OPTION", name); - } - } - else - { - // could not find this option and it could not be negated - result &= m_messages.error("CLI_UNRECOGNISED_OPTION", name); - } - } - else - { - // we have a command-line value which is represented internally as an option with an empty string as its name - // some very obscure commands do not have values - only options, so allow for that case too - unsigned found = find_definition(""); - if (found < m_definitions.size()) - add_value(found, name, "command line"); - else - result &= m_messages.error("CLI_NO_VALUES", name); - } - } - if (!result) set_invalid(); - return result; - } - - void usage(void) const throw(std::runtime_error) - { - m_messages.information("CLI_USAGE_PROGRAM", m_executable); - m_messages.information("CLI_USAGE_DEFINITIONS"); - for (unsigned d = 0; d < m_definitions.size(); d++) - m_messages.information(m_definitions[d].message()); - if (m_values.size() != 0) - { - m_messages.information("CLI_USAGE_VALUES"); - for (unsigned v = 0; v < m_values.size(); v++) - { - std::string source = m_values[v].m_source; - std::string key = name(v); - if (key.empty()) - { - // command-line values - m_messages.information("CLI_USAGE_VALUE_RESULT", string_value(v), source); - } - else if (kind(v) == cli_switch_kind) - { - // a switch - m_messages.information("CLI_USAGE_SWITCH_RESULT", (switch_value(v) ? name(v) : "no" + name(v)), source); - } - else - { - // other values - std::vector args; - args.push_back(name(v)); - args.push_back(string_value(v)); - args.push_back(source); - m_messages.information("CLI_USAGE_OPTION_RESULT", args); - } - } - } - if (ini_file_size() > 0) - { - m_messages.information("CLI_INI_HEADER"); - for (unsigned i = 0; i < ini_file_size(); i++) - { - if (file_exists(ini_file(i))) - m_messages.information("CLI_INI_FILE_PRESENT", filespec_to_relative_path(ini_file(i))); - else - m_messages.information("CLI_INI_FILE_ABSENT", filespec_to_relative_path(ini_file(i))); - } - } - } - - private: - // make this class uncopyable - cli_parser_data(const cli_parser_data&); - cli_parser_data& operator = (const cli_parser_data&); - }; - - //////////////////////////////////////////////////////////////////////////////// - - cli_parser::cli_parser(message_handler& messages) throw() : - m_data(new cli_parser_data(messages)) - { - } - - cli_parser::cli_parser(cli_parser::definitions_t definitions, message_handler& messages) throw(cli_mode_error) : - m_data(new cli_parser_data(messages)) - { - add_definitions(definitions); - } - - cli_parser::cli_parser(cli_parser::definitions_t definitions, const ini_manager& defaults, const std::string& ini_section, message_handler& messages) throw(cli_mode_error) : - m_data(new cli_parser_data(messages)) - { - add_definitions(definitions); - set_defaults(defaults, ini_section); - } - - cli_parser::cli_parser(char* argv[], cli_parser::definitions_t definitions, message_handler& messages) throw(cli_mode_error,message_handler_id_error,message_handler_format_error) : - m_data(new cli_parser_data(messages)) - { - add_definitions(definitions); - parse(argv); - } - - cli_parser::cli_parser(char* argv[], cli_parser::definitions_t definitions, const ini_manager& defaults, const std::string& ini_section, message_handler& messages) throw(cli_mode_error,message_handler_id_error,message_handler_format_error) : - m_data(new cli_parser_data(messages)) - { - add_definitions(definitions); - set_defaults(defaults, ini_section); - parse(argv); - } - - cli_parser::cli_parser(cli_parser::definitions definitions, message_handler& messages) throw(cli_mode_error) : - m_data(new cli_parser_data(messages)) - { - add_definitions(definitions); - } - - cli_parser::cli_parser(cli_parser::definitions definitions, const ini_manager& defaults, const std::string& ini_section, message_handler& messages) throw(cli_mode_error) : - m_data(new cli_parser_data(messages)) - { - add_definitions(definitions); - set_defaults(defaults, ini_section); - } - - cli_parser::cli_parser(char* argv[], cli_parser::definitions definitions, message_handler& messages) throw(cli_mode_error,message_handler_id_error,message_handler_format_error) : - m_data(new cli_parser_data(messages)) - { - add_definitions(definitions); - parse(argv); - } - - cli_parser::cli_parser(char* argv[], cli_parser::definitions definitions, const ini_manager& defaults, const std::string& ini_section, message_handler& messages) throw(cli_mode_error,message_handler_id_error,message_handler_format_error) : - m_data(new cli_parser_data(messages)) - { - add_definitions(definitions); - set_defaults(defaults, ini_section); - parse(argv); - } - - cli_parser::~cli_parser(void) throw() - { - } - - void cli_parser::add_definitions(cli_parser::definitions_t definitions) throw(cli_mode_error) - { - m_data->clear_definitions(); - // the definitions array is terminated by a definition with a null name pointer - for (unsigned i = 0; definitions[i].m_name; i++) - add_definition(definitions[i]); - } - - unsigned cli_parser::add_definition(const cli_parser::definition_t& definition) throw(cli_mode_error,cli_argument_error) - { - std::string name = definition.m_name ? definition.m_name : ""; - std::string message = definition.m_message ? definition.m_message : ""; - std::string value = definition.m_default ? definition.m_default : ""; - return add_definition(cli_parser::definition(name, definition.m_kind, definition.m_mode, message, value)); - } - - void cli_parser::add_definitions(cli_parser::definitions definitions) throw(cli_mode_error) - { - m_data->clear_definitions(); - for (unsigned i = 0; i < definitions.size(); i++) - add_definition(definitions[i]); - } - - unsigned cli_parser::add_definition(const cli_parser::definition& definition) throw(cli_mode_error) - { - return m_data->add_checked_definition(definition); - } - - void cli_parser::set_defaults(const ini_manager& defaults, const std::string& ini_section) throw() - { - m_data->set_defaults(defaults, ini_section); - } - - bool cli_parser::parse(char* argv[]) throw(cli_argument_error,message_handler_id_error,message_handler_format_error) - { - return m_data->parse(argv); - } - - bool cli_parser::valid(void) throw() - { - return m_data->valid(); - } - - unsigned cli_parser::size(void) const throw() - { - return m_data->m_values.size(); - } - - std::string cli_parser::name(unsigned i) const throw(cli_index_error) - { - return m_data->name(i); - } - - unsigned cli_parser::id(unsigned i) const throw(cli_index_error) - { - return m_data->id(i); - } - - cli_parser::kind_t cli_parser::kind(unsigned i) const throw(cli_index_error) - { - return m_data->kind(i); - } - - bool cli_parser::switch_kind(unsigned i) const throw(cli_index_error) - { - return kind(i) == cli_switch_kind; - } - - bool cli_parser::value_kind(unsigned i) const throw(cli_index_error) - { - return kind(i) == cli_value_kind; - } - - cli_parser::mode_t cli_parser::mode(unsigned i) const throw(cli_index_error) - { - return m_data->mode(i); - } - - bool cli_parser::single_mode(unsigned i) const throw(cli_index_error) - { - return mode(i) == cli_single_mode; - } - - bool cli_parser::multiple_mode(unsigned i) const throw(cli_index_error) - { - return mode(i) == cli_multiple_mode; - } - - bool cli_parser::cumulative_mode(unsigned i) const throw(cli_index_error) - { - return mode(i) == cli_cumulative_mode; - } - - bool cli_parser::switch_value(unsigned i) const throw(cli_mode_error,cli_index_error) - { - return m_data->switch_value(i); - } - - std::string cli_parser::string_value(unsigned i) const throw(cli_mode_error,cli_index_error) - { - return m_data->string_value(i); - } - - //////////////////////////////////////////////////////////////////////////////// - - void cli_parser::usage(void) const throw(std::runtime_error) - { - m_data->usage(); - } - - //////////////////////////////////////////////////////////////////////////////// - -} // end namespace stlplus diff --git a/src/stlplus/subsystems/cli_parser.hpp b/src/stlplus/subsystems/cli_parser.hpp deleted file mode 100644 index dc9740d..0000000 --- a/src/stlplus/subsystems/cli_parser.hpp +++ /dev/null @@ -1,309 +0,0 @@ -#ifndef STLPLUS_CLI_PARSER -#define STLPLUS_CLI_PARSER -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// A subsystem for managing command-line parsing, including using INI files to -// control the default options. - -//////////////////////////////////////////////////////////////////////////////// -#include "subsystems_fixes.hpp" -#include "message_handler.hpp" -#include "ini_manager.hpp" -#include "smart_ptr.hpp" -#include -#include - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - // Internals - - class cli_parser_data; - - //////////////////////////////////////////////////////////////////////////////// - // declarations - - // enum to define the basic behaviour of an argument - // - a switch is an option with no value but which can be switched on or off e.g. -help and -nohelp - // - a value is an option followed by a value e.g. -output results.txt - // (a default value can be removed by using the option as a negated switch e.g. -nooutput) - // - command-line values (i.e. any strings not preceded by '-') are treated - // internally as an option with no name and must be values - enum cli_kind_t {cli_switch_kind, cli_value_kind}; - - // the mode controls the behaviour if an option appears more than once in either the command-line or the ini files - // - a single mode option overrides all previous values so will only be found once in the parsed result - // - a multiple mode option can be repeated to define multiple values, but overrides values from ini files - // - a cumulative mode option is a multiple mode option which keeps ini file values as well - enum cli_mode_t {cli_single_mode, cli_multiple_mode, cli_cumulative_mode}; - - // There are two structures used for defining command-line parameters - // (1) a C struct which is used in a C array - this is used for declaring - // command-line parameters in a static declaration - // (2) a C++ class which is used in an STL vector - this is used for building - // command-line parameters within code - - // The C struct for definitions - struct cli_definition_t - { - // the name of the option, e.g. "help" - const char* m_name; - - // the kind of the option, e.g. cli_switch_kind - cli_kind_t m_kind; - - // the mode e.g. cli_single_mode - cli_mode_t m_mode; - - // the mnemonic for the message giving usage information for this option - const char* m_message; - - // built-in default value - null if not present - const char* m_default; - }; - - // The C array of the C struct. The array must be terminated by END_CLI_DEFINITIONS. - typedef cli_definition_t cli_definitions_t []; -#define END_CLI_DEFINITIONS {0,stlplus::cli_switch_kind,stlplus::cli_single_mode,"",0} - - // The C++ class for definitions - class cli_definition - { - public: - // constructor that allows a definition to be created in one line - cli_definition(const std::string& name, cli_kind_t kind, cli_mode_t mode, - const std::string& message, const std::string& default_value = std::string()) : - m_name(name), m_kind(kind), m_mode(mode), m_message(message), m_default(default_value) {} - - // the name of the option, e.g. "help" - const std::string& name(void) const; - - // the kind of the option, e.g. switch_kind - cli_kind_t kind(void) const; - - // the mode e.g. single_mode - cli_mode_t mode(void) const; - - // the mnemonic for the message giving usage - const std::string& message(void) const; - - // built-in default value - empty string if not present - const std::string& default_value(void) const; - - private: - std::string m_name; - cli_kind_t m_kind; - cli_mode_t m_mode; - std::string m_message; - std::string m_default; - }; - - // The C++ vector of the C++ class - typedef std::vector cli_definitions; - - ////////////////////////////////////////////////////////////////////////////// - // exceptions that can be thrown by the CLI parser - // they are all derivatives of std::logic_error because all errors are predictable by code inspection - // a correct program will never throw an exception - - // thrown if a command-line argument is accessed with the wrong mode - i.e. attempt to get the value of a switch - class cli_mode_error : public std::invalid_argument - { - public: - cli_mode_error(const std::string& arg) : std::invalid_argument(arg) {} - ~cli_mode_error(void) throw() {} - }; - - // similar to std::out_of_range thrown for using an index out of range - class cli_index_error : public std::out_of_range - { - public: - cli_index_error(const std::string& arg) : std::out_of_range(arg) {} - ~cli_index_error(void) throw() {} - }; - - // similar to std::invalid_argument - thrown for passing an illegal argument to a method - class cli_argument_error : public std::invalid_argument - { - public: - cli_argument_error(const std::string& arg) : std::invalid_argument(arg) {} - ~cli_argument_error(void) throw() {} - }; - - //////////////////////////////////////////////////////////////////////////////// - - class cli_parser - { - public: - // Type definitions map the global type names onto convenient scoped names - - typedef cli_kind_t kind_t; - typedef cli_mode_t mode_t; - typedef cli_definition_t definition_t; - typedef cli_definitions_t definitions_t; - typedef cli_definition definition; - typedef cli_definitions definitions; - - //////////////////////////////////////////////////////////////////////////////// - // Methods - - // various constructors - - // you have a choice of either creating an uninitialised CLI parser and then - // calling separate functions to set it up or of calling one of the - // composite constructors. However, you must set up the error handler in the - // constructor. - - // set up the parser with its error handler - // defer everything else - cli_parser(message_handler& errors) - throw(); - - // constructors using the C definitions_t structure - - // set up the parser with the error handler and define all the command-line options - // defer default values and parameter parsing - cli_parser(cli_definitions_t, message_handler& errors) - throw(cli_mode_error); - // set up the parser with the error handler and define all the command-line - // options and their default from the ini files - // defer parameter parsing - cli_parser(cli_definitions_t, const ini_manager& defaults, const std::string& ini_section, message_handler& errors) - throw(cli_mode_error); - // set up the parser with the error handler and define all the command-line - // options no ini files used for default values, so only built-in defaults - // supported then parse the command line - cli_parser(char* argv[], cli_definitions_t, message_handler& errors) - throw(cli_mode_error,message_handler_id_error,message_handler_format_error); - // set up the parser with the error handler and define all the command-line - // options and their default from the ini files then parse the command line - cli_parser(char* argv[], cli_definitions_t, const ini_manager& defaults, const std::string& ini_section, message_handler& errors) - throw(cli_mode_error,message_handler_id_error,message_handler_format_error); - - // constructors using the C++ definitions structure - - // set up the parser with the error handler and define all the command-line - // options from a C array of structs - // defer default values and parameter parsing - cli_parser(cli_definitions, message_handler& errors) - throw(cli_mode_error); - // set up the parser with the error handler and define all the command-line - // options and their default from the ini files - // defer parameter parsing - cli_parser(cli_definitions, const ini_manager& defaults, const std::string& ini_section, message_handler& errors) - throw(cli_mode_error); - // set up the parser with the error handler and define all the command-line - // options no ini files used for default values, so only built-in defaults - // supported then parse the command line - cli_parser(char* argv[], cli_definitions, message_handler& errors) - throw(cli_mode_error,message_handler_id_error,message_handler_format_error); - // set up the parser with the error handler and define all the command-line - // options and their default from the ini files then parse the command line - cli_parser(char* argv[], cli_definitions, const ini_manager& defaults, const std::string& ini_section, message_handler& errors) - throw(cli_mode_error,message_handler_id_error,message_handler_format_error); - - ~cli_parser(void) - throw(); - - // the separate functions for initialising the parser in steps. These are - // declared in the order of use. Firts, add definitions of command-line - // arguments. Then optionally load default values from ini files, then - // finally parse the command line. - - // add a set of C definitions. The definitions will be given ID codes from 0 - // to the number of elements - 1 in the array - void add_definitions(cli_definitions_t) - throw(cli_mode_error); - // add a single C definition, returning the ID code for it - unsigned add_definition(const definition_t&) - throw(cli_mode_error,cli_argument_error); - // add a set of C++ definitions. The definitions will be given ID codes from - // 0 to the number of elements - 1 in the array - void add_definitions(cli_definitions) - throw(cli_mode_error); - // add a single C++ definition, returning the ID code for it - unsigned add_definition(const definition&) - throw(cli_mode_error); - - // All definitions have an optional built-in default value which is stored - // in the definition types above. However, these can optionally be - // overridden by a value from an ini file. If you want this functionality, - // call this function. If you don't want ini file handling, simply don't - // call it. The values will be searched for only in the named section of the - // ini file (sections are labelled by e.g. [vassemble]), so in this case you - // would specify the section name as "vassemble" (exclude the brackets). - void set_defaults(const ini_manager& defaults, const std::string& ini_section) - throw(); - - // the final stage of initialisation is to read the command-line and extract - // the values from it. If parse errors are found, this will report the - // errors using the error handler and return false. - bool parse(char* argv[]) - throw(cli_argument_error,message_handler_id_error,message_handler_format_error); - - // test for whether the CLI parser is still valid (no errors have happened) - // after the initialisation phase - bool valid(void) - throw(); - - // iteration functions avoiding the use of iterators. Just loop through the - // arguments from 0 to size()-1 and use the index of the loop to interrogate - // the command-line for the value at that position. - - // the number of values to read, indexed 0 to size()-1 - unsigned size(void) const - throw(); - - // the argument name - std::string name(unsigned i) const - throw(cli_index_error); - // the argument ID, that is, the offset into the original definitions - unsigned id(unsigned i) const - throw(cli_index_error); - - // the kind (switch or value) and short-cut tests for the different kinds - cli_kind_t kind(unsigned i) const - throw(cli_index_error); - bool switch_kind(unsigned i) const - throw(cli_index_error); - bool value_kind(unsigned i) const - throw(cli_index_error); - - // the mode (single, multiple, cumulative) and short-cut tests for the - // different modes - you rarely need to know this since it mainly controls - // the parsing - cli_mode_t mode(unsigned i) const - throw(cli_index_error); - bool single_mode(unsigned i) const - throw(cli_index_error); - bool multiple_mode(unsigned i) const - throw(cli_index_error); - bool cumulative_mode(unsigned i) const - throw(cli_index_error); - - // get the switch's value, but only if the value is of switch kind - bool switch_value(unsigned i) const - throw(cli_mode_error,cli_index_error); - - // get the option's value, but only if it is of value kind - std::string string_value(unsigned i) const - throw(cli_mode_error,cli_index_error); - - // print the usage report - typically in response to the -help switch being on - void usage(void) const - throw(std::runtime_error); - - private: - friend class cli_parser_data; - smart_ptr_nocopy m_data; - }; - -} // end namespace stlplus - -#endif diff --git a/src/stlplus/subsystems/ini_manager.cpp b/src/stlplus/subsystems/ini_manager.cpp deleted file mode 100644 index 41f5b69..0000000 --- a/src/stlplus/subsystems/ini_manager.cpp +++ /dev/null @@ -1,1243 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// -#include "ini_manager.hpp" -#include "file_system.hpp" -#include -#include -#include -#include -#include -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - // local utilities - - static std::string trim(const std::string& val) - { - std::string result = val; - while (!result.empty() && isspace(result[0])) - result.erase(result.begin()); - while (!result.empty() && isspace(result[result.size()-1])) - result.erase(result.end()-1); - return result; - } - - //////////////////////////////////////////////////////////////////////////////// - // internal data structure for storing a single entry (i.e. line) from an ini file - // lines are categorised as blanks, comments or variables - // TODO - do I need an error category? - //////////////////////////////////////////////////////////////////////////////// - - class ini_entry - { - public: - enum kind_t {BLANK, COMMENT, VARIABLE}; - friend std::string to_string(kind_t kind) - { - switch(kind) - { - case BLANK: return "BLANK"; - case COMMENT: return "COMMENT"; - case VARIABLE: return "VARIABLE"; - } - return "<*unknown kind*>"; - } - - private: - unsigned m_line; - kind_t m_kind; - std::string m_text; - std::string m_name; - std::string m_value; - - public: - ini_entry(unsigned line) : m_line(line), m_kind(BLANK) {} - ini_entry(unsigned line, const std::string& comment) : m_line(line), m_kind(COMMENT), m_text("; " + comment) {} - ini_entry(unsigned line, const std::string& name, const std::string& value) : m_line(line), m_kind(VARIABLE), m_text(name + " = " + value), m_name(name), m_value(value) {} - ~ini_entry(void) {} - - unsigned line(void) const {return m_line;} - kind_t kind(void) const {return m_kind;} - bool blank(void) const {return m_kind == BLANK;} - bool comment(void) const {return m_kind == COMMENT;} - bool variable(void) const {return m_kind == VARIABLE;} - - const std::string& text(void) const {return m_text;} - const std::string& variable_name(void) const {return m_name;} - const std::string& variable_value(void) const {return m_value;} - - bool print(std::ostream& str) const - { - str << " " << m_line << ":" << to_string(m_kind) << ": " << m_text << std::endl; - return !str.fail(); - } - }; - - //////////////////////////////////////////////////////////////////////////////// - // internal data structure representing an ini file section containing all the - // entries for that section from a single ini file - //////////////////////////////////////////////////////////////////////////////// - - class ini_section - { - private: - friend class ini_file; - std::string m_title; - std::list m_entries; - - public: - ini_section(const std::string& title) : - m_title(title) - { - } - - ~ini_section(void) - { - } - - const std::string& title(void) const - { - return m_title; - } - - bool empty(void) const - { - // a section is empty if it contains no variables - for (std::list::const_iterator i = m_entries.begin(); i != m_entries.end(); i++) - { - if (i->variable()) - return false; - } - return true; - } - - void clear(void) - { - m_entries.clear(); - } - - bool variable_exists(const std::string variable) const - { - for (std::list::const_iterator i = m_entries.begin(); i != m_entries.end(); i++) - { - if (i->variable() && i->variable_name() == variable) - return true; - } - return false; - } - - unsigned variables_size(void) const - { - unsigned result = 0; - for (std::list::const_iterator i = m_entries.begin(); i != m_entries.end(); i++) - if (i->variable()) - result++; - return result; - } - - std::string variable_name(unsigned offset) const - { - unsigned j = 0; - for (std::list::const_iterator i = m_entries.begin(); i != m_entries.end(); i++) - { - if (i->variable()) - { - if (j == offset) - return i->variable_name(); - j++; - } - } - return std::string(); - } - - std::vector variable_names(void) const - { - std::vector result; - for (std::list::const_iterator i = m_entries.begin(); i != m_entries.end(); i++) - if (i->variable()) - result.push_back(i->variable_name()); - return result; - } - - std::string variable_value(unsigned offset) const - { - unsigned j = 0; - for (std::list::const_iterator i = m_entries.begin(); i != m_entries.end(); i++) - { - if (i->variable()) - { - if (j == offset) - return i->variable_value(); - j++; - } - } - return std::string(); - } - - std::string variable_value(const std::string variable) const - { - for (std::list::const_iterator i = m_entries.begin(); i != m_entries.end(); i++) - { - if (i->variable() && i->variable_name() == variable) - return i->variable_value(); - } - return std::string(); - } - - unsigned variable_line(const std::string variable) const - { - for (std::list::const_iterator i = m_entries.begin(); i != m_entries.end(); i++) - { - if (i->variable() && i->variable_name() == variable) - return i->line(); - } - return 0; - } - - bool add_variable(unsigned line, const std::string& variable, const std::string& value) - { - erase_variable(variable); - m_entries.push_back(ini_entry(line ? line : m_entries.size(), variable, value)); - return true; - } - - bool add_comment(unsigned line, const std::string& comment) - { - m_entries.push_back(ini_entry(line ? line : m_entries.size(), comment)); - return true; - } - - bool add_blank(unsigned line) - { - m_entries.push_back(ini_entry(line ? line : m_entries.size())); - return true; - } - - bool erase_variable(const std::string& variable) - { - for (std::list::iterator i = m_entries.begin(); i != m_entries.end(); i++) - { - if (i->variable() && i->variable_name() == variable) - { - m_entries.erase(i); - return true; - } - } - return false; - } - - bool print(std::ostream& str) const - { - str << " [" << m_title << "]" << std::endl; - for (std::list::const_iterator entry = m_entries.begin(); entry != m_entries.end(); entry++) - entry->print(str); - return !str.fail(); - } - }; - - //////////////////////////////////////////////////////////////////////////////// - // internal data structure representing a single ini file - //////////////////////////////////////////////////////////////////////////////// - - class ini_file - { - private: - friend class ini_section; - std::string m_filename; - bool m_changed; - bool m_writable; - std::list m_sections; - - std::list::const_iterator find_section(const std::string& section) const - { - for (std::list::const_iterator i = m_sections.begin(); i != m_sections.end(); i++) - { - if (i->title() == section) - return i; - } - return m_sections.end(); - } - - std::list::iterator find_section(const std::string& section) - { - for (std::list::iterator i = m_sections.begin(); i != m_sections.end(); i++) - { - if (i->title() == section) - return i; - } - return m_sections.end(); - } - - public: - - ini_file(void) : m_changed(false), m_writable(false) - { - } - - ini_file(const std::string& filename) : m_changed(false), m_writable(false) - { - read_file(filename); - } - - ~ini_file(void) - { - if (writable()) - save_file(); - } - - bool initialised(void) const - { - return !m_filename.empty(); - } - - bool writable(void) const - { - return m_writable; - } - - bool read_file(const std::string& filename) - { - m_filename = filename; - m_changed = false; - // file may not yet exist - possible to create an Ini file using this class - // so it is writable if the file exists and is writable or if the folder is writable - m_writable = file_writable(m_filename); - if (!file_exists(m_filename)) - return true; - // create a dummy top section to hold file comments - std::list::iterator section = m_sections.insert(m_sections.end(), ini_section("")); - std::ifstream source(m_filename.c_str()); - unsigned line_number = 0; - std::string line; - while(std::getline(source,line)) - { - line_number++; - unsigned i = 0; - while(i < line.size() && isspace(line[i])) - i++; - if (i < line.size() && line[i] == '[') - { - // found a section title - // skip the [ - i++; - // get the text up to the end ] or the end of the line - std::string title; - while (i < line.size() && line[i] != ']') - title += line[i++]; - // create a new section and make it the current section - section = m_sections.insert(m_sections.end(), ini_section(title)); - } - else if (i < line.size() && line[i] == ';') - { - // found a comment - // skip the ; because that is not part of the comment - i++; - // add the rest of the line as a comment to the current section - section->add_comment(line_number, line.substr(i, line.size()-1)); - } - else if (i == line.size()) - { - // found a blank line - section->add_blank(line_number); - } - else - { - // found a *possible* variable - now scan forwards for the = operator - std::string name; - while (i < line.size() && line[i] != '=') - name += line[i++]; - // skip the = sign - // TODO - detect a missing = sign here and convert to an error - if (i < line.size()) - i++; - // trim trailing whitespace off the name - name = trim(name); - // now get the value, minus any leading whitespace - std::string value; - while(i < line.size() && isspace(line[i])) - i++; - while (i < line.size()) - value += line[i++]; - // trim trailing whitespace off the value - value = trim(value); - // and finally add the name/value pair to the data structure - section->add_variable(line_number, name, value); - } - } - return true; - } - - bool save_file(void) - { - if (!initialised()) return false; - if (!m_changed) return true; - if (!m_writable) return false; - std::ofstream output(m_filename.c_str()); - for (std::list::iterator section = m_sections.begin(); section != m_sections.end(); section++) - { - if (!(section->title().empty())) - output << "[" << section->title() << "]" << std::endl; - for (std::list::iterator entry = section->m_entries.begin(); entry != section->m_entries.end(); entry++) - output << entry->text() << std::endl; - } - m_changed = false; - return true; - } - - std::string filename(void) const - { - return m_filename; - } - - bool empty(void) const - { - // file is empty if it has either no sections or an empty header section - if (m_sections.empty()) return true; - if ((m_sections.begin() == --m_sections.end()) && m_sections.begin()->empty()) return true; - return false; - } - - bool section_exists(const std::string& title) const - { - return find_section(title) != m_sections.end(); - } - - bool add_section(const std::string& section) - { - if (!m_writable) return false; - m_sections.push_back(ini_section(section)); - m_changed = true; - return true; - } - - bool empty_section(const std::string& section) - { - std::list::iterator found = find_section(section); - if (found == m_sections.end()) return false; - return found->empty(); - } - - bool erase_section(const std::string& section) - { - if (!m_writable) return false; - std::list::iterator found = find_section(section); - if (found == m_sections.end()) return false; - m_sections.erase(found); - m_changed = true; - return true; - } - - bool clear_section(const std::string& section) - { - if (!m_writable) return false; - std::list::iterator found = find_section(section); - if (found == m_sections.end()) return false; - found->clear(); - m_changed = true; - return true; - } - - unsigned sections_size(void) const - { - return m_sections.size(); - } - - const std::string& section_name(unsigned i) const - { - std::list::const_iterator result = m_sections.begin(); - for (unsigned j = 1; j <= i; j++) - result++; - return result->title(); - } - - std::vector section_names(void) const - { - std::vector result; - for (unsigned j = 0; j < sections_size(); j++) - result.push_back(section_name(j)); - return result; - } - - bool variable_exists(const std::string& section, const std::string variable) const - { - std::list::const_iterator found = find_section(section); - if (found == m_sections.end()) return false; - return found->variable_exists(variable); - } - - std::vector variable_names(const std::string& section) const - { - std::list::const_iterator found = find_section(section); - if (found == m_sections.end()) return std::vector(); - return found->variable_names(); - } - - unsigned variables_size(const std::string& section) const - { - std::list::const_iterator found = find_section(section); - if (found == m_sections.end()) return 0; - return found->variables_size(); - } - - unsigned variable_line(const std::string& section, const std::string variable) const - { - std::list::const_iterator found = find_section(section); - if (found == m_sections.end()) return 0; - return found->variable_line(variable); - } - - std::string variable_name(const std::string& section, unsigned i) const - { - std::list::const_iterator found = find_section(section); - if (found == m_sections.end()) return std::string(); - return found->variable_name(i); - } - - std::string variable_value(const std::string& section, unsigned i) const - { - std::list::const_iterator found = find_section(section); - if (found == m_sections.end()) return std::string(); - return found->variable_value(i); - } - - std::string variable_value(const std::string& section, const std::string variable) const - { - std::list::const_iterator found = find_section(section); - if (found == m_sections.end()) return std::string(); - return found->variable_value(variable); - } - - bool add_variable(const std::string& section, const std::string& variable, const std::string& value) - { - if (!m_writable) return false; - std::list::iterator found = find_section(section); - if (found == m_sections.end()) found = m_sections.insert(m_sections.end(),ini_section(section)); - if (found->add_variable(0,variable,value)) - m_changed = true; - return true; - } - - bool add_comment(const std::string& section, const std::string& comment) - { - if (!m_writable) return false; - std::list::iterator found = find_section(section); - if (found == m_sections.end()) found = m_sections.insert(m_sections.end(),ini_section(section)); - if (found->add_comment(0,comment)) - m_changed = true; - return true; - } - - bool add_blank(const std::string& section) - { - if (!m_writable) return false; - std::list::iterator found = find_section(section); - if (found == m_sections.end()) found = m_sections.insert(m_sections.end(),ini_section(section)); - if (found->add_blank(0)) - m_changed = true; - return true; - } - - bool erase_variable(const std::string& section, const std::string& variable) - { - if (!m_writable) return false; - std::list::iterator found = find_section(section); - if (found == m_sections.end()) return false; - if (found->erase_variable(variable)) - { - m_changed = true; - return true; - } - return false; - } - - bool print(std::ostream& str) const - { - str << "file: " << m_filename << std::endl; - for (std::list::const_iterator section = m_sections.begin(); section != m_sections.end(); section++) - section->print(str); - return !str.fail(); - } - }; - - //////////////////////////////////////////////////////////////////////////////// - // body data structure contains all the data and is pointed-to by exported data structure - - class ini_manager_body - { - private: - std::vector m_files; - unsigned m_count; - - ini_manager_body(const ini_manager_body&); - ini_manager_body& operator= (const ini_manager_body&); - - public: - - ini_manager_body(void) : m_count(1) - { - } - - ~ini_manager_body(void) - { - save(); - } - - void increment(void) - { - ++m_count; - } - - bool decrement(void) - { - --m_count; - return m_count == 0; - } - - ////////////////////////////////////////////////////////////////////////////// - // file management - - // add files starting with the most local file (e.g. the current project) which has depth 0 - // and working back to the most global (e.g. the installation settings) which has a depth of size()-1 - // This does nothing if the file has already been loaded - it is not permitted to manage the same file twice. - // Returns true if the file loaded okay or was already loaded (it is counted as successful if the file did - // not exist, only read errors cause a failure) - bool add_file(const std::string& filename) - { - // if this file has been loaded, don't load it again - // this is not an error - for (unsigned i = 0; i < m_files.size(); i++) - { - if (filespec_to_path(filename) == filespec_to_path(m_files[i].filename())) - return true; - } - // add a new file to the back of the list and then load it - // I do it in two steps rather than passing the filename to the constructor in order to return the load status - m_files.push_back(ini_file()); - return m_files.back().read_file(filename); - } - - // as above, returns false if *none* of the files were added - // filenames[0] is the local file, and so on - bool add_files(const std::vector& filenames) - { - bool result = true; - for (unsigned i = 0; i < filenames.size(); i++) - result &= add_file(filenames[i]); - return result; - } - - // saves modified ini files - returns true if all modified files were written successfully - bool save(void) - { - bool result = true; - for (unsigned i = 0; i < m_files.size(); i++) - result &= m_files[i].save_file(); - return result; - } - - // get the number of files being managed - unsigned size(void) const - { - return m_files.size(); - } - - // get the ini filename associated with a depth - std::string filename(unsigned depth) const - { - return m_files[depth].filename(); - } - - // test whether a file in the ini manager is writable - bool writable(unsigned depth) const - { - return m_files[depth].writable(); - } - - // test whether a file is empty - // An ini file is considered empty if it has no named sections and the header is empty or missing - bool empty(unsigned depth) const - { - return m_files[depth].empty(); - } - - // erase the ini file from the ini manager and from the disk - bool erase(unsigned depth) - { - std::string file = filename(depth); - remove(depth); - return file_delete(file); - } - - // remove the file from the ini manager but do not erase it from the disk - bool remove(unsigned depth) - { - if (m_files[depth].writable()) - m_files[depth].save_file(); - m_files.erase(m_files.begin() + depth); - return true; - } - - ////////////////////////////////////////////////////////////////////////////// - // section management - - // returns the union of all section names in all of the ini files - std::vector section_names(void) const - { - std::vector result; - for (unsigned i = 0; i < m_files.size(); i++) - { - std::vector file_result = section_names(i); - for (unsigned j = 0; j < file_result.size(); j++) - { - if (std::find(result.begin(), result.end(), file_result[j]) == result.end()) - result.push_back(file_result[j]); - } - } - return result; - } - - // returns the section names in one of the ini files - std::vector section_names(unsigned depth) const - { - return m_files[depth].section_names(); - } - - // tests whether a section is found in any of the ini files - bool section_exists(const std::string& title) const - { - for (unsigned i = 0; i < m_files.size(); i++) - { - if (m_files[i].section_exists(title)) - return true; - } - return false; - } - - // tests whether the section is found in the specific ini file - bool section_exists(const std::string& title, unsigned depth) const - { - return m_files[depth].section_exists(title); - } - - // adds a section to the specified ini file - does nothing if it is already present - bool add_section(const std::string& section, unsigned depth) - { - return m_files[depth].add_section(section); - } - - // test whether a section is empty - bool empty_section(const std::string& section, unsigned depth) - { - return m_files[depth].empty_section(section); - } - - // removes a section from the specified ini file if it exists there but cannot remove it from any other file - bool erase_section(const std::string& section, unsigned depth) - { - return m_files[depth].erase_section(section); - } - - // removes all the contents of a section from the specified ini file but keeps the empty section - bool clear_section(const std::string& section, unsigned depth) - { - return m_files[depth].clear_section(section); - } - - ////////////////////////////////////////////////////////////////////////////// - // variable management - - // test whether a variable exists in any of the ini files - bool variable_exists(const std::string& section, const std::string variable) const - { - for (unsigned i = 0; i < m_files.size(); i++) - { - if (variable_exists(section, variable, i)) - return true; - } - return false; - } - - // test whether a variable exists in specified ini file - bool variable_exists(const std::string& section, const std::string variable, unsigned depth) const - { - return m_files[depth].variable_exists(section, variable); - } - - // get the union of all variables declared in all ini files - std::vector variable_names(const std::string& section) const - { - std::vector result; - for (unsigned i = 0; i < m_files.size(); i++) - { - std::vector file_result = variable_names(section, i); - for (unsigned j = 0; j < file_result.size(); j++) - { - if (std::find(result.begin(), result.end(), file_result[j]) == result.end()) - result.push_back(file_result[j]); - } - } - return result; - } - - // get the set of all varaibale names from one file - std::vector variable_names(const std::string& section, unsigned depth) const - { - return m_files[depth].variable_names(section); - } - - // get the depth of the first ini file to define a variable - // returns 0 if defined in the local ini file, etc. Returns (unsigned)-1 if the variable doesn't exist - unsigned variable_depth(const std::string& section, const std::string variable) const - { - for (unsigned i = 0; i < m_files.size(); i++) - { - if (variable_exists(section, variable, i)) - return i; - } - return (unsigned)-1; - } - - // get the filename that first defines the variable - std::string variable_filename(const std::string& section, const std::string variable) const - { - for (unsigned i = 0; i < m_files.size(); i++) - { - if (variable_exists(section, variable, i)) - return filename(i); - } - return std::string(); - } - - unsigned variable_linenumber(const std::string& section, const std::string variable) const - { - for (unsigned i = 0; i < m_files.size(); i++) - { - if (variable_exists(section, variable, i)) - return m_files[i].variable_line(section,variable); - } - return 0; - } - - // get the value of a variable as a single unprocessed string - // if the variable does not exist the string will be empty, but beware that - // you also get an empty string if a variable exists but has no value - // you can differentiate between the two cases by using variable_exists_all above - std::string variable_value(const std::string& section, const std::string variable) const - { - for (unsigned i = 0; i < m_files.size(); i++) - { - if (variable_exists(section, variable, i)) - return variable_value(section, variable, i); - } - return std::string(); - } - - // get the value from the specified file - std::string variable_value(const std::string& section, const std::string variable, unsigned depth) const - { - return m_files[depth].variable_value(section, variable); - } - - // get the value of a variable as a processed string - // processing splits the value at commas and furthermore supports quoted - // strings (so that values can contain commas for example) - // quoted strings are dequoted before they are added to the result - // the result is a vector of dequoted strings, one per value in the comma-separated list - std::vector variable_values(const std::string& section, const std::string variable) const - { - for (unsigned i = 0; i < m_files.size(); i++) - { - if (variable_exists(section, variable, i)) - return variable_values(section, variable, i); - } - return std::vector(); - } - - // get the processed variable from the specified file - std::vector variable_values(const std::string& section, const std::string variable, unsigned depth) const - { - // get the unprocessed value and then do the processing into processed separate values - std::string value = variable_value(section, variable, depth); - std::vector result; - if (!value.empty()) - { - result.push_back(std::string()); - unsigned i = 0; - // loop which is repeated for each element in the comma-separated list - while(i < value.size()) - { - // skip leading whitespace - while (i < value.size() && isspace(value[i])) i++; - // get the value - this means all text up to the next comma or end of line - // also allow escaped characters - while (i < value.size()) - { - if (value[i] == ',') break; - if (value[i] == '\\') - { - // found an escaped character - de-escape it by only getting the next character - // beware of an escape character at the end of the line which just gets ignored - i++; - if (i < value.size()) result.back() += value[i++]; - } - else if (value[i] == '"') - { - // get a quoted substring - // first skip the opening quote - i++; - // keep getting characters until a close-quote, but allow the quote character to be escaped by itself - while (i < value.size()) - { - if (value[i] == '"') - { - // found a quote skip it - i++; - // now establish whether its an escaped quote - // if it is, keep it, but de-escape it by only getting the next character - // it it isn't, break out and continue processing the value as a non-quoted string - if (i < value.size() && value[i] != '"') break; - if (i < value.size()) result.back() += value[i++]; - } - else - { - // non-quote and non-escape so just get the character - result.back() += value[i++]; - } - } - } - else - { - // non-escape so just get the character - result.back() += value[i++]; - } - } - // trim trailing whitespace off the value - while (!result.back().empty() && isspace(result.back()[result.back().size()-1])) result.back().erase(result.back().size()-1,1); - // now check for whether there is a comma or end of line - if (i <= value.size() && value[i] == ',') - { - // skip the comma and add a new string to the result ready for the next iteration - i++; - result.push_back(std::string()); - } - else - { - // end of line found so break out - break; - } - } - } - return result; - } - - // add a variable to the specified file - bool add_variable(const std::string& section, const std::string& variable, const std::string& value, unsigned depth) - { - return m_files[depth].add_variable(section, variable, value); - } - - // add a variable as a processed string - // processing means that the values in the string vector are converted into a comma-separated list - // values containing reserved characters are automatically quoted - so you should not even try to quote them yourself - bool add_variable(const std::string& section, const std::string& variable, const std::vector& values, unsigned depth) - { - // convert the values vector into a comma-separated string with each value escaped so that special characters do not confuse the reader - // the characters escaped are: '\', ',', '"' - std::string value; - for (unsigned v = 0; v < values.size(); v++) - { - const std::string& element = values[v]; - // add the comma between values === add a comma before all but the first value - if (v > 0) value += ','; - for (unsigned i = 0; i < element.size(); i++) - { - // add a character at a time, escaping special characters - if (element[i] == '"' || element[i] == ',' || element[i] == '\\') - { - // escape the character - value += '\\'; - } - value += element[i]; - } - } - return add_variable(section, variable, value, depth); - } - - // erase a variable from the specified file - // this does not remove the variable from other ini files, so the variable may still exist - // to mask a global variable, set the variable to an empty string instead - bool erase_variable(const std::string& section, const std::string& variable, unsigned depth) - { - return m_files[depth].erase_variable(section, variable); - } - - ////////////////////////////////////////////////////////////////////////////// - // sundry line-entry management - - // add a comment to the specified ini file - bool add_comment(const std::string& section, const std::string& comment, unsigned depth) - { - return m_files[depth].add_comment(section, comment); - } - - // add a blank line to the specified ini file - bool add_blank(const std::string& section, unsigned depth) - { - return m_files[depth].add_blank(section); - } - - bool print(std::ostream& str) const - { - str << "----------------------------------------" << std::endl; - for (unsigned depth = 0; depth < m_files.size(); depth++) - { - m_files[depth].print(str); - str << "----------------------------------------" << std::endl; - } - return !str.fail(); - } - }; - - //////////////////////////////////////////////////////////////////////////////// - // exported data structure representing the set of all ini files and providing - // the access functions exported by the class - //////////////////////////////////////////////////////////////////////////////// - - //////////////////////////////////////////////////////////////////////////////// - // constructors/destructors - - ini_manager::ini_manager(void) : m_body(new ini_manager_body) - { - } - - ini_manager::ini_manager(const std::vector& filenames) : m_body(new ini_manager_body) - { - add_files(filenames); - } - - ini_manager::ini_manager(const ini_manager& manager) : m_body(0) - { - m_body = manager.m_body; - m_body->increment(); - } - - ini_manager& ini_manager::operator= (const ini_manager& manager) - { - if (m_body == manager.m_body) return *this; - if (m_body->decrement()) - delete m_body; - m_body = manager.m_body; - m_body->increment(); - return *this; - } - - ini_manager::~ini_manager(void) - { - if (m_body->decrement()) - delete m_body; - } - - //////////////////////////////////////////////////////////////////////////////// - // file management - - bool ini_manager::add_file(const std::string& filename) - { - return m_body->add_file(filename); - } - - bool ini_manager::add_files(const std::vector& filenames) - { - return m_body->add_files(filenames); - } - - bool ini_manager::save(void) - { - return m_body->save(); - } - - unsigned ini_manager::size(void) const - { - return m_body->size(); - } - - std::string ini_manager::filename(unsigned depth) const - { - return m_body->filename(depth); - } - - bool ini_manager::writable(unsigned depth) const - { - return m_body->writable(depth); - } - - bool ini_manager::empty(unsigned depth) const - { - return m_body->empty(depth); - } - - bool ini_manager::erase(unsigned depth) - { - return m_body->erase(depth); - } - - bool ini_manager::remove(unsigned depth) - { - return m_body->remove(depth); - } - - //////////////////////////////////////////////////////////////////////////////// - // section management - - std::vector ini_manager::section_names(void) const - { - return m_body->section_names(); - } - - std::vector ini_manager::section_names(unsigned depth) const - { - return m_body->section_names(depth); - } - - bool ini_manager::section_exists(const std::string& section) const - { - return m_body->section_exists(section); - } - - bool ini_manager::section_exists(const std::string& section, unsigned depth) const - { - return m_body->section_exists(section, depth); - } - - bool ini_manager::add_section(const std::string& section, unsigned depth) - { - return m_body->add_section(section, depth); - } - - bool ini_manager::empty_section(const std::string& section, unsigned depth) - { - return m_body->empty_section(section, depth); - } - - bool ini_manager::erase_section(const std::string& section, unsigned depth) - { - return m_body->erase_section(section, depth); - } - - bool ini_manager::clear_section(const std::string& section, unsigned depth) - { - return m_body->clear_section(section, depth); - } - - //////////////////////////////////////////////////////////////////////////////// - // variable management - - bool ini_manager::variable_exists(const std::string& section, const std::string variable) const - { - return m_body->variable_exists(section, variable); - } - - bool ini_manager::variable_exists(const std::string& section, const std::string variable, unsigned depth) const - { - return m_body->variable_exists(section, variable, depth); - } - - std::vector ini_manager::variable_names(const std::string& section) const - { - return m_body->variable_names(section); - } - - std::vector ini_manager::variable_names(const std::string& section, unsigned depth) const - { - return m_body->variable_names(section, depth); - } - - unsigned ini_manager::variable_depth(const std::string& section, const std::string variable) const - { - return m_body->variable_depth(section, variable); - } - - std::string ini_manager::variable_filename(const std::string& section, const std::string variable) const - { - return m_body->variable_filename(section, variable); - } - - unsigned ini_manager::variable_linenumber(const std::string& section, const std::string variable) const - { - return m_body->variable_linenumber(section, variable); - } - - std::string ini_manager::variable_value(const std::string& section, const std::string variable) const - { - return m_body->variable_value(section, variable); - } - - std::string ini_manager::variable_value(const std::string& section, const std::string variable, unsigned depth) const - { - return m_body->variable_value(section, variable, depth); - } - - std::vector ini_manager::variable_values(const std::string& section, const std::string variable) const - { - return m_body->variable_values(section, variable); - } - - std::vector ini_manager::variable_values(const std::string& section, const std::string variable, unsigned depth) const - { - return m_body->variable_values(section, variable, depth); - } - - bool ini_manager::add_variable(const std::string& section, const std::string& variable, const std::string& value, unsigned depth) - { - return m_body->add_variable(section, variable, value, depth); - } - - bool ini_manager::add_variable(const std::string& section, const std::string& variable, const std::vector& values, unsigned depth) - { - return m_body->add_variable(section, variable, values, depth); - } - - bool ini_manager::erase_variable(const std::string& section, const std::string& variable, unsigned depth) - { - return m_body->erase_variable(section, variable, depth); - } - - - //////////////////////////////////////////////////////////////////////////////// - // sundry entries - - bool ini_manager::add_comment(const std::string& section, const std::string& comment, unsigned depth) - { - return m_body->add_comment(section, comment, depth); - } - - bool ini_manager::add_blank(const std::string& section, unsigned depth) - { - return m_body->add_blank(section, depth); - } - - bool ini_manager::print(std::ostream& str) const - { - return m_body->print(str); - } - - //////////////////////////////////////////////////////////////////////////////// - // diagnostic print - - std::ostream& operator << (std::ostream& str, const ini_manager& manager) - { - manager.print(str); - return str; - } - - //////////////////////////////////////////////////////////////////////////////// - -} // end namespace stlplus diff --git a/src/stlplus/subsystems/ini_manager.hpp b/src/stlplus/subsystems/ini_manager.hpp deleted file mode 100644 index f1d7a66..0000000 --- a/src/stlplus/subsystems/ini_manager.hpp +++ /dev/null @@ -1,201 +0,0 @@ -#ifndef STLPLUS_INI_MANAGER -#define STLPLUS_INI_MANAGER -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// A subsystem for managing INI (i.e. .ini) files -// An INI file has the following format - -// file ::= header { section }* -// header ::= { comment | blank }* -// section ::= section_header { declaration | comment | blank }* -// section_header ::= '[' title ']' '\n' -// declaration ::= variable '=' value '\n' -// comment ::= ';' text '\n' -// blank ::= '\n' -// title ::= [~']']* -// variable ::= [~'=']* -// value ::= .* -// text ::= .* - -// Whitespace is trimmed from the leading and trailing ends of title, variable and value -// Note: a header is represented internally as a Clint section (i.e. a section with no name) - -//////////////////////////////////////////////////////////////////////////////// -#include "subsystems_fixes.hpp" -#include -#include -#include - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - // Internals - - class ini_manager_body; - - //////////////////////////////////////////////////////////////////////////////// - // Ini-file manager class - - class ini_manager - { - public: - - ini_manager(void); - - explicit ini_manager(const std::vector& filenames); - - ini_manager(const ini_manager&); - ini_manager& operator= (const ini_manager&); - - ~ini_manager(void); - - ////////////////////////////////////////////////////////////////////////////// - // file management - - // add files starting with the most local file (e.g. the current project) which has depth 0 - // and working back to the most global (e.g. the installation settings) which has a depth of size()-1 - // This does nothing if the file has already been loaded - it is not permitted to manage the same file twice. - // Returns true if the file loaded okay or was already loaded (it is counted as successful if the file did - // not exist, only read errors cause a failure) - bool add_file(const std::string& filename); - - // as above, returns false if *none* of the files were added - // filenames[0] is the local file, and so on - bool add_files(const std::vector& filenames); - - // saves modified ini files - returns true if all modified files were written successfully - bool save(void); - - // get the number of files being managed - unsigned size(void) const; - - // get the ini filename associated with a depth - std::string filename(unsigned depth = 0) const; - - // test whether a file in the ini manager is writable - bool writable(unsigned depth = 0) const; - - // test whether a file is empty - // An ini file is considered empty if it has no named sections and the header is empty or missing - bool empty(unsigned depth = 0) const; - - // erase the ini file from the ini manager and from the disk - bool erase(unsigned depth = 0); - - // remove the file from the ini manager but do not erase it from the disk - bool remove(unsigned depth = 0); - - ////////////////////////////////////////////////////////////////////////////// - // section management - - // returns the union of all section names in all of the ini files - std::vector section_names(void) const; - - // returns the section names in one of the ini files - std::vector section_names(unsigned depth) const; - - // tests whether a section is found in any of the ini files - bool section_exists(const std::string& title) const; - - // tests whether the section is found in the specific ini file - bool section_exists(const std::string& title, unsigned depth) const; - - // adds a section to the specified ini file - does nothing if it is already present - bool add_section(const std::string& section, unsigned depth = 0); - - // test whether a section is empty - bool empty_section(const std::string& section, unsigned depth = 0); - - // removes a section from the specified ini file if it exists there but cannot remove it from any other file - bool erase_section(const std::string& section, unsigned depth = 0); - - // removes all the contents of a section from the specified ini file but keeps the empty section - bool clear_section(const std::string& section, unsigned depth = 0); - - ////////////////////////////////////////////////////////////////////////////// - // variable management - - // test whether a variable exists in any of the ini files - bool variable_exists(const std::string& section, const std::string variable) const; - - // test whether a variable exists in specified ini file - bool variable_exists(const std::string& section, const std::string variable, unsigned depth) const; - - // get the union of all variables declared in all ini files - std::vector variable_names(const std::string& section) const; - - // get the set of all varaibale names from one file - std::vector variable_names(const std::string& section, unsigned depth) const; - - // get the depth of the first ini file to define a variable - // returns 0 if defined in the local ini file, etc. Returns (unsigned)-1 if the variable doesn't exist - unsigned variable_depth(const std::string& section, const std::string variable) const; - - // get the filename that first defines the variable - std::string variable_filename(const std::string& section, const std::string variable) const; - // ditto for its linenumber within that file - unsigned variable_linenumber(const std::string& section, const std::string variable) const; - - // get the value of a variable as a single unprocessed string - // if the variable does not exist the string will be empty, but beware that - // you also get an empty string if a variable exists but has no value - // you can differentiate between the two cases by using variable_exists_all above - std::string variable_value(const std::string& section, const std::string variable) const; - - // get the value from the specified file - std::string variable_value(const std::string& section, const std::string variable, unsigned depth) const; - - // get the value of a variable as a processed string - // processing splits the value at commas and furthermore supports quoted strings (so that values can contain commas for example) - // quoted strings are dequoted before they are added to the result - // the result is a vector of dequoted strings, one per value in the comma-separated list - std::vector variable_values(const std::string& section, const std::string variable) const; - - // get the processed variable from the specified file - std::vector variable_values(const std::string& section, const std::string variable, unsigned depth) const; - - // add a variable to the specified file - bool add_variable(const std::string& section, const std::string& variable, const std::string& value, unsigned depth = 0); - - // add a variable as a processed string - // processing means that the values in the string vector are converted into a comma-separated list - // values containing reserved characters are automatically quoted - so you should not even try to quote them yourself - bool add_variable(const std::string& section, const std::string& variable, const std::vector& values, unsigned depth = 0); - - // erase a variable from the specified file - // this does not remove the variable from other ini files, so the variable may still exist - // to mask a global variable, set the variable to an empty string instead - bool erase_variable(const std::string& section, const std::string& variable, unsigned depth = 0); - - ////////////////////////////////////////////////////////////////////////////// - // sundry line-entry management - - // add a comment to the specified ini file - bool add_comment(const std::string& section, const std::string& comment, unsigned depth = 0); - - // add a blank line to the specified ini file - bool add_blank(const std::string& section, unsigned depth = 0); - - bool print(std::ostream&) const; - - private: - friend class ini_manager_body; - ini_manager_body* m_body; - }; - - //////////////////////////////////////////////////////////////////////////////// - // diagnostic print routine - - std::ostream& operator << (std::ostream&, const ini_manager&); - - //////////////////////////////////////////////////////////////////////////////// - -} // end namespace stlplus - -#endif diff --git a/src/stlplus/subsystems/library_manager.cpp b/src/stlplus/subsystems/library_manager.cpp deleted file mode 100644 index 0daf1be..0000000 --- a/src/stlplus/subsystems/library_manager.cpp +++ /dev/null @@ -1,2332 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// -#include "library_manager.hpp" -#include "file_system.hpp" -#include -#include -#include - -//////////////////////////////////////////////////////////////////////////////// - -static const char* LibraryNameExtension = "lmn"; -static const char* HeaderExtension = "lmh"; - -//////////////////////////////////////////////////////////////////////////////// -// local operations -//////////////////////////////////////////////////////////////////////////////// - -typedef std::map lm_callback_map; - -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; -} - -// Context file and library operations -// These must be readable/writeable without a library data structure present -// so that the name of the library is known before creation - -static bool read_context(const std::string& path, const std::string& owner, std::string& name, bool& writable) -{ - std::string spec = stlplus::create_filespec(path, owner, LibraryNameExtension); - name = ""; - writable = false; - if (!stlplus::file_exists(spec)) return false; - std::ifstream input(spec.c_str()); - input >> name >> writable; - return !input.fail(); -} - -static bool write_context(const std::string& path, const std::string& owner, const std::string& name, bool writable) -{ - std::string spec = stlplus::create_filespec(path, owner, LibraryNameExtension); - std::ofstream output(spec.c_str()); - output << name << " " << writable << std::endl; - return !output.fail(); -} - -static bool create_library(const std::string& path, const std::string& owner, const std::string& name, bool writable) -{ - std::string spec = stlplus::create_filespec(path, owner, LibraryNameExtension); - if (stlplus::is_present(path) && !stlplus::is_folder(path)) return false; - if (!stlplus::folder_exists(path) && !stlplus::folder_create(path)) return false; - return write_context(path, owner, name, writable); -} - -static bool erase_library(const std::string& path, const std::string& owner) -{ - // check that it is a library before deleting it! - std::string spec = stlplus::create_filespec(path, owner, LibraryNameExtension); - if (!stlplus::file_exists(spec)) return false; - return stlplus::folder_delete(path, true); -} - -// dependency checking - -typedef std::map,stlplus::lm_dependencies> visited_map; - -static stlplus::lm_dependencies& out_of_date_check (visited_map& visited, - const stlplus::library_manager* manager, - const std::string& library, - const stlplus::lm_unit_name& name) -{ - // the visited field contains an entry if a unit has been visited - it also contains the reason for out-of-date-ness - // this is initially set empty (up to date) since the unit has to be added - // before it is checked just in case there is a recursive loop - // the dependencies field is filled in if the unit is subsequently found to be out of date - std::pair full_name = std::make_pair(library,name); - // first check whether this unit has already been visited - this should in - // principle never happen because the same test is performed before trying - // to recurse, so consider this to be paranoid-mode programming - visited_map::iterator found = visited.find(full_name); - if (found != visited.end()) - return found->second; - // now add this unit to prevent recursion loops later. This entry is added - // to when out-of-date dependencies are found and is also the return value - // of the function - visited[full_name] = stlplus::lm_dependencies(); - // now find the unit - const stlplus::lm_unit_ptr unit = manager->find(library,name); - if (!unit) - { - // if a unit is missing it fails with a dependency on itself - this is a - // bit of a work-around, but this again is paranoid-mode programming since - // the calling function (this function calling itself recursively) should - // check the unit's existence before recursing - visited[full_name].unit_add(stlplus::lm_unit_dependency(library,name)); - } - else - { - // we're onto the real checks now - first get the datestamp of this unit: - // all dependencies must be older than this - time_t unit_modified = unit->modified(); - // check dependency on source file if there is one - if (unit->source_file_present()) - { - std::string source_file = unit->source_file().path_full(unit->library_path()); - time_t source_modified = stlplus::file_modified(source_file); - if (source_modified == 0 || source_modified > unit_modified) - { - visited[full_name].set_source_file(unit->source_file()); - } - } - // now check the other file dependencies - for (unsigned i = 0; i < unit->file_size(); i++) - { - // a file dependency is a dependency on a file outside of the library system - const stlplus::lm_file_dependency& dependency = unit->file_dependency(i); - std::string file_full = dependency.path_full(unit->library_path()); - time_t source_modified = stlplus::file_modified(file_full); - if (source_modified == 0 || source_modified > unit_modified) - { - visited[full_name].file_add(dependency); - } - } - // now check and recurse on unit dependencies - for (unsigned j = 0; j < unit->unit_size(); j++) - { - const stlplus::lm_unit_dependency& dependency = unit->unit_dependency(j); - std::pair other_name = std::make_pair(dependency.library(), dependency.unit_name()); - // perform the datestamp checking at this level and only recurse if this does not detect an error - const stlplus::lm_unit_ptr other_unit = manager->find(other_name.first, other_name.second); - if (!other_unit) - { - visited[full_name].unit_add(dependency); - } - else - { - time_t other_modified = other_unit->modified(); - if (other_modified == 0 || other_modified > unit_modified) - { - visited[full_name].unit_add(dependency); - } - else - { - // prevent recursion before doing it - visited_map::iterator other_found = visited.find(other_name); - if (other_found != visited.end()) - { - // if the unit was found to be out of date on the previous visit, add it to the failed dependencies - if (!other_found->second.empty()) - { - visited[full_name].unit_add(dependency); - } - } - else - { - // the unit hasn't been visited before, so recurse on it now - stlplus::lm_dependencies other_dependencies = - out_of_date_check(visited, manager, other_name.first, other_name.second); - if (!other_dependencies.empty()) - { - visited[full_name].unit_add(dependency); - } - } - } - } - } - } - return visited[full_name]; -} - -//////////////////////////////////////////////////////////////////////////////// -// class lm_unit_name - -stlplus::lm_unit_name::lm_unit_name(const std::string& name, const std::string& type) : - m_name(name), m_type(type) -{ -} - -stlplus::lm_unit_name::~lm_unit_name(void) -{ -} - -const std::string& stlplus::lm_unit_name::name(void) const -{ - return m_name; -} - -void stlplus::lm_unit_name::set_name(const std::string& name) -{ - m_name = name; -} - -void stlplus::lm_unit_name::lowercase(void) -{ - m_name = ::lowercase(m_name); -} - -const std::string& stlplus::lm_unit_name::type(void) const -{ - return m_type; -} - -void stlplus::lm_unit_name::set_type(const std::string& type) -{ - m_type = type; -} - -bool stlplus::lm_unit_name::write(std::ostream& context) const -{ - context << m_name << " " << m_type; - return !context.fail(); -} - -bool stlplus::lm_unit_name::read(std::istream& context) -{ - context >> m_name >> m_type; - return !context.fail(); -} - -std::string stlplus::lm_unit_name::to_string(void) const -{ - return m_name + ":" + m_type; -} - -bool stlplus::lm_unit_name::print(std::ostream& str) const -{ - str << to_string(); - return !str.fail(); -} - -std::ostream& stlplus::operator << (std::ostream& str, const stlplus::lm_unit_name& name) -{ - name.print(str); - return str; -} - -bool stlplus::operator == (const stlplus::lm_unit_name& l, const stlplus::lm_unit_name& r) -{ - return l.name() == r.name() && l.type() == r.type(); -} - -bool stlplus::operator < (const stlplus::lm_unit_name& l, const stlplus::lm_unit_name& r) -{ - // sort by name then type - return (l.name() != r.name()) ? l.name() < r.name() : l.type() < r.type(); -} - -//////////////////////////////////////////////////////////////////////////////// -// dependencies - -// file dependencies - -stlplus::lm_file_dependency::lm_file_dependency(void) : m_line(0), m_column(0) -{ -} - -stlplus::lm_file_dependency::lm_file_dependency(const std::string& library_path, const std::string& path, unsigned line, unsigned column) -{ - set_path(library_path, path); - set_line(line); - set_column(column); -} - -stlplus::lm_file_dependency::~lm_file_dependency(void) -{ -} - -const std::string& stlplus::lm_file_dependency::path(void) const -{ - return m_path; -} - -std::string stlplus::lm_file_dependency::path_full(const std::string& library_path) const -{ - return filespec_to_path(library_path, m_path); -} - -void stlplus::lm_file_dependency::set_path(const std::string& library_path, const std::string& path) -{ - m_path = filespec_to_relative_path(library_path, path); -} - -unsigned stlplus::lm_file_dependency::line(void) const -{ - return m_line; -} - -void stlplus::lm_file_dependency::set_line(unsigned line) -{ - m_line = line; -} - -unsigned stlplus::lm_file_dependency::column(void) const -{ - return m_column; -} - -void stlplus::lm_file_dependency::set_column(unsigned column) -{ - m_column = column; -} - -bool stlplus::lm_file_dependency::write(std::ostream& context) const -{ - context << m_path << " " << m_line << " " << m_column; - return !context.fail(); -} - -bool stlplus::lm_file_dependency::read(std::istream& context) -{ - context >> m_path >> m_line >> m_column; - return !context.fail(); -} - -bool stlplus::lm_file_dependency::print(std::ostream& str) const -{ - str << "file: " << m_path; - if (m_line != 0) - str << ":" << m_line << ":" << m_column; - return !str.fail(); -} - -std::ostream& stlplus::operator << (std::ostream& str, const stlplus::lm_file_dependency& dependency) -{ - dependency.print(str); - return str; -} - -// unit dependency - -stlplus::lm_unit_dependency::lm_unit_dependency(void) -{ -} - -stlplus::lm_unit_dependency::lm_unit_dependency(const std::string& library, const lm_unit_name& name) : - m_library(library), m_name(name) -{ -} - -stlplus::lm_unit_dependency::~lm_unit_dependency(void) -{ -} - -const std::string& stlplus::lm_unit_dependency::library(void) const -{ - return m_library; -} - -void stlplus::lm_unit_dependency::set_library(const std::string& library) -{ - m_library = library; -} - -const stlplus::lm_unit_name& stlplus::lm_unit_dependency::unit_name(void) const -{ - return m_name; -} - -void stlplus::lm_unit_dependency::set_unit_name(const lm_unit_name& unit_name) -{ - m_name = unit_name; -} - -const std::string& stlplus::lm_unit_dependency::name(void) const -{ - return m_name.name(); -} - -void stlplus::lm_unit_dependency::set_name(const std::string& name) -{ - m_name.set_name(name); -} - -const std::string& stlplus::lm_unit_dependency::type(void) const -{ - return m_name.type(); -} - -void stlplus::lm_unit_dependency::set_type(const std::string& type) -{ - m_name.set_type(type); -} - -bool stlplus::lm_unit_dependency::write(std::ostream& context) const -{ - context << m_library; - m_name.write(context); - return !context.fail(); -} - -bool stlplus::lm_unit_dependency::read(std::istream& context) -{ - context >> m_library; - m_name.read(context);; - return !context.fail(); -} - -bool stlplus::lm_unit_dependency::print(std::ostream& str) const -{ - str << "unit: " << m_library << "." << m_name; - return !str.fail(); -} - -std::ostream& stlplus::operator << (std::ostream& str, const stlplus::lm_unit_dependency& dependency) -{ - dependency.print(str); - return str; -} - -// dependencies - -stlplus::lm_dependencies::lm_dependencies(void) : m_source(0) -{ -} - -stlplus::lm_dependencies::lm_dependencies(const lm_dependencies& r) : m_source(0) -{ - *this = r; -} - -stlplus::lm_dependencies& stlplus::lm_dependencies::operator=(const stlplus::lm_dependencies& r) -{ - if (m_source) - delete m_source; - m_source = 0; - if (r.m_source) - m_source = new lm_file_dependency(*r.m_source); - m_files = r.m_files; - m_units = r.m_units; - return *this; -} - -stlplus::lm_dependencies::~lm_dependencies(void) -{ - if (m_source) - delete m_source; -} - -void stlplus::lm_dependencies::set_source_file(const lm_file_dependency& source) -{ - if (m_source) - delete m_source; - m_source = new lm_file_dependency(source); -} - -bool stlplus::lm_dependencies::source_file_present(void) const -{ - return (m_source != 0); -} - -const stlplus::lm_file_dependency& stlplus::lm_dependencies::source_file(void) const -{ - return *m_source; -} - -unsigned stlplus::lm_dependencies::file_add(const lm_file_dependency& dependency) -{ - m_files.push_back(dependency); - return m_files.size()-1; -} - -unsigned stlplus::lm_dependencies::file_size(void) const -{ - return m_files.size(); -} - -const stlplus::lm_file_dependency& stlplus::lm_dependencies::file_dependency(unsigned i) const -{ - return m_files[i]; -} - -void stlplus::lm_dependencies::file_erase(unsigned i) -{ - m_files.erase(m_files.begin()+i); -} - -unsigned stlplus::lm_dependencies::unit_add(const lm_unit_dependency& dependency) -{ - m_units.push_back(dependency); - return m_units.size()-1; -} - -unsigned stlplus::lm_dependencies::unit_size(void) const -{ - return m_units.size(); -} - -const stlplus::lm_unit_dependency& stlplus::lm_dependencies::unit_dependency(unsigned i) const -{ - return m_units[i]; -} - -void stlplus::lm_dependencies::unit_erase(unsigned i) -{ - m_units.erase(m_units.begin()+i); -} - -void stlplus::lm_dependencies::clear(void) -{ - if (m_source) - delete m_source; - m_source = 0; - m_files.clear(); - m_units.clear(); -} - -bool stlplus::lm_dependencies::empty(void) const -{ - return (m_source == 0) && m_files.empty() && m_units.empty(); -} - -bool stlplus::lm_dependencies::write(std::ostream& context) const -{ - context << (m_source ? true : false) << " "; - if (m_source) m_source->write(context); - context << std::endl; - context << m_files.size() << std::endl; - for (unsigned i = 0; i < m_files.size(); i++) - { - m_files[i].write(context); - context << std::endl; - } - context << m_units.size() << std::endl; - for (unsigned j = 0; j < m_units.size(); j++) - { - m_units[j].write(context); - context << std::endl; - } - return !context.fail(); -} - -bool stlplus::lm_dependencies::read(std::istream& context) -{ - clear(); - bool source_present = false; - context >> source_present; - if (source_present) - { - m_source = new lm_file_dependency(); - m_source->read(context); - } - unsigned files_size = 0; - context >> files_size; - for (unsigned i = 0; i < files_size; i++) - { - m_files.push_back(lm_file_dependency()); - m_files.back().read(context); - } - unsigned units_size = 0; - context >> units_size; - for (unsigned j = 0; j < units_size; j++) - { - m_units.push_back(lm_unit_dependency()); - m_units.back().read(context); - } - return !context.fail(); -} - -bool stlplus::lm_dependencies::print(std::ostream& str) const -{ - if (m_source) - str << " source file: " << *m_source << std::endl; - for (unsigned i = 0; i < m_files.size(); i++) - str << " " << m_files[i] << std::endl; - for (unsigned j = 0; j < m_units.size(); j++) - str << " " << m_units[j] << std::endl; - return !str.fail(); -} - -std::ostream& stlplus::operator << (std::ostream& str, const stlplus::lm_dependencies& dependencies) -{ - dependencies.print(str); - return str; -} - -//////////////////////////////////////////////////////////////////////////////// -// lm_unit -//////////////////////////////////////////////////////////////////////////////// - -stlplus::lm_unit::lm_unit(const lm_unit_name& name, lm_library* library) : - m_name(name), m_header_modified(false), m_loaded(false), m_marked(false), m_library(library), m_error(false) -{ - read_header(); -} - -stlplus::lm_unit::~lm_unit(void) -{ - write_header(); -} - -//////////////////////////////////////// -// Header data - -const stlplus::lm_unit_name& stlplus::lm_unit::unit_name(void) const -{ - return m_name; -} - -const std::string& stlplus::lm_unit::name(void) const -{ - return m_name.name(); -} - -const std::string& stlplus::lm_unit::type(void) const -{ - return m_name.type(); -} - -// dependencies - -// source file dependency - -void stlplus::lm_unit::set_source_file(const lm_file_dependency& dependency) -{ - m_header_modified = true; - m_dependencies.set_source_file(dependency); -} - -bool stlplus::lm_unit::source_file_present(void) const -{ - return m_dependencies.source_file_present(); -} - -const stlplus::lm_file_dependency& stlplus::lm_unit::source_file(void) const -{ - return m_dependencies.source_file(); -} - -// other file dependencies - -unsigned stlplus::lm_unit::file_add(const lm_file_dependency& dependency) -{ - m_header_modified = true; - return m_dependencies.file_add(dependency); -} - -unsigned stlplus::lm_unit::file_size(void) const -{ - return m_dependencies.file_size(); -} - -const stlplus::lm_file_dependency& stlplus::lm_unit::file_dependency(unsigned i) const -{ - return m_dependencies.file_dependency(i); -} - -void stlplus::lm_unit::file_erase(unsigned i) -{ - m_header_modified = true; - m_dependencies.file_erase(i); -} - -// unit dependencies - -unsigned stlplus::lm_unit::unit_add(const lm_unit_dependency& dependency) -{ - m_header_modified = true; - return m_dependencies.unit_add(dependency); -} - -unsigned stlplus::lm_unit::unit_size(void) const -{ - return m_dependencies.unit_size(); -} - -const stlplus::lm_unit_dependency& stlplus::lm_unit::unit_dependency(unsigned i) const -{ - return m_dependencies.unit_dependency(i); -} - -void stlplus::lm_unit::unit_erase(unsigned i) -{ - m_header_modified = true; - m_dependencies.unit_erase(i); -} - -const stlplus::lm_dependencies& stlplus::lm_unit::dependencies(void) const -{ - return m_dependencies; -} - -void stlplus::lm_unit::set_dependencies(const lm_dependencies& dependencies) -{ - m_header_modified = true; - m_dependencies = dependencies; -} - -void stlplus::lm_unit::clear_dependencies(void) -{ - m_header_modified = true; - m_dependencies.clear(); -} - -bool stlplus::lm_unit::empty_dependencies(void) const -{ - return m_dependencies.empty(); -} - -// dependency checking - -bool stlplus::lm_unit::out_of_date(void) const -{ - return m_library->out_of_date(m_name); -} - -bool stlplus::lm_unit::up_to_date(void) const -{ - return m_library->up_to_date(m_name); -} - -stlplus::lm_dependencies stlplus::lm_unit::out_of_date_reason(void) const -{ - return m_library->out_of_date_reason(m_name); -} - -// supplementary data - -const std::string& stlplus::lm_unit::supplementary_data(void) const -{ - return m_supplement; -} - -void stlplus::lm_unit::set_supplementary_data(const std::string& data) -{ - m_supplement = data; - m_header_modified = true; -} - -//////////////////////////////////////// -// unit data management - -bool stlplus::lm_unit::load(void) -{ - if (out_of_date()) return false; - if (m_loaded) return true; - // get the user data for this type - lm_callback_map::iterator callback = m_library->m_manager->m_callbacks.find(type()); - if (callback == m_library->m_manager->m_callbacks.end()) return false; - void* data = callback->second.m_type_data; - bool result = read(filename(), data); - m_loaded = true; - return result; -} - -bool stlplus::lm_unit::save(void) -{ - if (!m_marked) return true; - if (!m_loaded) return false; - // get the user data for this type - lm_callback_map::iterator callback = m_library->m_manager->m_callbacks.find(type()); - if (callback == m_library->m_manager->m_callbacks.end()) return false; - void* data = callback->second.m_type_data; - bool result = write(filename(), data); - if (result) m_marked = false; - return result; -} - -bool stlplus::lm_unit::loaded(void) const -{ - return m_loaded; -} - -void stlplus::lm_unit::mark(void) -{ - m_marked = true; -} - -time_t stlplus::lm_unit::modified(void) const -{ - return file_modified(filename()); -} - -//////////////////////////////////////// -// containing library manager details - -const stlplus::lm_library* stlplus::lm_unit::library(void) const -{ - return m_library; -} - -stlplus::lm_library* stlplus::lm_unit::library(void) -{ - return m_library; -} - -const std::string& stlplus::lm_unit::library_name(void) const -{ - return m_library->name(); -} - -const std::string& stlplus::lm_unit::library_path(void) const -{ - return m_library->path(); -} - -// error handling - these apply to the last read/write operation - -bool stlplus::lm_unit::error(void) const -{ - return m_error; -} - -// functions that customise subclasses of this superclass - -bool stlplus::lm_unit::read(const std::string& filespec, void* type_data) -{ - std::ifstream input(filespec.c_str()); - bool result = read(input, type_data); - if (input.fail()) - { - result = false; - m_error = true; - } - return result; -} - -bool stlplus::lm_unit::read(std::istream&, void*) -{ - return false; -} - -bool stlplus::lm_unit::write(const std::string& filespec, void* type_data) -{ - std::ofstream output(filespec.c_str()); - bool result = write(output, type_data); - if (output.fail()) - { - result = false; - m_error = true; - } - return result; -} - -bool stlplus::lm_unit::write(std::ostream&, void*) -{ - return false; -} - -bool stlplus::lm_unit::purge(void) -{ - return true; -} - -stlplus::lm_unit* stlplus::lm_unit::clone(void) const -{ - return new lm_unit(*this); -} - -bool stlplus::lm_unit::print(std::ostream& str) const -{ - str << m_name << " " << (m_loaded ? "loaded" : "") << " " << (m_marked ? "needs saving" : ""); - return !str.fail(); -} - -bool stlplus::lm_unit::print_long(std::ostream& str) const -{ - str << "header:" << std::endl; - str << " name: " << m_name.name() << std::endl; - str << " type: " << library()->manager()->description(m_name.type()) << std::endl; - str << " dependencies:" << std::endl; - // Note: I've inlined this rather than call the above-defined print for dependencies - // This is so that I can use the library manager to look up the descriptions of unit types - // I can also convert paths so that they are relative to the current directory rather than the library - // print the source file dependency if present - if (m_dependencies.source_file_present()) - { - str << " source file: "; - str << filespec_to_relative_path(m_dependencies.source_file().path_full(library()->path())); - if (m_dependencies.source_file().line() != 0) - str << ":" << m_dependencies.source_file().line() << ":" << m_dependencies.source_file().column(); - str << std::endl; - } - // now print other file dependencies - // convert these to relative paths too - for (unsigned f = 0; f < m_dependencies.file_size(); f++) - { - str << " file: "; - str << filespec_to_relative_path(m_dependencies.file_dependency(f).path_full(library()->path())); - if (m_dependencies.file_dependency(f).line() != 0) - str << ":" << m_dependencies.file_dependency(f).line() << ":" << m_dependencies.file_dependency(f).column(); - str << std::endl; - } - // now print unit dependencies - // convert unit types to their descriptive strings - for (unsigned u = 0; u < m_dependencies.unit_size(); u++) - { - str << " " << library()->manager()->description(m_dependencies.unit_dependency(u).type()) << ": "; - str << m_dependencies.unit_dependency(u).library() << "." << m_dependencies.unit_dependency(u).name(); - str << std::endl; - } - if (!m_supplement.empty()) - { - str << " supplementary data: " << m_supplement << std::endl; - } - return !str.fail(); -} - -// header file management - -std::string stlplus::lm_unit::filename(void) const -{ - return stlplus::create_filespec(library_path(), m_name.name(), m_name.type()); -} - -std::string stlplus::lm_unit::header_filename(void) const -{ - return stlplus::create_filespec(library_path(), m_name.name(), m_name.type() + std::string(HeaderExtension)); -} - -bool stlplus::lm_unit::read_header(void) -{ - if (file_exists(header_filename())) - { - std::ifstream input(header_filename().c_str()); - m_dependencies.read(input); - input.get(); - std::getline(input, m_supplement); - } - m_header_modified = false; - return true; -} - -bool stlplus::lm_unit::write_header(void) -{ - if (!m_header_modified) return true; - std::ofstream output(header_filename().c_str()); - m_dependencies.write(output); - output << m_supplement << std::endl; - m_header_modified = false; - return true; -} - -// print diagnostics - -std::ostream& stlplus::operator << (std::ostream& str, const stlplus::lm_unit& u) -{ - u.print(str); - return str; -} - -//////////////////////////////////////////////////////////////////////////////// -// lm_library -//////////////////////////////////////////////////////////////////////////////// - -//////////////////////////////////////////////////////////////////////////////// -// constructors/destructors -// copy operations only legal on unopened libraries - -stlplus::lm_library::lm_library(library_manager* manager) : m_writable(false), m_manager(manager) -{ -} - -stlplus::lm_library::lm_library(const lm_library& library) : m_writable(library.m_writable), m_manager(library.m_manager) -{ -} - -stlplus::lm_library& stlplus::lm_library::operator = (const stlplus::lm_library& library) -{ - m_writable = library.m_writable; - m_manager = library.m_manager; - return *this; -} - -stlplus::lm_library::~lm_library(void) -{ - close(); -} - -const stlplus::library_manager* stlplus::lm_library::manager(void) const -{ - return m_manager; -} - -stlplus::library_manager* stlplus::lm_library::manager(void) -{ - return m_manager; -} - -////////////////////////////////////////////////////////////////////////////// -// initialisers - -bool stlplus::lm_library::create(const std::string& name, const std::string& path, bool writable) -{ - close(); - if (!create_library(path, m_manager->m_owner, name, writable)) return false; - return open(path); -} - -bool stlplus::lm_library::open(const std::string& path) -{ - close(); - if (!read_context(path, m_manager->m_owner, m_name, m_writable)) return false; - // convert path to full path on load - m_path = folder_to_path(path); - return load_types(); -} - -////////////////////////////////////////////////////////////////////////////// -// management of types - -bool stlplus::lm_library::load_type(const std::string& type) -{ - lm_callback_map::iterator callback = m_manager->m_callbacks.find(type); - if (callback == m_manager->m_callbacks.end()) return false; - // a null callback means create a dummy unit from the baseclass only - lm_create_callback fn = callback->second.m_callback; - void* data = callback->second.m_type_data; - // for each file in the library folder that matches the type of the create callback, create an unloaded unit - std::vector files = folder_wildcard(m_path, stlplus::create_filename("*", type), false, true); - for (unsigned i = 0; i < files.size(); i++) - { - // key by unit name - lowercase name if case-insensitive - lm_unit_name uname(basename_part(files[i]),type); - if (!m_manager->m_unit_case) uname.lowercase(); - lm_unit_ptr unit; - // if there's a callback, use it to create the subclass, else create the - // superclass which only contains header information - if (fn) - unit.set(fn(uname,this,data)); - else - unit.set(new lm_unit(uname,this)); - m_units[uname] = unit; - } - return true; -} - -bool stlplus::lm_library::load_types(void) -{ - bool result = true; - for (lm_callback_map::const_iterator i = m_manager->m_callbacks.begin(); i != m_manager->m_callbacks.end(); i++) - result &= load_type(i->first); - return result; -} - -bool stlplus::lm_library::remove_type(const std::string& type) -{ - bool result = true; - std::vector units = names(type); - for (std::vector::iterator i = units.begin(); i != units.end(); i++) - { - lm_unit_name name(*i, type); - std::map::iterator ni = local_find(name); - if (ni != m_units.end()) - m_units.erase(ni); - } - return result; -} - -////////////////////////////////////////////////////////////////////////////// -// whole library operations - -bool stlplus::lm_library::load(void) -{ - bool result = true; - for (std::map::iterator i = m_units.begin(); i != m_units.end(); i++) - result &= i->second->load(); - return result; -} - -bool stlplus::lm_library::save(void) -{ - bool result = true; - for (std::map::iterator i = m_units.begin(); i != m_units.end(); i++) - result &= i->second->save(); - return result; -} - -bool stlplus::lm_library::purge(void) -{ - bool result = true; - for (std::map::iterator i = m_units.begin(); i != m_units.end(); i++) - result &= i->second->purge(); - return result; -} - -bool stlplus::lm_library::close(void) -{ - bool result = save(); - m_units.clear(); - m_path = ""; - m_writable = false; - return result; -} - -bool stlplus::lm_library::erase(void) -{ - // preserve the path because close destroys it - std::string path = m_path; - return close() && erase_library(path, m_manager->m_owner); -} - -const std::string& stlplus::lm_library::name(void) const -{ - return m_name; -} - -const std::string& stlplus::lm_library::path(void) const -{ - return m_path; -} - -////////////////////////////////////////////////////////////////////////////// -// managing read/write status - -bool stlplus::lm_library::set_read_write(bool writable) -{ - if (m_writable == writable) return true; - if (os_read_only()) return false; - m_writable = writable; - if (!write_context(m_path, m_manager->m_owner, m_name, m_writable)) - read_context(m_path, m_manager->m_owner, m_name, m_writable); - return m_writable == writable; -} - -bool stlplus::lm_library::set_writable(void) -{ - return set_read_write(true); -} - -bool stlplus::lm_library::set_read_only(void) -{ - return set_read_write(false); -} - -bool stlplus::lm_library::writable(void) const -{ - return os_writable() && lm_writable(); -} - -bool stlplus::lm_library::read_only(void) const -{ - return os_read_only() || lm_read_only(); -} - -bool stlplus::lm_library::os_writable(void) const -{ - return folder_writable(path()); -} - -bool stlplus::lm_library::os_read_only(void) const -{ - return !folder_writable(path()); -} - -bool stlplus::lm_library::lm_writable(void) const -{ - return m_writable; -} - -bool stlplus::lm_library::lm_read_only(void) const -{ - return !m_writable; -} - -////////////////////////////////////////////////////////////////////////////// -// unit management - -std::map::iterator stlplus::lm_library::local_find(const lm_unit_name& name) -{ - // implement the case-sensitivity - lm_unit_name local = name; - if (!m_manager->m_unit_case) local.lowercase(); - return m_units.find(local); -} - -std::map::const_iterator stlplus::lm_library::local_find(const lm_unit_name& name) const -{ - // implement the case-sensitivity - lm_unit_name local = name; - if (!m_manager->m_unit_case) local.set_name(lowercase(local.name())); - return m_units.find(local); -} - -bool stlplus::lm_library::exists(const lm_unit_name& name) const -{ - return find(name).present(); -} - -stlplus::lm_unit_ptr stlplus::lm_library::create(const stlplus::lm_unit_name& name) -{ - if (read_only()) return lm_unit_ptr(); - // preserve the unit's name, but use a lowercase key in case-insensitive mode - lm_unit_name uname = name; - if (!m_manager->m_unit_case) uname.lowercase(); - // remove any existing unit with the same name - erase(uname); - // use the callbacks to create a new unit - lm_callback_map::iterator callback = m_manager->m_callbacks.find(name.type()); - if (callback == m_manager->m_callbacks.end()) return lm_unit_ptr(); - lm_unit_ptr new_unit; - new_unit.set(callback->second.m_callback(name,this,callback->second.m_type_data)); - new_unit->m_loaded = true; - // add it to the library manager - m_units[uname] = new_unit; - return m_units[uname]; -} - -bool stlplus::lm_library::loaded(const lm_unit_name& name) const -{ - lm_unit_ptr unit = find(name); - return unit && unit->loaded(); -} - -bool stlplus::lm_library::load(const lm_unit_name& name) -{ - lm_unit_ptr unit = find(name); - if (!unit) return false; - return unit->load(); -} - -bool stlplus::lm_library::purge(const lm_unit_name& name) -{ - lm_unit_ptr unit = find(name); - if (!unit) return false; - bool result = save(name); - result &= unit->purge(); - unit->m_loaded = false; - return result; -} - -bool stlplus::lm_library::save(const lm_unit_name& name) -{ - if (read_only()) return false; - lm_unit_ptr unit = find(name); - if (!unit) return false; - return unit->save(); -} - -bool stlplus::lm_library::erase(const lm_unit_name& name) -{ - if (read_only()) return false; - std::map::iterator i = local_find(name); - if (i == m_units.end()) return false; - std::string spec = i->second->filename(); - std::string header_spec = i->second->header_filename(); - m_units.erase(i); - file_delete(spec); - file_delete(header_spec); - return true; -} - -bool stlplus::lm_library::mark(const lm_unit_name& name) -{ - if (read_only()) return false; - lm_unit_ptr unit = find(name); - if (!unit) return false; - unit->mark(); - return true; -} - -time_t stlplus::lm_library::modified(const lm_unit_name& name) const -{ - lm_unit_ptr unit = find(name); - return unit ? unit->modified() : 0; -} - -bool stlplus::lm_library::erase_by_source(const std::string& source_file) -{ - if (read_only()) return false; - if (source_file.empty()) return false; - bool result = false; - std::string source_file_full = filespec_to_path(source_file); - // erase by unit name so that I don't have to deal with an iterator to a changing map - std::vector units = names(); - for (std::vector::iterator i = units.begin(); i != units.end(); i++) - { - lm_unit_ptr unit = find(*i); - if (unit && unit->source_file_present()) - { - std::string file_full = unit->source_file().path_full(unit->library_path()); - if (file_full == source_file_full) - { - erase(*i); - result = true; - } - } - } - return result; -} - -const stlplus::lm_unit_ptr stlplus::lm_library::find(const stlplus::lm_unit_name& name) const -{ - std::map::const_iterator i = local_find(name); - if (i == m_units.end()) return lm_unit_ptr(); - return i->second; -} - -stlplus::lm_unit_ptr stlplus::lm_library::find(const stlplus::lm_unit_name& name) -{ - std::map::iterator i = local_find(name); - if (i == m_units.end()) return lm_unit_ptr(); - return i->second; -} - -std::vector stlplus::lm_library::names(void) const -{ - std::vector result; - for (std::map::const_iterator i = m_units.begin(); i != m_units.end(); i++) - result.push_back(i->second->unit_name()); - return result; -} - -std::vector stlplus::lm_library::names(const std::string& type) const -{ - std::vector result; - for (std::map::const_iterator i = m_units.begin(); i != m_units.end(); i++) - if (i->first.type() == type) - result.push_back(i->second->name()); - return result; -} - -std::vector stlplus::lm_library::handles(void) const -{ - std::vector result; - for (std::map::const_iterator i = m_units.begin(); i != m_units.end(); i++) - result.push_back(i->second); - return result; -} - -std::vector stlplus::lm_library::handles(const std::string& type) const -{ - std::vector result; - for (std::map::const_iterator i = m_units.begin(); i != m_units.end(); i++) - if (i->first.type() == type) - result.push_back(i->second); - return result; -} - -//////////////////////////////////////////////////////////////////////////////// -// dependency checking - -bool stlplus::lm_library::out_of_date(const stlplus::lm_unit_name& name) const -{ - return m_manager->out_of_date(m_name, name); -} - -bool stlplus::lm_library::up_to_date(const stlplus::lm_unit_name& name) const -{ - return m_manager->up_to_date(m_name, name); -} - -stlplus::lm_dependencies stlplus::lm_library::out_of_date_reason(const stlplus::lm_unit_name& unit) const -{ - return m_manager->out_of_date_reason(m_name, unit); -} - -std::pair stlplus::lm_library::tidy(void) -{ - std::pair result = std::make_pair(true,0); - // erase every unit that is out of date - // this will potentially make other units out of date, so keep erasing until - // everything is up to date or an error occurs - for (;;) - { - std::vector units = names(); - unsigned initial_count = result.second; - for (std::vector::iterator i = units.begin(); i != units.end(); i++) - { - if (out_of_date(*i)) - { - if (!erase(*i)) - result.first = false; - else - result.second++; - } - } - if (!result.first) break; - if (result.second == initial_count) break; - } - return result; -} - -//////////////////////////////////////////////////////////////////////////////// -// do-everything print routine! - -bool stlplus::lm_library::pretty_print(std::ostream& str, - bool print_units, - const std::string& type) const -{ - // print the library information - if (this == m_manager->work()) - str << "->> "; - else - str << " "; - str << name() << " in directory " << folder_to_relative_path(path()); - if (read_only()) str << " (locked)"; - str << std::endl; - // select the units - if (print_units) - { - // separate into a block per unit kind - for (lm_callback_map::const_iterator j = m_manager->m_callbacks.begin(); j != m_manager->m_callbacks.end(); j++) - { - // select the requested unit kind - if (type.empty() || type == j->first) - { - // get all the units of this kind - std::vector unit_names = names(j->first); - if (!unit_names.empty()) - { - str << " " << j->second.m_description << std::endl; - for (unsigned k = 0; k < unit_names.size(); k++) - { - lm_dependencies reason = out_of_date_reason(stlplus::lm_unit_name(unit_names[k],j->first)); - str << " - " << unit_names[k]; - if (!reason.empty()) str << " (out of date)"; - str << std::endl; - if (!reason.empty()) - { - if (reason.source_file_present()) - { - const lm_file_dependency& file = reason.source_file(); - str << " * source file " << filespec_to_relative_path(file.path_full(path())) << " has changed" << std::endl; - } - for (unsigned i1 = 0; i1 < reason.file_size(); i1++) - { - const lm_file_dependency& file = reason.file_dependency(i1); - str << " * file " << filespec_to_relative_path(file.path_full(path())) << " has changed" << std::endl; - } - for (unsigned i2 = 0; i2 < reason.unit_size(); i2++) - { - const lm_unit_dependency& file = reason.unit_dependency(i2); - lm_callback_map::const_iterator entry = m_manager->m_callbacks.find(file.type()); - str << " * " << entry->second.m_description << " " << file.library() << "." << file.name() << " has changed" << std::endl; - } - } - } - } - } - } - } - return true; -} - -//////////////////////////////////////////////////////////////////////////////// -// diagnostic print routines - -bool stlplus::lm_library::print(std::ostream& str) const -{ - str << name() << " in " << path() << " " << (writable() ? "writable" : "read-only") << std::endl; - for (std::map::const_iterator i = m_units.begin(); i != m_units.end(); i++) - i->second->print(str); - return !str.fail(); -} - -bool stlplus::lm_library::print_long(std::ostream& str) const -{ - str << name() << " in " << path() << " " << (writable() ? "writable" : "read-only") << std::endl; - for (std::map::const_iterator i = m_units.begin(); i != m_units.end(); i++) - i->second->print_long(str); - return !str.fail(); -} - -std::ostream& stlplus::operator << (std::ostream& str, const stlplus::lm_library& lib) -{ - lib.print(str); - return str; -} - -//////////////////////////////////////////////////////////////////////////////// -// library manager -//////////////////////////////////////////////////////////////////////////////// - -// static functions allow you to test whether a directory is a library before opening it -// you can also find the library's name without opening it - -bool stlplus::library_manager::is_library(const std::string& path, const std::string& owner) -{ - std::string spec = stlplus::create_filespec(path, owner, LibraryNameExtension); - return file_exists(spec); -} - -std::string stlplus::library_manager::library_name(const std::string& path, const std::string& owner) -{ - std::string name; - bool writable = false; - if (!read_context(path, owner, name, writable)) - return std::string(); - return name; -} - -bool stlplus::library_manager::is_library(const std::string& path) -{ - return is_library(path, m_owner); -} - -std::string stlplus::library_manager::library_name(const std::string& path) -{ - return library_name(path, m_owner); -} - -////////////////////////////////////////////////////////////////////////////// -// tructors - -stlplus::library_manager::library_manager(const std::string& owner, bool library_case, bool unit_case) : - m_owner(owner), m_ini_files(0), m_library_case(library_case), m_unit_case(unit_case) -{ -} - -stlplus::library_manager::~library_manager(void) -{ - close(); -} - -////////////////////////////////////////////////////////////////////////////// -// case sensitivity - -bool stlplus::library_manager::library_case(void) const -{ - return m_library_case; -} - -void stlplus::library_manager::set_library_case(bool library_case) -{ - m_library_case = library_case; -} - -bool stlplus::library_manager::unit_case(void) const -{ - return m_unit_case; -} - -void stlplus::library_manager::set_unit_case(bool unit_case) -{ - m_unit_case = unit_case; -} - -//////////////////////////////////////////////////////////////////////////////// -// type handling - -bool stlplus::library_manager::add_type(const std::string& type, - const std::string& description, - lm_create_callback fn, - void* type_data) -{ - bool result = true; - m_callbacks[type] = lm_callback_entry(fn, description, type_data); - for (std::list::iterator i = m_libraries.begin(); i != m_libraries.end(); i++) - result &= i->load_type(type); - return result; -} - -bool stlplus::library_manager::remove_type(const std::string& type) -{ - bool result = true; - for (std::list::iterator i = m_libraries.begin(); i != m_libraries.end(); i++) - result &= i->remove_type(type); - m_callbacks.erase(type); - return result; -} - -std::vector stlplus::library_manager::types(void) const -{ - std::vector result; - for (lm_callback_map::const_iterator i = m_callbacks.begin(); i != m_callbacks.end(); i++) - result.push_back(i->first); - return result; -} - -std::string stlplus::library_manager::description(const std::string& type) const -{ - lm_callback_map::const_iterator found = m_callbacks.find(type); - if (found == m_callbacks.end()) return std::string(); - return found->second.m_description; -} - -stlplus::lm_create_callback stlplus::library_manager::callback(const std::string& type) const -{ - lm_callback_map::const_iterator found = m_callbacks.find(type); - if (found == m_callbacks.end()) return 0; - return found->second.m_callback; -} - -void* stlplus::library_manager::type_data(const std::string& type) const -{ - lm_callback_map::const_iterator found = m_callbacks.find(type); - if (found == m_callbacks.end()) return 0; - return found->second.m_type_data; -} - -////////////////////////////////////////////////////////////////////////////// -// mapping file handling - -void stlplus::library_manager::set_mapping_file(const std::string& mapping_file) -{ - m_mapping_file = mapping_file; -} - -bool stlplus::library_manager::load_mappings(const std::string& mapping_file) -{ - m_mapping_file = mapping_file; - if (!file_exists(mapping_file)) - { - return false; - } - std::ifstream input(mapping_file.c_str()); - if (input.fail()) - return false; - // each line of the map file is a path to a library - // the first line is the work library - may be empty - // mappings are saved as paths relative to the mapping file and converted to full paths on load - bool result = true; - unsigned line = 1; - for (std::string path = ""; std::getline(input,path); line++) - { - if (path.empty()) continue; - std::string full_path = folder_to_path(folder_part(m_mapping_file), path); - if (!is_library(full_path)) - { - result = false; - } - else - { - lm_library* lib = open(full_path); - if (!lib) - result = false; - else if (line == 1) - setwork(lib->name()); - } - } - return result; -} - -std::string stlplus::library_manager::mapping_file() -{ - return m_mapping_file; -} - -bool stlplus::library_manager::set_ini_manager(ini_manager* ini_files, const std::string& library_section, const std::string& work_name) -{ - if (!ini_files) return false; - bool result = true; - m_ini_files = ini_files; - m_ini_section = library_section; - m_ini_work = work_name; - // now load the existing library mappings if present - in any case create the sections - // Note: a library mapping is saved as a path relative to the ini file - on load convert it to a path relative to the current directory - if (m_ini_files->section_exists(m_ini_section)) - { - std::vector library_names = m_ini_files->variable_names(m_ini_section); - std::string work_name; - for (unsigned i = 0; i < library_names.size(); i++) - { - std::string library_name = library_names[i]; - // if the variable name is the work name, then this is a mapping to an existing library name - // if it is null, then it is masking a global library definition so ignore it - // otherwise it is a mapping to a directory containing the library - if (library_name.empty()) - { - } - else if (library_name == m_ini_work) - { - work_name = m_ini_files->variable_value(m_ini_section, library_name); - } - else - { - std::string value = m_ini_files->variable_value(m_ini_section, library_name); - std::string filename = m_ini_files->variable_filename(m_ini_section, library_name); - // get the path to the ini file defining this library, strip off the ini filename to get the folder - // then combine this with the library path from that ini file to the library to get a full path to the library - // whew! - std::string full_path = folder_to_path(folder_part(filename),value); - if (!is_library(full_path)) - result = false; - else - { - lm_library* lib = open(full_path); - if (!lib) - result = false; - } - } - } - // work must be set after all the libraries have been opened because it is - // illegal to set work to a library that doesn't already exist in the - // library manager - if (work_name.empty()) - unsetwork(); - else - result &= setwork(work_name); - } - return result; -} - -stlplus::ini_manager* stlplus::library_manager::get_ini_manager(void) const -{ - return m_ini_files; -} - -bool stlplus::library_manager::save_mappings (void) -{ - bool result = true; - // save to mapping file or ini manager or both - if (!m_mapping_file.empty()) - { - if (m_libraries.size() == 0) - { - // if the file would be empty, delete it - if (!file_delete(m_mapping_file)) - result = false; - } - else - { - std::ofstream output(m_mapping_file.c_str()); - if (output.fail()) - { - result = false; - } - else - { - // each line of the map file is a path to a library - // the first line is the work library - // mappings are saved as relative paths to the mapping file and converted to full paths on load - if (!work_name().empty()) - output << folder_to_relative_path(folder_part(m_mapping_file), path(work_name())); - output << std::endl; - std::vector libraries = names(); - for (unsigned i = 0; i < libraries.size(); ++i) - { - if (libraries[i] != work_name()) - output << folder_to_relative_path(folder_part(m_mapping_file), path(libraries[i])) << std::endl; - } - if (output.fail()) - result = false; - } - } - } - if (m_ini_files) - { - // this is somewhat tricky! - // first remove all local mappings - // then need to compare the surviving library mappings with the contents of the library manager - // if there's a library in the ini files not in the library manager, mask it with an empty local declaration - // if there's a library in the ini files with the same mapping as the library manager, do nothing - // if there's a library in the ini files with a different mapping write that library mapping - // if there's a mapping missing from the ini files, write it - // finally write the work mapping if there is one - // clear all local mappings - // TODO - rework this so that the ini files only change if the mappings change - m_ini_files->clear_section(m_ini_section); - m_ini_files->add_comment(m_ini_section, "generated automatically by the library manager"); - // look for globally defined library mappings that need to be overridden in the local ini file - std::vector ini_names = m_ini_files->variable_names(m_ini_section); - for (unsigned i = 0; i < ini_names.size(); i++) - { - std::string ini_name = ini_names[i]; - // check for a global library that needs to be locally masked - if (!exists(ini_name)) - m_ini_files->add_variable(m_ini_section, ini_name, ""); - else - { - // check for a library that is locally remapped - std::string value = m_ini_files->variable_value(m_ini_section, ini_name); - std::string filename = m_ini_files->variable_filename(m_ini_section, ini_name); - std::string full_ini_path = folder_to_path(folder_part(filename), value); - std::string full_lib_path = folder_to_path(path(ini_name)); - if (full_ini_path != full_lib_path) - { - // write the path relative to the ini file - std::string relative_path = folder_to_relative_path(folder_part(filename), full_lib_path); - m_ini_files->add_variable(m_ini_section, ini_name, relative_path); - } - } - } - // now scan the library for mappings that aren't yet in the ini file - std::vector lib_names = names(); - for (unsigned j = 0; j < lib_names.size(); j++) - { - std::string lib_name = lib_names[j]; - if (std::find(ini_names.begin(), ini_names.end(), lib_name) == ini_names.end()) - { - // write the path relative to the ini file - std::string full_lib_path = folder_to_path(path(lib_name)); - std::string filename = m_ini_files->variable_filename(m_ini_section, lib_name); - std::string relative_path = folder_to_relative_path(folder_part(filename), full_lib_path); - m_ini_files->add_variable(m_ini_section, lib_name, relative_path); - } - } - // write the work library - also write a blank value if work is not set but is defined in another library - if (!work_name().empty()) - m_ini_files->add_variable(m_ini_section, m_ini_work, work_name()); - else if (m_ini_files->variable_exists(m_ini_section, m_ini_work)) - m_ini_files->add_variable(m_ini_section, m_ini_work, ""); - m_ini_files->add_blank(m_ini_section); - // remove the section from the ini file manager if there's nothing in it - if (m_ini_files->empty_section(m_ini_section)) - m_ini_files->erase_section(m_ini_section); - if (!m_ini_files->save()) - result = false; - } - return result; -} - -////////////////////////////////////////////////////////////////////////////// -// library management - -bool stlplus::library_manager::exists(const std::string& name) const -{ - return find(name) != 0; -} - -stlplus::lm_library* stlplus::library_manager::create(const std::string& name, const std::string& path, bool writable) -{ - if (!create_library(path, m_owner, name, writable)) return 0; - return open(path); -} - -stlplus::lm_library* stlplus::library_manager::open(const std::string& path) -{ - std::string name; - bool writable = false; - if (!read_context(path, m_owner, name, writable)) return 0; - // remove any pre-existing library with the same name - close(name); - // add the library to the manager and open it - m_libraries.push_back(lm_library(this)); - if (!m_libraries.back().open(folder_to_path(path))) - { - // remove the library in the event of an error - m_libraries.erase(--m_libraries.end()); - return 0; - } - return &m_libraries.back(); -} - -bool stlplus::library_manager::load(const std::string& name) -{ - std::list::iterator found = local_find(name); - if (found == m_libraries.end()) return false; - return found->load(); -} - -bool stlplus::library_manager::save(const std::string& name) -{ - std::list::iterator found = local_find(name); - if (found == m_libraries.end()) return false; - return found->save(); -} - -bool stlplus::library_manager::purge(const std::string& name) -{ - std::list::iterator found = local_find(name); - if (found == m_libraries.end()) return false; - return found->purge(); -} - -bool stlplus::library_manager::close(const std::string& name) -{ - bool result= true; - std::list::iterator found = local_find(name); - if (found == m_libraries.end()) return false; - result &= found->close(); - m_libraries.erase(found); - if (name == m_work) m_work = ""; - return result; -} - -bool stlplus::library_manager::erase(const std::string& name) -{ - bool result= true; - std::list::iterator found = local_find(name); - if (found == m_libraries.end()) return false; - result &= found->erase(); - m_libraries.erase(found); - if (name == m_work) m_work = ""; - return result; -} - -// operations on all libraries - as above but applied to all the libraries in the manager - -bool stlplus::library_manager::load(void) -{ - bool result = true; - for (std::list::iterator i = m_libraries.begin(); i != m_libraries.end(); i++) - result &= i->load(); - return result; -} - -bool stlplus::library_manager::save(void) -{ - bool result = true; - for (std::list::iterator i = m_libraries.begin(); i != m_libraries.end(); i++) - result &= i->save(); - return result; -} - -bool stlplus::library_manager::purge(void) -{ - bool result = true; - for (std::list::iterator i = m_libraries.begin(); i != m_libraries.end(); i++) - result &= i->purge(); - return result; -} - -bool stlplus::library_manager::close(void) -{ - bool result = true; - for (std::list::iterator i = m_libraries.begin(); i != m_libraries.end(); ) - { - std::list::iterator next = i; - next++; - result &= i->close(); - m_libraries.erase(i); - i = next; - } - return result; -} - -bool stlplus::library_manager::erase(void) -{ - bool result = true; - for (std::list::iterator i = m_libraries.begin(); i != m_libraries.end(); ) - { - std::list::iterator next = i; - next++; - result &= i->erase(); - m_libraries.erase(i); - i = next; - } - return result; -} - -// get name and path of a library - name can differ in case if the library manager is case-insensitive - -std::string stlplus::library_manager::name(const std::string& name) const -{ - std::list::const_iterator found = local_find(name); - if (found == m_libraries.end()) return std::string(); - return found->name(); -} - -std::string stlplus::library_manager::path(const std::string& name) const -{ - std::list::const_iterator found = local_find(name); - if (found == m_libraries.end()) return std::string(); - return found->path(); -} - -// control and test read/write status - -bool stlplus::library_manager::set_writable(const std::string& name) -{ - std::list::iterator found = local_find(name); - if (found == m_libraries.end()) return false; - return found->set_writable(); -} - -bool stlplus::library_manager::set_read_only(const std::string& name) -{ - std::list::iterator found = local_find(name); - if (found == m_libraries.end()) return false; - return found->set_read_only(); -} - -bool stlplus::library_manager::writable(const std::string& name) const -{ - std::list::const_iterator found = local_find(name); - if (found == m_libraries.end()) return false; - return found->writable(); -} - -bool stlplus::library_manager::read_only(const std::string& name) const -{ - std::list::const_iterator found = local_find(name); - if (found == m_libraries.end()) return false; - return found->read_only(); -} - -bool stlplus::library_manager::os_writable(const std::string& library) const -{ - std::list::const_iterator found = local_find(library); - if (found == m_libraries.end()) return false; - return found->os_writable(); -} - -bool stlplus::library_manager::os_read_only(const std::string& library) const -{ - std::list::const_iterator found = local_find(library); - if (found == m_libraries.end()) return false; - return found->os_read_only(); -} - -bool stlplus::library_manager::lm_writable(const std::string& library) const -{ - std::list::const_iterator found = local_find(library); - if (found == m_libraries.end()) return false; - return found->lm_writable(); -} - -bool stlplus::library_manager::lm_read_only(const std::string& library) const -{ - std::list::const_iterator found = local_find(library); - if (found == m_libraries.end()) return false; - return found->lm_read_only(); -} - -// find a library in the manager - returns null if not found - -stlplus::lm_library* stlplus::library_manager::find(const std::string& name) -{ - std::list::iterator found = local_find(name); - if (found == m_libraries.end()) return 0; - return &(*found); -} - -const stlplus::lm_library* stlplus::library_manager::find(const std::string& name) const -{ - std::list::const_iterator found = local_find(name); - if (found == m_libraries.end()) return 0; - return &(*found); -} - -// get the set of all library names - -std::vector stlplus::library_manager::names(void) const -{ - std::vector result; - for (std::list::const_iterator i = m_libraries.begin(); i != m_libraries.end(); i++) - result.push_back(i->name()); - return result; -} - -// get the set of all libraries - -std::vector stlplus::library_manager::handles(void) const -{ - std::vector result; - for (std::list::const_iterator i = m_libraries.begin(); i != m_libraries.end(); i++) - result.push_back(&(*i)); - return result; -} - -std::vector stlplus::library_manager::handles(void) -{ - std::vector result; - for (std::list::iterator i = m_libraries.begin(); i != m_libraries.end(); i++) - result.push_back(&(*i)); - return result; -} - -////////////////////////////////////////////////////////////////////////////// -// current library management - -bool stlplus::library_manager::setwork(const std::string& name) -{ - unsetwork(); - std::list::const_iterator found = local_find(name); - if (found == m_libraries.end()) return false; - m_work = found->name(); - return true; -} - -bool stlplus::library_manager::unsetwork(void) -{ - m_work = ""; - return true; -} - -const stlplus::lm_library* stlplus::library_manager::work(void) const -{ - if (m_work.empty()) return 0; - return find(m_work); -} - -stlplus::lm_library* stlplus::library_manager::work(void) -{ - if (m_work.empty()) return 0; - return find(m_work); -} - -std::string stlplus::library_manager::work_name(void) const -{ - return m_work; -} - -////////////////////////////////////////////////////////////////////////////// -// unit management within a library - -bool stlplus::library_manager::exists(const std::string& name, const stlplus::lm_unit_name& unit) const -{ - std::list::const_iterator found = local_find(name); - if (found == m_libraries.end()) return false; - return found->exists(unit); -} - -stlplus::lm_unit_ptr stlplus::library_manager::create(const std::string& name, const stlplus::lm_unit_name& unit) -{ - std::list::iterator found = local_find(name); - if (found == m_libraries.end()) return stlplus::lm_unit_ptr(); - return found->create(unit); -} - -bool stlplus::library_manager::loaded(const std::string& name, const stlplus::lm_unit_name& unit) const -{ - std::list::const_iterator found = local_find(name); - if (found == m_libraries.end()) return stlplus::lm_unit_ptr(); - return found->loaded(unit); -} - -bool stlplus::library_manager::load(const std::string& name, const stlplus::lm_unit_name& unit) -{ - std::list::iterator found = local_find(name); - if (found == m_libraries.end()) return stlplus::lm_unit_ptr(); - return found->load(unit); -} - -bool stlplus::library_manager::purge(const std::string& name, const stlplus::lm_unit_name& unit) -{ - std::list::iterator found = local_find(name); - if (found == m_libraries.end()) return false; - return found->purge(unit); -} - -bool stlplus::library_manager::save(const std::string& name, const stlplus::lm_unit_name& unit) -{ - std::list::iterator found = local_find(name); - if (found == m_libraries.end()) return false; - return found->save(unit); -} - -bool stlplus::library_manager::erase(const std::string& name, const stlplus::lm_unit_name& unit) -{ - std::list::iterator found = local_find(name); - if (found == m_libraries.end()) return false; - return found->erase(unit); -} - -bool stlplus::library_manager::mark(const std::string& name, const stlplus::lm_unit_name& unit) -{ - std::list::iterator found = local_find(name); - if (found == m_libraries.end()) return false; - return found->mark(unit); -} - -time_t stlplus::library_manager::modified(const std::string& name, const stlplus::lm_unit_name& unit) const -{ - std::list::const_iterator found = local_find(name); - if (found == m_libraries.end()) return 0; - return found->modified(unit); -} - -bool stlplus::library_manager::erase_by_source(const std::string& source_file) -{ - bool result = true; - for (std::list::iterator i = m_libraries.begin(); i != m_libraries.end(); i++) - if (i->writable()) - result &= i->erase_by_source(source_file); - return result; -} - -const stlplus::lm_unit_ptr stlplus::library_manager::find(const std::string& name, const stlplus::lm_unit_name& unit) const -{ - std::list::const_iterator found = local_find(name); - if (found == m_libraries.end()) - return stlplus::lm_unit_ptr(); - return found->find(unit); -} - -stlplus::lm_unit_ptr stlplus::library_manager::find(const std::string& name, const stlplus::lm_unit_name& unit) -{ - std::list::iterator found = local_find(name); - if (found == m_libraries.end()) return stlplus::lm_unit_ptr(); - return found->find(unit); -} - -std::vector stlplus::library_manager::names(const std::string& name) const -{ - std::list::const_iterator found = local_find(name); - if (found == m_libraries.end()) return std::vector(); - return found->names(); -} - -std::vector stlplus::library_manager::names(const std::string& name, const std::string& type) const -{ - std::list::const_iterator found = local_find(name); - if (found == m_libraries.end()) return std::vector(); - return found->names(type); -} - -std::vector stlplus::library_manager::handles(const std::string& name) const -{ - std::list::const_iterator found = local_find(name); - if (found == m_libraries.end()) return std::vector(); - return found->handles(); -} - -std::vector stlplus::library_manager::handles(const std::string& name, const std::string& type) const -{ - std::list::const_iterator found = local_find(name); - if (found == m_libraries.end()) return std::vector(); - return found->handles(type); -} - -////////////////////////////////////////////////////////////////////////////// -// dependency checking -// done at the top level because a global view of the libraries is required - -bool stlplus::library_manager::out_of_date(const std::string& library, const stlplus::lm_unit_name& name) const -{ - return !up_to_date(library, name); -} - -bool stlplus::library_manager::up_to_date(const std::string& library, const stlplus::lm_unit_name& name) const -{ - lm_dependencies reason = out_of_date_reason(library, name); - return reason.empty(); -} - -stlplus::lm_dependencies stlplus::library_manager::out_of_date_reason(const std::string& library, const stlplus::lm_unit_name& name) const -{ - std::map,lm_dependencies> visited; - return out_of_date_check(visited, this, library, name); -} - -std::pair stlplus::library_manager::tidy(const std::string& library) -{ - std::list::iterator found = local_find(library); - if (found == m_libraries.end()) return std::make_pair(false,0); - return found->tidy(); -} - -std::pair stlplus::library_manager::tidy(void) -{ - std::pair result = std::make_pair(true,0); - for (std::list::iterator i = m_libraries.begin(); i != m_libraries.end(); i++) - { - if (i->writable()) - { - std::pair library_result = i->tidy(); - result.second += library_result.second; - result.first &= library_result.first; - } - } - return result; -} - -////////////////////////////////////////////////////////////////////////////// -// do-everything print routine! - -bool stlplus::library_manager::pretty_print(std::ostream& str, - bool print_units, - const std::string& lib, - const std::string& type) const -{ - bool library_found = false; - for (std::list::const_iterator l = m_libraries.begin(); l != m_libraries.end(); l++) - { - // select the library - if (lib.empty() || lib == l->name()) - { - l->pretty_print(str, print_units, type); - library_found = true; - } - } - if (!library_found) - { - if (lib.empty()) - str << "there are no libraries in the library list" << std::endl; - else - str << "there is no library called " << lib << " in the library list" << std::endl; - return false; - } - return true; -} - -//////////////////////////////////////////////////////////////////////////////// -// diagnostic print routines - -bool stlplus::library_manager::print(std::ostream& str) const -{ - for (std::list::const_iterator l = m_libraries.begin(); l != m_libraries.end(); l++) - l->print(str); - return str; -} - -bool stlplus::library_manager::print_long(std::ostream& str) const -{ - for (std::list::const_iterator l = m_libraries.begin(); l != m_libraries.end(); l++) - l->print_long(str); - return str; -} - -// find a library by name - -std::list::iterator stlplus::library_manager::local_find(const std::string& name) -{ - for (std::list::iterator i = m_libraries.begin(); i != m_libraries.end(); i++) - { - if (m_library_case) - { - if (i->name() == name) return i; - } - else - { - if (lowercase(i->name()) == lowercase(name)) return i; - } - } - return m_libraries.end(); -} - -std::list::const_iterator stlplus::library_manager::local_find(const std::string& name) const -{ - for (std::list::const_iterator i = m_libraries.begin(); i != m_libraries.end(); i++) - { - if (m_library_case) - { - if (i->name() == name) return i; - } - else - { - if (lowercase(i->name()) == lowercase(name)) return i; - } - } - return m_libraries.end(); -} - -std::ostream& stlplus::operator << (std::ostream& str, const stlplus::library_manager& manager) -{ - manager.print(str); - return str; -} - -//////////////////////////////////////////////////////////////////////////////// diff --git a/src/stlplus/subsystems/library_manager.hpp b/src/stlplus/subsystems/library_manager.hpp deleted file mode 100644 index 96e1afd..0000000 --- a/src/stlplus/subsystems/library_manager.hpp +++ /dev/null @@ -1,708 +0,0 @@ -#ifndef STLPLUS_LIBRARY_MANAGER -#define STLPLUS_LIBRARY_MANAGER -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// Generalised library manager. - -// Manages library units in a set of library directories. A unit is both a file -// on-disk and a data-structure in memory. To use the library manager, you need -// to: - -// - design a type based on lm_unit with serialising functions read/write -// - decide on a file extension for the type -// - decide on a description of the type -// - write a create callback for this type -// - register the file extension, description and callback with the library manager - -//////////////////////////////////////////////////////////////////////////////// -#include "subsystems_fixes.hpp" -#include "ini_manager.hpp" -#include "smart_ptr.hpp" -#include -#include -#include -#include -#include -#include - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - // Internals - //////////////////////////////////////////////////////////////////////////////// - - class lm_library; - class library_manager; - - //////////////////////////////////////////////////////////////////////////////// - // unit names - //////////////////////////////////////////////////////////////////////////////// - - class lm_unit_name - { - public: - lm_unit_name(const std::string& name = std::string(), const std::string& type = std::string()); - ~lm_unit_name(void); - - const std::string& name(void) const; - void set_name(const std::string& name); - void lowercase(void); - - const std::string& type(void) const; - void set_type(const std::string& type); - - bool write(std::ostream& context) const; - bool read(std::istream& context); - - std::string to_string(void) const; - bool print(std::ostream&) const; - - private: - std::string m_name; - std::string m_type; - }; - - std::ostream& operator << (std::ostream&, const lm_unit_name&); - bool operator == (const lm_unit_name& l, const lm_unit_name& r); - bool operator < (const lm_unit_name& l, const lm_unit_name& r); - - //////////////////////////////////////////////////////////////////////////////// - // dependencies - //////////////////////////////////////////////////////////////////////////////// - - // dependencies on external files - - class lm_file_dependency - { - public: - lm_file_dependency(void); - lm_file_dependency(const std::string& library_path, const std::string& path, unsigned line = 0, unsigned column = 0); - ~lm_file_dependency(void); - - // a path can be retrieved as either a relative path to the library or as a - // full path by providing the library path as an argument - const std::string& path(void) const; - std::string path_full(const std::string& library_path) const; - void set_path(const std::string& library_path, const std::string& path); - - unsigned line(void) const; - void set_line(unsigned line = 0); - - unsigned column(void) const; - void set_column(unsigned column = 0); - - bool write(std::ostream& context) const; - bool read(std::istream& context); - - bool print(std::ostream&) const; - - private: - std::string m_path; // file dependencies are stored as paths relative to the containing library - unsigned m_line; // line - starts at 1, 0 means no line/column information - unsigned m_column; // column - starts at 0 - }; - - std::ostream& operator <<(std::ostream&, const lm_file_dependency&); - - // dependencies on other units - - class lm_unit_dependency - { - public: - lm_unit_dependency(void); - lm_unit_dependency(const std::string& library, const lm_unit_name& name); - ~lm_unit_dependency(void); - - const std::string& library(void) const; - void set_library(const std::string& library); - - const lm_unit_name& unit_name(void) const; - void set_unit_name(const lm_unit_name& unit_name); - - const std::string& name(void) const; - void set_name(const std::string& name); - - const std::string& type(void) const; - void set_type(const std::string& type); - - bool write(std::ostream& context) const; - bool read(std::istream& context); - - bool print(std::ostream&) const; - - private: - std::string m_library; - lm_unit_name m_name; - }; - - std::ostream& operator<<(std::ostream&, const lm_unit_dependency&); - - // the set of all dependencies - - class lm_dependencies - { - public: - lm_dependencies(void); - lm_dependencies(const lm_dependencies&); - lm_dependencies& operator=(const lm_dependencies&); - ~lm_dependencies(void); - - // source file dependency - void set_source_file(const lm_file_dependency&); - bool source_file_present(void) const; - const lm_file_dependency& source_file(void) const; - - // other file dependencies - unsigned file_add(const lm_file_dependency& dependency); - unsigned file_size(void) const; - const lm_file_dependency& file_dependency(unsigned) const; - void file_erase(unsigned); - - // unit dependencies - unsigned unit_add(const lm_unit_dependency& dependency); - unsigned unit_size(void) const; - const lm_unit_dependency& unit_dependency(unsigned) const; - void unit_erase(unsigned); - - void clear(void); - bool empty(void) const; - - bool write(std::ostream& context) const; - bool read(std::istream& context); - - bool print(std::ostream&) const; - - private: - lm_file_dependency* m_source; // source file dependency (optional) - std::vector m_files; // other file dependencies - std::vector m_units; // unit dependencies - }; - - std::ostream& operator << (std::ostream&, const lm_dependencies&); - - //////////////////////////////////////////////////////////////////////////////// - // library unit superclass - // user's units must be derivatives of lm_unit and overload all the virtuals - - class lm_unit - { - friend class lm_library; - public: - //////////////////////////////////////// - // constructor/destructor - - lm_unit(const lm_unit_name& name, lm_library* library); - virtual ~lm_unit(void); - - //////////////////////////////////////// - // Header data - - // unit name - const lm_unit_name& unit_name(void) const; - const std::string& name(void) const; - const std::string& type(void) const; - - // dependencies - // all file dependencies are converted for internal use to a path relative to the library - // they can be retrieved either in this form or as a full path - - // source file dependency - void set_source_file(const lm_file_dependency&); - bool source_file_present(void) const; - const lm_file_dependency& source_file(void) const; - - // other file dependencies - unsigned file_add(const lm_file_dependency& dependency); - unsigned file_size(void) const; - const lm_file_dependency& file_dependency(unsigned) const; - void file_erase(unsigned); - - // unit dependencies - unsigned unit_add(const lm_unit_dependency& dependency); - unsigned unit_size(void) const; - const lm_unit_dependency& unit_dependency(unsigned) const; - void unit_erase(unsigned); - - const lm_dependencies& dependencies(void) const; - void set_dependencies(const lm_dependencies&); - void clear_dependencies(void); - bool empty_dependencies(void) const; - - // dependency checking - - bool out_of_date(void) const; - bool up_to_date(void) const; - lm_dependencies out_of_date_reason(void) const; - - // supplementary data - - const std::string& supplementary_data(void) const; - void set_supplementary_data(const std::string& data); - - //////////////////////////////////////// - // unit data management - - bool load(void); - bool save(void); - bool loaded(void) const; - void mark(void); - - // file modified time - only changes after a save - - time_t modified(void) const; - - //////////////////////////////////////// - // containing library manager details - - // get the owning library - const lm_library* library(void) const; - lm_library* library(void); - - // owning library name and path - const std::string& library_name(void) const; - const std::string& library_path(void) const; - - //////////////////////////////////////// - // error handling - these apply to the last read/write operation - - bool error(void) const; - - //////////////////////////////////////// - // functions that customise subclasses of this superclass - // You MUST provide at least: - // - read - either read operation can be overloaded - // - write - either write operation can be overloaded - // - clone - - // read(filename) is the one actually called to read your data - // the default read(filename) simply calls read(istream) to actually read the file - // the default read(istream) does nothing but fail by returning false so you must overload one or other - - // you can just overload read(istream) if you want to use IOstream, or you - // can overload read(filename) to use any I/O system - - virtual bool read(const std::string& filename, void* type_data); - virtual bool read(std::istream& file, void* type_data); - - // as above, but for writing the data type - - // write(filename) is the one actually called to write your data - // the default write(filename) simply calls write(ostream) to actually write the file - // the default write(ostream) does nothing but fail by returning false so you must overload one or other - - // you can just overload write(ostream) if you want to use IOstream, or you - // can overload write(filename) to use any I/O system - - virtual bool write(const std::string& filename, void* type_data); - virtual bool write(std::ostream& file, void* type_data); - - // purge clears any memory associated with the unit - makes the unit unloaded - // the default does nothing - virtual bool purge(void); - - // the clone function creates a new-ed copy of the subclass - virtual lm_unit* clone(void) const; - - // the default print routines print header-only information - // you can overload these to provide a debug printout of the data structure - virtual bool print(std::ostream&) const; - virtual bool print_long(std::ostream&) const; - - protected: - // header file management - std::string filename(void) const; - std::string header_filename(void) const; - bool write_header(void); - bool read_header(void); - - private: - // header fields - lm_unit_name m_name; // name - lm_dependencies m_dependencies; // file and unit dependencies - std::string m_supplement; // supplementary data - bool m_header_modified; // header modified - - // internal fields - bool m_loaded; // loaded flag - bool m_marked; // mark - determines whether the unit needs saving - - // library manager fields - lm_library* m_library; // parent library - - // error handling fields - bool m_error; // error flag if load or save fails - from IOstream - }; - - // Iostream print calls the short print method - std::ostream& operator << (std::ostream& str, const lm_unit& u); - - //////////////////////////////////////////////////////////////////////////////// - // other types used in the library manager - //////////////////////////////////////////////////////////////////////////////// - - // user types - - typedef smart_ptr_nocopy lm_unit_ptr; - typedef lm_unit* (*lm_create_callback)(const lm_unit_name& unit_name, lm_library* parent_library, void* type_data); - - // internal types used in the library manager but made global because they are shared - - struct lm_callback_entry - { - lm_create_callback m_callback; - std::string m_description; - void* m_type_data; - - lm_callback_entry(lm_create_callback callback = 0, const std::string& description = std::string(), void* type_data = 0) : - m_callback(callback), m_description(description), m_type_data(type_data) {} - }; - - //////////////////////////////////////////////////////////////////////////////// - // Library - // Must be contained in a library_manager - // Manages objects of class lm_unit and its subclasses - //////////////////////////////////////////////////////////////////////////////// - - class lm_library - { - public: - friend class library_manager; - friend class lm_unit; - - ////////////////////////////////////////////////////////////////////////////// - // constructors/destructor - lm_library should only ever be constructed by library_manager - - lm_library(library_manager* manager); - lm_library(const lm_library&); - lm_library& operator = (const lm_library&); - ~lm_library(void); - - public: - - const library_manager* manager(void) const; - library_manager* manager(void); - - ////////////////////////////////////////////////////////////////////////////// - // initialisers - - bool create(const std::string& name, const std::string& path, bool writable); - bool open(const std::string& path); - - ////////////////////////////////////////////////////////////////////////////// - // management of types - - bool load_type(const std::string& type); - bool load_types(void); - bool remove_type(const std::string& type); - - ////////////////////////////////////////////////////////////////////////////// - // whole library operations - - bool load(void); - bool save(void); - bool purge(void); - bool close(void); - bool erase(void); - - const std::string& name(void) const; - const std::string& path(void) const; - - ////////////////////////////////////////////////////////////////////////////// - // managing read/write status - - bool set_read_write(bool writable); - bool set_writable(void); - bool set_read_only(void); - bool writable(void) const; - bool read_only(void) const; - bool os_writable(void) const; - bool os_read_only(void) const; - bool lm_writable(void) const; - bool lm_read_only(void) const; - - ////////////////////////////////////////////////////////////////////////////// - // unit management - - bool exists(const lm_unit_name& name) const; - lm_unit_ptr create(const lm_unit_name&); - bool loaded(const lm_unit_name& name) const; - bool load(const lm_unit_name& unit); - bool purge(const lm_unit_name& unit); - bool save(const lm_unit_name& unit); - bool erase(const lm_unit_name& name); - bool mark(const lm_unit_name& name); - time_t modified(const lm_unit_name& name) const; - - bool erase_by_source(const std::string& source_file); - - const lm_unit_ptr find(const lm_unit_name& name) const; - lm_unit_ptr find(const lm_unit_name& name); - - std::vector names(void) const; - std::vector names(const std::string& type) const; - std::vector handles(void) const; - std::vector handles(const std::string& type) const; - - ////////////////////////////////////////////////////////////////////////////// - // dependency checking - - bool out_of_date(const lm_unit_name& name) const; - bool up_to_date(const lm_unit_name& name) const; - lm_dependencies out_of_date_reason(const lm_unit_name& name) const; - - std::pair tidy(void); - - ////////////////////////////////////////////////////////////////////////////// - // do-everything print function - - bool pretty_print(std::ostream& str, - bool print_units = false, // print the unit names not just the library names - const std::string& type = std::string()) const; // print just this type ("" means all) - - //////////////////////////////////////////////////////////////////////////////// - // diagnostic print routines - - bool print(std::ostream& str) const; - bool print_long(std::ostream& str) const; - - private: - - std::map::iterator local_find(const lm_unit_name& name); - std::map::const_iterator local_find(const lm_unit_name& name) const; - - std::string m_name; // name - std::string m_path; // path - bool m_writable; // writable - std::map m_units; // units - library_manager* m_manager; // parent library manager - }; - - std::ostream& operator << (std::ostream& str, const lm_library& lib); - - //////////////////////////////////////////////////////////////////////////////// - // Library Manager - //////////////////////////////////////////////////////////////////////////////// - - class library_manager - { - public: - friend class lm_library; - friend class lm_unit; - - //////////////////////////////////////////////////////////////////////////////// - // static functions allow you to test whether a directory is a library before opening it - // you can also find the library's name without opening it - - static bool is_library(const std::string& path, const std::string& owner); - static std::string library_name(const std::string& path, const std::string& owner); - - // non-static forms test for libraries with the same owner as the library manager - - bool is_library(const std::string& path); - std::string library_name(const std::string& path); - - ////////////////////////////////////////////////////////////////////////////// - // tructors - - explicit library_manager(const std::string& owner, bool library_case = false, bool unit_case = false); - ~library_manager(void); - - ////////////////////////////////////////////////////////////////////////////// - // case sensitivity - - bool library_case(void) const; - void set_library_case(bool library_case); - - bool unit_case(void) const; - void set_unit_case(bool library_case); - - ////////////////////////////////////////////////////////////////////////////// - // type handling - // only units of types added in this way will be recognised - - bool add_type(const std::string& type, - const std::string& description, - lm_create_callback fn = 0, - void* type_data = 0); - bool remove_type(const std::string& type); - std::vector types(void) const; - - std::string description(const std::string& type) const; - lm_create_callback callback(const std::string& type) const; - void* type_data(const std::string& type) const; - - ////////////////////////////////////////////////////////////////////////////// - // Library mappings - // The library manager implements two different styles of library mappings - // - mapping file - // - ini file - // mapping file handling uses a simple text file to store the mappings in an internally-defined format - // ini file handling stores library mappings using the ini_manager component - // These modes are switched on by simply specifying a mapping file or an ini file to hold the mappings - - // mapping file methods - - // set but do not load - use this when you want to create a new mapping file - void set_mapping_file(const std::string& mapping_file); - // set and load - use this with an existing mapping file - bool load_mappings (const std::string& mapping_file); - // return the mapping file string - std::string mapping_file(); - - // ini file methods - the ini manager must be pre-loaded with the list of ini files to manage - - // set and load - this will create the relevant sections in the local ini file if not present already - bool set_ini_manager(ini_manager* ini_files, const std::string& library_section, const std::string& work_section); - ini_manager* get_ini_manager(void) const; - - // save to the library mapping handler, whichever kind it is - bool save_mappings (void); - - ////////////////////////////////////////////////////////////////////////////// - // library management - - // operations on a single library - // test whether a named library exists - bool exists(const std::string& name) const; - // create a new libarry in the specified directory - lm_library* create(const std::string& name, const std::string& path, bool writable = true); - // open an existing library - lm_library* open(const std::string& path); - // load all units in the library - bool load(const std::string& name); - // save all marked units in the library - bool save(const std::string& name); - // purge all loaded units in the library - bool purge(const std::string& name); - // close the library - remove it from the manager but leave on disk - bool close(const std::string& name); - // erase the library - delete the directory and remove the library from the manager - bool erase(const std::string& name); - - // operations on all libraries - as above but applied to all the libraries in the manager - bool load(void); - bool save(void); - bool purge(void); - bool close(void); - bool erase(void); - - // get name and path of a library - name can differ in case if the library manager is case-insensitive - std::string name(const std::string& library) const; - std::string path(const std::string& library) const; - - // control and test read/write status - bool set_writable(const std::string& library); - bool set_read_only(const std::string& library); - bool writable(const std::string& library) const; - bool read_only(const std::string& library) const; - bool os_writable(const std::string& library) const; - bool os_read_only(const std::string& library) const; - bool lm_writable(const std::string& library) const; - bool lm_read_only(const std::string& library) const; - - // find a library in the manager - returns null if not found - lm_library* find(const std::string& name); - const lm_library* find(const std::string& name) const; - - // get the set of all library names - std::vector names(void) const; - // get the set of all libraries - std::vector handles(void) const; - std::vector handles(void); - - ////////////////////////////////////////////////////////////////////////////// - // current library management - - bool setwork(const std::string& library); - bool unsetwork(void); - const lm_library* work(void) const; - lm_library* work(void); - std::string work_name(void) const; - - ////////////////////////////////////////////////////////////////////////////// - // unit management within a library - // Note: you can also manipulate the library class through a handle returned by find() or handles() - - bool exists(const std::string& library, const lm_unit_name& name) const; - lm_unit_ptr create(const std::string& library, const lm_unit_name& name); - bool loaded(const std::string& library, const lm_unit_name& name) const; - bool load(const std::string& library, const lm_unit_name& name); - bool purge(const std::string& library, const lm_unit_name& name); - bool save(const std::string& library, const lm_unit_name& name); - bool erase(const std::string& library, const lm_unit_name& name); - bool mark(const std::string& library, const lm_unit_name& name); - time_t modified(const std::string& library, const lm_unit_name& name) const; - - bool erase_by_source(const std::string& source_file); - - const lm_unit_ptr find(const std::string& library, const lm_unit_name& name) const; - lm_unit_ptr find(const std::string& library, const lm_unit_name& name); - - std::vector names(const std::string& library) const; - std::vector names(const std::string& library, const std::string& type) const; - std::vector handles(const std::string& library) const; - std::vector handles(const std::string& library, const std::string& type) const; - - ////////////////////////////////////////////////////////////////////////////// - // dependency checking - - bool out_of_date(const std::string& library, const lm_unit_name& name) const; - bool up_to_date(const std::string& library, const lm_unit_name& name) const; - lm_dependencies out_of_date_reason(const std::string& library, const lm_unit_name& name) const; - - // delete out of date units from a library or all libraries - // return the number of units tidied and a flag to say whether all units were successfully tidied - std::pair tidy(const std::string& library); - std::pair tidy(void); - - ////////////////////////////////////////////////////////////////////////////// - // do-everything print routine! - - bool pretty_print(std::ostream& str, - bool print_units = false, // print the unit names not just the library names - const std::string& library = std::string(), // print just the specified library ("" means all) - const std::string& type = std::string()) const; // print just this type ("" means all) - - //////////////////////////////////////////////////////////////////////////////// - // diagnostic print routines - - bool print(std::ostream& str) const; - bool print_long(std::ostream& str) const; - - ////////////////////////////////////////////////////////////////////////////// - // internals - - private: - // NOT a copyable object - library_manager(const library_manager&); - library_manager& operator = (const library_manager&); - - protected: - std::list::iterator local_find(const std::string& name); - std::list::const_iterator local_find(const std::string& name) const; - - std::string m_owner; // owner application name - std::string m_mapping_file; // mapping file method of library management - ini_manager* m_ini_files; // ini manager method of library management - std::string m_ini_section; // ini manager method of library management - std::string m_ini_work; // ini manager method of library management - std::list m_libraries; // libraries - std::string m_work; // work library - std::map m_callbacks; // callbacks - bool m_library_case; // case sensitivity for library names - bool m_unit_case; // case sensitivity for unit names - }; - - std::ostream& operator << (std::ostream& str, const library_manager& libraries); - - //////////////////////////////////////////////////////////////////////////////// - -} // end namespace stlplus - -#endif diff --git a/src/stlplus/subsystems/message_handler.cpp b/src/stlplus/subsystems/message_handler.cpp deleted file mode 100644 index fd1d2e8..0000000 --- a/src/stlplus/subsystems/message_handler.cpp +++ /dev/null @@ -1,2014 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// -#include "message_handler.hpp" -#include "dprintf.hpp" -#include -#include -#include -#include -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - // Utilities - - static std::ostream& operator<< (std::ostream& device, const std::vector& data) - { - for (unsigned i = 0; i < data.size(); i++) - device << data[i] << std::endl; - device.flush(); - return device; - } - - //////////////////////////////////////////////////////////////////////////////// - - static const char* information_id = "INFORMATION"; - static const char* context_id = "CONTEXT"; - static const char* supplement_id = "SUPPLEMENT"; - static const char* warning_id = "WARNING"; - static const char* error_id = "ERROR"; - static const char* fatal_id = "FATAL"; - static const char* position_id = "POSITION"; - - static const char* default_information_format = "@0"; - static const char* default_context_format = "context: @0"; - static const char* default_supplement_format = "supplement: @0"; - static const char* default_warning_format = "warning: @0"; - static const char* default_error_format = "error: @0"; - static const char* default_fatal_format = "FATAL: @0"; - static const char* default_position_format = "\"@1\" (@2,@3) : @0"; - - //////////////////////////////////////////////////////////////////////////////// - // position class - - message_position::message_position(void) : - m_filename(std::string()), m_line(0), m_column(0) - { - } - - message_position::message_position(const std::string& filename, unsigned line, unsigned column) : - m_filename(filename), m_line(line), m_column(column) - { - } - - message_position::~message_position(void) - { - } - - const std::string& message_position::filename(void) const - { - return m_filename; - } - - unsigned message_position::line(void) const - { - return m_line; - } - - unsigned message_position::column(void) const - { - return m_column; - } - - message_position message_position::operator + (unsigned offset) const - { - message_position result(*this); - result += offset; - return result; - } - - message_position& message_position::operator += (unsigned offset) - { - m_column += offset; - return *this; - } - - bool message_position::empty(void) const - { - return m_filename.empty(); - } - - bool message_position::valid(void) const - { - return !empty(); - } - - std::vector message_position::show(void) const - { - std::vector result; - if (valid()) - { - // skip row-1 lines of the file and print out the resultant line - std::ifstream source(filename().c_str()); - std::string current_line; - for (unsigned i = 0; i < line(); i++) - { - if (!source.good()) - return result; - std::getline(source,current_line); - } - result.push_back(current_line); - // now put an up-arrow at the appropriate column - // preserve any tabs in the original line - result.push_back(std::string()); - for (unsigned j = 0; j < column(); j++) - { - if (j < current_line.size() && current_line[j] == '\t') - result.back() += '\t'; - else - result.back() += ' '; - } - result.back() += '^'; - } - return result; - } - - std::string to_string(const message_position& where) - { - return dformat("{%s:%u:%u}", where.filename().c_str(), where.line(), where.column()); - } - - //////////////////////////////////////////////////////////////////////////////// - // context subclass - - class message_context_body - { - private: - unsigned m_depth; - message_handler_base* m_base; - - public: - message_context_body(message_handler_base& handler) : - m_depth(0), m_base(0) - { - set(handler); - } - - ~message_context_body(void) - { - pop(); - } - - void set(message_handler_base& handler) - { - m_base = &handler; - m_depth = m_base->context_depth(); - } - - void pop(void) - { - if (m_base) - m_base->pop_context(m_depth); - } - private: - message_context_body(const message_context_body&); - message_context_body& operator=(const message_context_body&); - }; - - // exported context class - - message_context::message_context(message_handler_base& handler) : m_body(new message_context_body(handler)) - { - } - - void message_context::set(message_handler_base& handler) - { - m_body->set(handler); - } - - void message_context::pop(void) - { - m_body->pop(); - } - - //////////////////////////////////////////////////////////////////////////////// - // exceptions - - // read_error - - message_handler_read_error::message_handler_read_error(const message_position& position, const std::string& reason) : - std::runtime_error(to_string(position) + std::string(": Message handler read error - ") + reason), - m_position(position) - { - } - - message_handler_read_error::~message_handler_read_error(void) throw() - { - } - - const message_position& message_handler_read_error::where(void) const - { - return m_position; - } - - // format error - - message_handler_format_error::message_handler_format_error(const std::string& format, unsigned offset) : - std::runtime_error(std::string("Message handler formatting error in \"") + format + std::string("\"")), - m_position("",0,0), m_format(format), m_offset(offset) - { - } - - message_handler_format_error::message_handler_format_error(const message_position& pos, const std::string& format, unsigned offset) : - std::runtime_error(to_string(pos) + std::string(": Message handler formatting error in \"") + format + std::string("\"")), - m_position(pos), m_format(format), m_offset(offset) - { - } - - message_handler_format_error::~message_handler_format_error(void) throw() - { - } - - const message_position& message_handler_format_error::where(void) const - { - return m_position; - } - - const std::string& message_handler_format_error::format(void) const - { - return m_format; - } - - unsigned message_handler_format_error::offset(void) const - { - return m_offset; - } - - // id_error - - message_handler_id_error::message_handler_id_error(const std::string& id) : - std::runtime_error(std::string("Message handler message ID not found: ") + id), m_id(id) - { - } - - message_handler_id_error::~message_handler_id_error(void) throw() - { - } - - const std::string& message_handler_id_error::id(void) const - { - return m_id; - } - - // limit_error - - message_handler_limit_error::message_handler_limit_error(unsigned limit) : - std::runtime_error(std::string("Message handler limit error: ") + dformat("%u",limit) + std::string(" reached")), - m_limit(limit) - { - } - - message_handler_limit_error::~message_handler_limit_error(void) throw() - { - } - - unsigned message_handler_limit_error::limit(void) const - { - return m_limit; - } - - // fatal_error - - message_handler_fatal_error::message_handler_fatal_error(const std::string& id) : - std::runtime_error(std::string("Message Handler Fatal error: ") + id), - m_id(id) - { - } - - message_handler_fatal_error::~message_handler_fatal_error(void) throw() - { - } - - const std::string& message_handler_fatal_error::id(void) const - { - return m_id; - } - - //////////////////////////////////////////////////////////////////////////////// - - class message - { - private: - unsigned m_index; // index into message files - unsigned m_line; // line - unsigned m_column; // column - std::string m_text; // text - - public: - message(unsigned index = (unsigned)-1, - unsigned line = 0, - unsigned column = 0, - const std::string& text = "") : - m_index(index),m_line(line),m_column(column),m_text(text) - { - } - message(const std::string& text) : - m_index((unsigned)-1),m_line(0),m_column(0),m_text(text) - { - } - - ~message(void) - { - } - - unsigned index(void) const - { - return m_index; - } - - unsigned line(void) const - { - return m_line; - } - - unsigned column(void) const - { - return m_column; - } - - const std::string& text(void) const - { - return m_text; - } - }; - - //////////////////////////////////////////////////////////////////////////////// - - class pending_message - { - private: - message_position m_position; - std::string m_id; - std::vector m_args; - public: - pending_message(const message_position& position, const std::string& id, const std::vector& args) : - m_position(position), m_id(id), m_args(args) {} - pending_message(const std::string& id, const std::vector& args) : - m_position(message_position()), m_id(id), m_args(args) {} - ~pending_message(void) {} - - const message_position& position(void) const {return m_position;} - const std::string& id(void) const {return m_id;} - const std::vector& args(void) const {return m_args;} - }; - - //////////////////////////////////////////////////////////////////////////////// - - class message_handler_base_body - { - public: - std::vector m_files; // message files in the order they were added - std::map m_messages; // messages stored as id:message pairs - bool m_show; // show source - std::list m_context; // context message stack - std::list m_supplement; // supplementary message stack - - public: - message_handler_base_body(void) : - m_show(false) - { - // preload with default formats - add_message(information_id,default_information_format); - add_message(warning_id,default_warning_format); - add_message(error_id,default_error_format); - add_message(fatal_id,default_fatal_format); - add_message(position_id,default_position_format); - add_message(context_id,default_context_format); - add_message(supplement_id,default_supplement_format); - } - - ~message_handler_base_body(void) - { - } - - void set_show(bool show) - { - m_show = show; - } - - void add_message_file(const std::string& file) - throw(message_handler_read_error) - { - m_files.push_back(file); - std::ifstream input(file.c_str()); - if (!input.good()) - throw message_handler_read_error(message_position(file,0,0), std::string("file not found: ") + file); - std::string line; - unsigned l = 0; - while(input.good()) - { - std::getline(input,line); - l++; - if (line.size() > 0 && isalpha(line[0])) - { - // Get the id field which starts with an alphabetic and contains alphanumerics and underscore - std::string id; - unsigned i = 0; - for (; i < line.size(); i++) - { - if (isalnum(line[i]) || line[i] == '_') - id += line[i]; - else - break; - } - // check this ID is unique within the files - however it is legal to override a hard-coded message - std::map::iterator found = m_messages.find(id); - if (found != m_messages.end() && found->second.index() != (unsigned)-1) - throw message_handler_read_error(message_position(file,l,i), std::string("repeated ID ") + id); - // skip whitespace - for (; i < line.size(); i++) - { - if (!isspace(line[i])) - break; - } - // now get the text part and add the message to the message map - std::string text = line.substr(i, line.size()-i); - m_messages[id] = message(m_files.size()-1, l, i, text); - } - } - } - - void add_message(const std::string& id, const std::string& text) - throw() - { - m_messages[id] = message((unsigned)-1, 0, 0, text); - } - - bool message_present(const std::string& id) const - throw() - { - return m_messages.find(id) != m_messages.end(); - } - - void push_supplement(const message_position& position, - const std::string& message_id, - const std::vector& args) - { - m_supplement.push_back(pending_message(position,message_id,args)); - } - - void push_context(const message_position& position, - const std::string& message_id, - const std::vector& args) - { - m_context.push_back(pending_message(position,message_id,args)); - } - - void pop_context(unsigned depth) - { - while (depth < m_context.size()) - m_context.pop_back(); - } - - unsigned context_depth(void) const - { - return m_context.size(); - } - - std::vector format_report(const message_position& position, - const std::string& message_id, - const std::string& status_id, - const std::vector& args) - throw(message_handler_id_error,message_handler_format_error) - { - // gathers everything together into a full multi-line report - std::vector result; - // the first part of the report is the status message (info/warning/error/fatal) - result.push_back(format_id(position, message_id, status_id, args)); - // now append any supplemental messages that have been stacked - // these are printed in FIFO order i.e. the order that they were added to the handler - for (std::list::iterator j = m_supplement.begin(); j != m_supplement.end(); j++) - result.push_back(format_id(j->position(),j->id(),supplement_id,j->args())); - // now discard any supplementary messages because they only persist until they are printed once - m_supplement.clear(); - // now append any context messages that have been stacked - // these are printed in LIFO order i.e. closest context first - for (std::list::reverse_iterator i = m_context.rbegin(); i != m_context.rend(); i++) - result.push_back(format_id(i->position(),i->id(),context_id,i->args())); - return result; - } - - std::string format_id(const message_position& position, - const std::string& message_id, - const std::string& status_id, - const std::vector& args) - throw(message_handler_id_error,message_handler_format_error) - { - // This function creates a fully-formatted single-line message from a - // combination of the position format and the status format plus the message - // ID and its arguments. There are up to three levels of substitution - // required to do this. - - // get the status format from the status_id - std::map::iterator status_found = m_messages.find(status_id); - if (status_found == m_messages.end()) throw message_handler_id_error(status_id); - - // similarly get the message format - std::map::iterator message_found = m_messages.find(message_id); - if (message_found == m_messages.end()) throw message_handler_id_error(message_id); - - // format the message contents - std::string message_text = format_message(message_found->second, args); - - // now format the message in the status string (informational, warning etc). - std::vector status_args; - status_args.push_back(message_text); - std::string result = format_message(status_found->second, status_args); - - // finally, if the message is positional, format the status message in the positional string - if (position.valid()) - { - // get the position format from the message set - std::map::iterator position_found = m_messages.find(position_id); - if (position_found == m_messages.end()) throw message_handler_id_error(position_id); - - // now format the message - std::vector position_args; - position_args.push_back(result); - position_args.push_back(position.filename()); - position_args.push_back(dformat("%u",position.line())); - position_args.push_back(dformat("%u",position.column())); - result = format_message(position_found->second, position_args); - // add the source file text and position if that option is on - - if (m_show) - { - std::vector show = position.show(); - for (unsigned i = 0; i < show.size(); i++) - { - result += std::string("\n"); - result += show[i]; - } - } - } - return result; - } - - std::string format_message(const message& mess, - const std::vector& args) - throw(message_handler_format_error) - { - // this function creates a formatted string from the stored message text and - // the arguments. Most of the work is done in format_string. However, if a - // formatting error is found, this function uses extra information stored in - // the message data structure to improve the reporting of the error - try - { - // This is the basic string formatter which performs parameter substitution - // into a message. Parameter placeholders are in the form @0, @1 etc, where - // the number is the index of the argument in the args vector. At present, - // no field codes are supported as in printf formats Algorithm: scan the - // input string and transfer it into the result. When an @nnn appears, - // substitute the relevant argument from the args vector. Throw an exception - // if its out of range. Also convert C-style escaped characters of the form - // \n. - std::string format = mess.text(); - std::string result; - for (unsigned i = 0; i < format.size(); ) - { - if (format[i] == '@') - { - // an argument substitution has been found - now find the argument number - if (i+1 == format.size()) throw message_handler_format_error(format, i); - i++; - // check for @@ which is an escaped form of '@' - if (format[i] == '@') - { - result += '@'; - i++; - } - else - { - // there must be at least one digit in the substitution number - if (!isdigit(format[i])) throw message_handler_format_error(format,i); - unsigned a = 0; - for (; i < format.size() && isdigit(format[i]); i++) - { - a *= 10; - a += (format[i] - '0'); - } - // check for an argument request out of the range of the set of arguments - if (a >= args.size()) throw message_handler_format_error(format,i-1); - result += args[a]; - } - } - else if (format[i] == '\\') - { - // an escaped character has been found - if (i+1 == format.size()) throw message_handler_format_error(format, i); - i++; - // do the special ones first, then all the others just strip off the \ and leave the following characters - switch(format[i]) - { - case '\\': - result += '\\'; - break; - case 't': - result += '\t'; - break; - case 'n': - result += '\n'; - break; - case 'r': - result += '\r'; - break; - case 'a': - result += '\a'; - break; - default: - result += format[i]; - break; - } - i++; - } - else - { - // plain text found - just append to the result - result += format[i++]; - } - } - return result; - } - catch(message_handler_format_error& exception) - { - // if the message came from a message file, improve the error reporting by adding file positional information - // Also adjust the position from the start of the text (stored in the message field) to the column of the error - if (mess.index() != (unsigned)-1) - throw message_handler_format_error( - message_position(m_files[mess.index()], mess.line(), mess.column()+exception.offset()), - exception.format(), - exception.offset()); - else - throw exception; - } - } - - private: - message_handler_base_body(message_handler_base_body&); - message_handler_base_body& operator=(message_handler_base_body&); - }; - - //////////////////////////////////////////////////////////////////////////////// - - message_handler_base::message_handler_base(bool show) - throw() : - m_base_body(new message_handler_base_body) - { - m_base_body->set_show(show); - } - - message_handler_base::message_handler_base(const std::string& file, bool show) - throw(message_handler_read_error) : - m_base_body(new message_handler_base_body) - { - m_base_body->set_show(show); - add_message_file(file); - } - - message_handler_base::message_handler_base(const std::vector& files, bool show) - throw(message_handler_read_error) : - m_base_body(new message_handler_base_body) - { - m_base_body->set_show(show); - add_message_files(files); - } - - message_handler_base::~message_handler_base(void) - throw() - { - } - - //////////////////////////////////////////////////////////////////////////////// - - void message_handler_base::add_message_file(const std::string& file) - throw(message_handler_read_error) - { - m_base_body->add_message_file(file); - } - - void message_handler_base::add_message_files(const std::vector& files) - throw(message_handler_read_error) - { - for (unsigned i = 0; i < files.size(); i++) - add_message_file(files[i]); - } - - void message_handler_base::add_message(const std::string& id, const std::string& text) - throw() - { - m_base_body->add_message(id,text); - } - - bool message_handler_base::message_present(const std::string& id) const - throw() - { - return m_base_body->message_present(id); - } - - //////////////////////////////////////////////////////////////////////////////// - - void message_handler_base::set_information_format(const std::string& format) throw() - { - add_message(information_id, format); - } - - void message_handler_base::set_warning_format(const std::string& format) throw() - { - add_message(warning_id, format); - } - - void message_handler_base::set_error_format(const std::string& format) throw() - { - add_message(error_id, format); - } - - void message_handler_base::set_fatal_format(const std::string& format) throw() - { - add_message(fatal_id, format); - } - - void message_handler_base::set_position_format(const std::string& format) throw() - { - add_message(position_id, format); - } - - void message_handler_base::set_context_format(const std::string& format) throw() - { - add_message(context_id, format); - } - - void message_handler_base::set_supplement_format(const std::string& format) throw() - { - add_message(supplement_id, format); - } - - //////////////////////////////////////////////////////////////////////////////// - - void message_handler_base::show_position(void) - throw() - { - m_base_body->set_show(true); - } - - void message_handler_base::hide_position(void) - throw() - { - m_base_body->set_show(false); - } - - //////////////////////////////////////////////////////////////////////////////// - // information messages - - std::vector message_handler_base::information_message(const std::string& id, - const std::vector& args) - throw(message_handler_id_error,message_handler_format_error) - { - return information_message(message_position(), id, args); - } - - std::vector message_handler_base::information_message(const std::string& id) - throw(message_handler_id_error,message_handler_format_error) - { - std::vector args; - return information_message(id, args); - } - - std::vector message_handler_base::information_message(const std::string& id, - const std::string& arg1) - throw(message_handler_id_error,message_handler_format_error) - { - std::vector args; - args.push_back(arg1); - return information_message(id, args); - } - - std::vector message_handler_base::information_message(const std::string& id, - const std::string& arg1, - const std::string& arg2) - throw(message_handler_id_error,message_handler_format_error) - { - std::vector args; - args.push_back(arg1); - args.push_back(arg2); - return information_message(id, args); - } - - std::vector message_handler_base::information_message(const std::string& id, - const std::string& arg1, - const std::string& arg2, - const std::string& arg3) - throw(message_handler_id_error,message_handler_format_error) - { - std::vector args; - args.push_back(arg1); - args.push_back(arg2); - args.push_back(arg3); - return information_message(id, args); - } - - std::vector message_handler_base::information_message(const message_position& position, - const std::string& id, - const std::vector& args) - throw(message_handler_id_error,message_handler_format_error) - { - return m_base_body->format_report(position, id, information_id, args); - } - - std::vector message_handler_base::information_message(const message_position& position, - const std::string& id) - throw(message_handler_id_error,message_handler_format_error) - { - std::vector args; - return information_message(position, id, args); - } - - std::vector message_handler_base::information_message(const message_position& position, - const std::string& id, - const std::string& arg1) - throw(message_handler_id_error,message_handler_format_error) - { - std::vector args; - args.push_back(arg1); - return information_message(position, id, args); - } - - std::vector message_handler_base::information_message(const message_position& position, - const std::string& id, - const std::string& arg1, - const std::string& arg2) - throw(message_handler_id_error,message_handler_format_error) - { - std::vector args; - args.push_back(arg1); - args.push_back(arg2); - return information_message(position, id, args); - } - - std::vector message_handler_base::information_message(const message_position& position, - const std::string& id, - const std::string& arg1, - const std::string& arg2, - const std::string& arg3) - throw(message_handler_id_error,message_handler_format_error) - { - std::vector args; - args.push_back(arg1); - args.push_back(arg2); - args.push_back(arg3); - return information_message(position, id, args); - } - - //////////////////////////////////////////////////////////////////////////////// - // warning messages - - std::vector message_handler_base::warning_message(const std::string& id, - const std::vector& args) - throw(message_handler_id_error,message_handler_format_error) - { - return warning_message(message_position(), id, args); - } - - std::vector message_handler_base::warning_message(const std::string& id) - throw(message_handler_id_error,message_handler_format_error) - { - std::vector args; - return warning_message(id, args); - } - - std::vector message_handler_base::warning_message(const std::string& id, - const std::string& arg1) - throw(message_handler_id_error,message_handler_format_error) - { - std::vector args; - args.push_back(arg1); - return warning_message(id, args); - } - - std::vector message_handler_base::warning_message(const std::string& id, - const std::string& arg1, - const std::string& arg2) - throw(message_handler_id_error,message_handler_format_error) - { - std::vector args; - args.push_back(arg1); - args.push_back(arg2); - return warning_message(id, args); - } - - std::vector message_handler_base::warning_message(const std::string& id, - const std::string& arg1, - const std::string& arg2, - const std::string& arg3) - throw(message_handler_id_error,message_handler_format_error) - { - std::vector args; - args.push_back(arg1); - args.push_back(arg2); - args.push_back(arg3); - return warning_message(id, args); - } - - std::vector message_handler_base::warning_message(const message_position& position, - const std::string& id, - const std::vector& args) - throw(message_handler_id_error,message_handler_format_error) - { - return m_base_body->format_report(position, id, warning_id, args); - } - - std::vector message_handler_base::warning_message(const message_position& position, - const std::string& id) - throw(message_handler_id_error,message_handler_format_error) - { - std::vector args; - return warning_message(position, id, args); - } - - std::vector message_handler_base::warning_message(const message_position& position, - const std::string& id, - const std::string& arg1) - throw(message_handler_id_error,message_handler_format_error) - { - std::vector args; - args.push_back(arg1); - return warning_message(position, id, args); - } - - std::vector message_handler_base::warning_message(const message_position& position, - const std::string& id, - const std::string& arg1, - const std::string& arg2) - throw(message_handler_id_error,message_handler_format_error) - { - std::vector args; - args.push_back(arg1); - args.push_back(arg2); - return warning_message(position, id, args); - } - - std::vector message_handler_base::warning_message(const message_position& position, - const std::string& id, - const std::string& arg1, - const std::string& arg2, - const std::string& arg3) - throw(message_handler_id_error,message_handler_format_error) - { - std::vector args; - args.push_back(arg1); - args.push_back(arg2); - args.push_back(arg3); - return warning_message(position, id, args); - } - - //////////////////////////////////////////////////////////////////////////////// - // error messages - - std::vector message_handler_base::error_message(const std::string& id, - const std::vector& args) - throw(message_handler_id_error,message_handler_format_error) - { - return error_message(message_position(), id, args); - } - - std::vector message_handler_base::error_message(const std::string& id) - throw(message_handler_id_error,message_handler_format_error) - { - std::vector args; - return error_message(id, args); - } - - std::vector message_handler_base::error_message(const std::string& id, - const std::string& arg1) - throw(message_handler_id_error,message_handler_format_error) - { - std::vector args; - args.push_back(arg1); - return error_message(id, args); - } - - std::vector message_handler_base::error_message(const std::string& id, - const std::string& arg1, - const std::string& arg2) - throw(message_handler_id_error,message_handler_format_error) - { - std::vector args; - args.push_back(arg1); - args.push_back(arg2); - return error_message(id, args); - } - - std::vector message_handler_base::error_message(const std::string& id, - const std::string& arg1, - const std::string& arg2, - const std::string& arg3) - throw(message_handler_id_error,message_handler_format_error) - { - std::vector args; - args.push_back(arg1); - args.push_back(arg2); - args.push_back(arg3); - return error_message(id, args); - } - - std::vector message_handler_base::error_message(const message_position& position, - const std::string& id, - const std::vector& args) - throw(message_handler_id_error,message_handler_format_error) - { - return m_base_body->format_report(position, id, error_id, args); - } - - std::vector message_handler_base::error_message(const message_position& position, - const std::string& id) - throw(message_handler_id_error,message_handler_format_error) - { - std::vector args; - return error_message(position, id, args); - } - - std::vector message_handler_base::error_message(const message_position& position, - const std::string& id, - const std::string& arg1) - throw(message_handler_id_error,message_handler_format_error) - { - std::vector args; - args.push_back(arg1); - return error_message(position, id, args); - } - - std::vector message_handler_base::error_message(const message_position& position, - const std::string& id, - const std::string& arg1, - const std::string& arg2) - throw(message_handler_id_error,message_handler_format_error) - { - std::vector args; - args.push_back(arg1); - args.push_back(arg2); - return error_message(position, id, args); - } - - std::vector message_handler_base::error_message(const message_position& position, - const std::string& id, - const std::string& arg1, - const std::string& arg2, - const std::string& arg3) - throw(message_handler_id_error,message_handler_format_error) - { - std::vector args; - args.push_back(arg1); - args.push_back(arg2); - args.push_back(arg3); - return error_message(position, id, args); - } - - //////////////////////////////////////////////////////////////////////////////// - // fatal messages - - std::vector message_handler_base::fatal_message(const std::string& id, - const std::vector& args) - throw(message_handler_id_error,message_handler_format_error) - { - return fatal_message(message_position(), id, args); - } - - std::vector message_handler_base::fatal_message(const std::string& id) - throw(message_handler_id_error,message_handler_format_error) - { - std::vector args; - return fatal_message(id, args); - } - - std::vector message_handler_base::fatal_message(const std::string& id, - const std::string& arg1) - throw(message_handler_id_error,message_handler_format_error) - { - std::vector args; - args.push_back(arg1); - return fatal_message(id, args); - } - - std::vector message_handler_base::fatal_message(const std::string& id, - const std::string& arg1, - const std::string& arg2) - throw(message_handler_id_error,message_handler_format_error) - { - std::vector args; - args.push_back(arg1); - args.push_back(arg2); - return fatal_message(id, args); - } - - std::vector message_handler_base::fatal_message(const std::string& id, - const std::string& arg1, - const std::string& arg2, - const std::string& arg3) - throw(message_handler_id_error,message_handler_format_error) - { - std::vector args; - args.push_back(arg1); - args.push_back(arg2); - args.push_back(arg3); - return fatal_message(id, args); - } - - std::vector message_handler_base::fatal_message(const message_position& position, - const std::string& id, - const std::vector& args) - throw(message_handler_id_error,message_handler_format_error) - { - return m_base_body->format_report(position, id, fatal_id, args); - } - - std::vector message_handler_base::fatal_message(const message_position& position, - const std::string& id) - throw(message_handler_id_error,message_handler_format_error) - { - std::vector args; - return fatal_message(position, id, args); - } - - std::vector message_handler_base::fatal_message(const message_position& position, - const std::string& id, - const std::string& arg1) - throw(message_handler_id_error,message_handler_format_error) - { - std::vector args; - args.push_back(arg1); - return fatal_message(position, id, args); - } - - std::vector message_handler_base::fatal_message(const message_position& position, - const std::string& id, - const std::string& arg1, - const std::string& arg2) - throw(message_handler_id_error,message_handler_format_error) - { - std::vector args; - args.push_back(arg1); - args.push_back(arg2); - return fatal_message(position, id, args); - } - - std::vector message_handler_base::fatal_message(const message_position& position, - const std::string& id, - const std::string& arg1, - const std::string& arg2, - const std::string& arg3) - throw(message_handler_id_error,message_handler_format_error) - { - std::vector args; - args.push_back(arg1); - args.push_back(arg2); - args.push_back(arg3); - return fatal_message(position, id, args); - } - - //////////////////////////////////////////////////////////////////////////////// - // supplemental messages - - void message_handler_base::push_supplement(const std::string& id, - const std::vector& args) - throw(message_handler_id_error,message_handler_format_error) - { - push_supplement(message_position(), id, args); - } - - void message_handler_base::push_supplement(const std::string& id) - throw(message_handler_id_error,message_handler_format_error) - { - std::vector args; - push_supplement(id, args); - } - - void message_handler_base::push_supplement(const std::string& id, - const std::string& arg1) - throw(message_handler_id_error,message_handler_format_error) - { - std::vector args; - args.push_back(arg1); - push_supplement(id, args); - } - - void message_handler_base::push_supplement(const std::string& id, - const std::string& arg1, - const std::string& arg2) - throw(message_handler_id_error,message_handler_format_error) - { - std::vector args; - args.push_back(arg1); - args.push_back(arg2); - push_supplement(id, args); - } - - void message_handler_base::push_supplement(const std::string& id, - const std::string& arg1, - const std::string& arg2, - const std::string& arg3) - throw(message_handler_id_error,message_handler_format_error) - { - std::vector args; - args.push_back(arg1); - args.push_back(arg2); - args.push_back(arg3); - push_supplement(id, args); - } - - void message_handler_base::push_supplement(const message_position& position, - const std::string& id, - const std::vector& args) - throw(message_handler_id_error,message_handler_format_error) - { - m_base_body->push_supplement(position, id, args); - } - - void message_handler_base::push_supplement(const message_position& position, - const std::string& id) - throw(message_handler_id_error,message_handler_format_error) - { - std::vector args; - push_supplement(position, id, args); - } - - void message_handler_base::push_supplement(const message_position& position, - const std::string& id, - const std::string& arg1) - throw(message_handler_id_error,message_handler_format_error) - { - std::vector args; - args.push_back(arg1); - push_supplement(position, id, args); - } - - void message_handler_base::push_supplement(const message_position& position, - const std::string& id, - const std::string& arg1, - const std::string& arg2) - throw(message_handler_id_error,message_handler_format_error) - { - std::vector args; - args.push_back(arg1); - args.push_back(arg2); - push_supplement(position, id, args); - } - - void message_handler_base::push_supplement(const message_position& position, - const std::string& id, - const std::string& arg1, - const std::string& arg2, - const std::string& arg3) - throw(message_handler_id_error,message_handler_format_error) - { - std::vector args; - args.push_back(arg1); - args.push_back(arg2); - args.push_back(arg3); - push_supplement(position, id, args); - } - - //////////////////////////////////////////////////////////////////////////////// - // context - - void message_handler_base::push_context (const std::string& id, - const std::vector& args) - throw(message_handler_id_error,message_handler_format_error) - { - push_context(message_position(), id, args); - } - - void message_handler_base::push_context (const std::string& id) - throw(message_handler_id_error,message_handler_format_error) - { - std::vector args; - push_context(id, args); - } - - void message_handler_base::push_context (const std::string& id, - const std::string& arg1) - throw(message_handler_id_error,message_handler_format_error) - { - std::vector args; - args.push_back(arg1); - push_context(id, args); - } - - void message_handler_base::push_context (const std::string& id, - const std::string& arg1, - const std::string& arg2) - throw(message_handler_id_error,message_handler_format_error) - { - std::vector args; - args.push_back(arg1); - args.push_back(arg2); - push_context(id, args); - } - - void message_handler_base::push_context (const std::string& id, - const std::string& arg1, - const std::string& arg2, - const std::string& arg3) - throw(message_handler_id_error,message_handler_format_error) - { - std::vector args; - args.push_back(arg1); - args.push_back(arg2); - args.push_back(arg3); - push_context(id, args); - } - - void message_handler_base::push_context (const message_position& position, - const std::string& id, - const std::vector& args) - throw(message_handler_id_error,message_handler_format_error) - { - m_base_body->push_context(position, id, args); - } - - void message_handler_base::push_context (const message_position& position, - const std::string& id) - throw(message_handler_id_error,message_handler_format_error) - { - std::vector args; - push_context(position, id, args); - } - - void message_handler_base::push_context (const message_position& position, - const std::string& id, - const std::string& arg1) - throw(message_handler_id_error,message_handler_format_error) - { - std::vector args; - args.push_back(arg1); - push_context(position, id, args); - } - - void message_handler_base::push_context (const message_position& position, - const std::string& id, - const std::string& arg1, - const std::string& arg2) - throw(message_handler_id_error,message_handler_format_error) - { - std::vector args; - args.push_back(arg1); - args.push_back(arg2); - push_context(position, id, args); - } - - void message_handler_base::push_context (const message_position& position, - const std::string& id, - const std::string& arg1, - const std::string& arg2, - const std::string& arg3) - throw(message_handler_id_error,message_handler_format_error) - { - std::vector args; - args.push_back(arg1); - args.push_back(arg2); - args.push_back(arg3); - push_context(position, id, args); - } - - void message_handler_base::pop_context(void) throw() - { - m_base_body->pop_context(m_base_body->context_depth()-1); - } - - void message_handler_base::pop_context(unsigned depth) throw() - { - m_base_body->pop_context(depth); - } - - unsigned message_handler_base::context_depth(void) const - throw() - { - return m_base_body->context_depth(); - } - - message_context message_handler_base::auto_push_context(const std::string& id, const std::vector& args) - throw(message_handler_id_error,message_handler_format_error) - { - return auto_push_context(message_position(), id, args); - } - - message_context message_handler_base::auto_push_context(const std::string& id) - throw(message_handler_id_error,message_handler_format_error) - { - std::vector args; - return auto_push_context(id, args); - } - - message_context message_handler_base::auto_push_context(const std::string& id, - const std::string& arg1) - throw(message_handler_id_error,message_handler_format_error) - { - std::vector args; - args.push_back(arg1); - return auto_push_context(id, args); - } - - message_context message_handler_base::auto_push_context (const std::string& id, - const std::string& arg1, - const std::string& arg2) - throw(message_handler_id_error,message_handler_format_error) - { - std::vector args; - args.push_back(arg1); - args.push_back(arg2); - return auto_push_context(id, args); - } - - message_context message_handler_base::auto_push_context(const std::string& id, - const std::string& arg1, - const std::string& arg2, - const std::string& arg3) - throw(message_handler_id_error,message_handler_format_error) - { - std::vector args; - args.push_back(arg1); - args.push_back(arg2); - args.push_back(arg3); - return auto_push_context(id, args); - } - - message_context message_handler_base::auto_push_context(const message_position& position, - const std::string& id, - const std::vector& args) - throw(message_handler_id_error,message_handler_format_error) - { - message_context result(*this); - m_base_body->push_context(position, id, args); - return result; - } - - message_context message_handler_base::auto_push_context(const message_position& position, - const std::string& id) - throw(message_handler_id_error,message_handler_format_error) - { - std::vector args; - return auto_push_context(position, id, args); - } - - message_context message_handler_base::auto_push_context(const message_position& position, - const std::string& id, - const std::string& arg1) - throw(message_handler_id_error,message_handler_format_error) - { - std::vector args; - args.push_back(arg1); - return auto_push_context(position, id, args); - } - - message_context message_handler_base::auto_push_context(const message_position& position, - const std::string& id, - const std::string& arg1, - const std::string& arg2) - throw(message_handler_id_error,message_handler_format_error) - { - std::vector args; - args.push_back(arg1); - args.push_back(arg2); - return auto_push_context(position, id, args); - } - - message_context message_handler_base::auto_push_context(const message_position& position, - const std::string& id, - const std::string& arg1, - const std::string& arg2, - const std::string& arg3) - throw(message_handler_id_error,message_handler_format_error) - { - std::vector args; - args.push_back(arg1); - args.push_back(arg2); - args.push_back(arg3); - return auto_push_context(position, id, args); - } - - //////////////////////////////////////////////////////////////////////////////// - // iostream-based derivative uses the above base class to generate messages then uses iostream to print them - //////////////////////////////////////////////////////////////////////////////// - - class message_handler_body - { - private: - std::ostream* m_device; // TextIO output device - unsigned m_limit; // error limit - unsigned m_errors; // error count - - public: - message_handler_body(std::ostream& device, unsigned limit) : - m_device(&device), m_limit(limit), m_errors(0) - { - } - - ~message_handler_body(void) - { - device().flush(); - } - - std::ostream& device(void) - { - return *m_device; - } - - unsigned limit(void) const - { - return m_limit; - } - - void set_limit(unsigned limit) - { - m_limit = limit; - } - - unsigned count(void) const - { - return m_errors; - } - - void set_count(unsigned count) - { - m_errors = count; - } - - void error_increment(void) - { - ++m_errors; - } - - bool limit_reached(void) const - { - return m_limit > 0 && m_errors >= m_limit; - } - - private: - message_handler_body(const message_handler_body&); - message_handler_body& operator=(const message_handler_body&); - }; - - //////////////////////////////////////////////////////////////////////////////// - - message_handler::message_handler(std::ostream& device, unsigned limit, bool show) - throw() : - message_handler_base(show), m_body(new message_handler_body(device, limit)) - { - } - - message_handler::message_handler(std::ostream& device, - const std::string& message_file, unsigned limit, bool show) - throw(message_handler_read_error) : - message_handler_base(message_file,show), m_body(new message_handler_body(device, limit)) - { - } - - message_handler::message_handler(std::ostream& device, const std::vector& message_files, unsigned limit, bool show) - throw(message_handler_read_error) : - message_handler_base(message_files,show), m_body(new message_handler_body(device, limit)) - { - } - - message_handler::~message_handler(void) - throw() - { - } - - //////////////////////////////////////////////////////////////////////////////// - // error count - - void message_handler::set_error_limit(unsigned error_limit) - throw() - { - m_body->set_limit(error_limit); - } - - unsigned message_handler::error_limit(void) const - throw() - { - return m_body->limit(); - } - - void message_handler::reset_error_count(void) - throw() - { - m_body->set_count(0); - } - - unsigned message_handler::error_count(void) const - throw() - { - return m_body->count(); - } - - std::ostream& message_handler::device(void) - { - return m_body->device(); - } - - //////////////////////////////////////////////////////////////////////////////// - // information messages - - bool message_handler::information(const std::string& id, - const std::vector& args) - throw(message_handler_id_error,message_handler_format_error) - { - device() << information_message(id, args); - return true; - } - - bool message_handler::information(const std::string& id) - throw(message_handler_id_error,message_handler_format_error) - { - device() << information_message(id); - return true; - } - - bool message_handler::information(const std::string& id, - const std::string& arg1) - throw(message_handler_id_error,message_handler_format_error) - { - device() << information_message(id, arg1); - return true; - } - - bool message_handler::information(const std::string& id, - const std::string& arg1, - const std::string& arg2) - throw(message_handler_id_error,message_handler_format_error) - { - device() << information_message(id, arg1, arg2); - return true; - } - - bool message_handler::information(const std::string& id, - const std::string& arg1, - const std::string& arg2, - const std::string& arg3) - throw(message_handler_id_error,message_handler_format_error) - { - device() << information_message(id, arg1, arg2, arg3); - return true; - } - - bool message_handler::information(const message_position& position, - const std::string& id, - const std::vector& args) - throw(message_handler_id_error,message_handler_format_error) - { - device() << information_message(position, id, args); - return true; - } - - bool message_handler::information(const message_position& position, - const std::string& id) - throw(message_handler_id_error,message_handler_format_error) - { - device() << information_message(position, id); - return true; - } - - bool message_handler::information(const message_position& position, - const std::string& id, - const std::string& arg1) - throw(message_handler_id_error,message_handler_format_error) - { - device() << information_message(position, id, arg1); - return true; - } - - bool message_handler::information(const message_position& position, - const std::string& id, - const std::string& arg1, - const std::string& arg2) - throw(message_handler_id_error,message_handler_format_error) - { - device() << information_message(position, id, arg1, arg2); - return true; - } - - bool message_handler::information(const message_position& position, - const std::string& id, - const std::string& arg1, - const std::string& arg2, - const std::string& arg3) - throw(message_handler_id_error,message_handler_format_error) - { - device() << information_message(position, id, arg1, arg2, arg3); - return true; - } - - //////////////////////////////////////////////////////////////////////////////// - // warning messages - - bool message_handler::warning(const std::string& id, - const std::vector& args) - throw(message_handler_id_error,message_handler_format_error) - { - device() << warning_message(id, args); - return true; - } - - bool message_handler::warning(const std::string& id) - throw(message_handler_id_error,message_handler_format_error) - { - device() << warning_message(id); - return true; - } - - bool message_handler::warning(const std::string& id, - const std::string& arg1) - throw(message_handler_id_error,message_handler_format_error) - { - device() << warning_message(id, arg1); - return true; - } - - bool message_handler::warning(const std::string& id, - const std::string& arg1, - const std::string& arg2) - throw(message_handler_id_error,message_handler_format_error) - { - device() << warning_message(id, arg1, arg2); - return true; - } - - bool message_handler::warning(const std::string& id, - const std::string& arg1, - const std::string& arg2, - const std::string& arg3) - throw(message_handler_id_error,message_handler_format_error) - { - device() << warning_message(id, arg1, arg2, arg3); - return true; - } - - bool message_handler::warning(const message_position& position, - const std::string& id, - const std::vector& args) - throw(message_handler_id_error,message_handler_format_error) - { - device() << warning_message(position, id, args); - return true; - } - - bool message_handler::warning(const message_position& position, - const std::string& id) - throw(message_handler_id_error,message_handler_format_error) - { - device() << warning_message(position, id); - return true; - } - - bool message_handler::warning(const message_position& position, - const std::string& id, - const std::string& arg1) - throw(message_handler_id_error,message_handler_format_error) - { - device() << warning_message(position, id, arg1); - return true; - } - - bool message_handler::warning(const message_position& position, - const std::string& id, - const std::string& arg1, - const std::string& arg2) - throw(message_handler_id_error,message_handler_format_error) - { - device() << warning_message(position, id, arg1, arg2); - return true; - } - - bool message_handler::warning(const message_position& position, - const std::string& id, - const std::string& arg1, - const std::string& arg2, - const std::string& arg3) - throw(message_handler_id_error,message_handler_format_error) - { - device() << warning_message(position, id, arg1, arg2, arg3); - return true; - } - - //////////////////////////////////////////////////////////////////////////////// - // error messages - - bool message_handler::error(const std::string& id, - const std::vector& args) - throw(message_handler_id_error,message_handler_format_error,message_handler_limit_error) - { - device() << error_message(id, args); - m_body->error_increment(); - if (m_body->limit_reached()) throw message_handler_limit_error(m_body->limit()); - return false; - } - - bool message_handler::error(const std::string& id) - throw(message_handler_id_error,message_handler_format_error,message_handler_limit_error) - { - device() << error_message(id); - m_body->error_increment(); - if (m_body->limit_reached()) throw message_handler_limit_error(m_body->limit()); - return false; - } - - bool message_handler::error(const std::string& id, - const std::string& arg1) - throw(message_handler_id_error,message_handler_format_error,message_handler_limit_error) - { - device() << error_message(id, arg1); - m_body->error_increment(); - if (m_body->limit_reached()) throw message_handler_limit_error(m_body->limit()); - return false; - } - - bool message_handler::error(const std::string& id, - const std::string& arg1, - const std::string& arg2) - throw(message_handler_id_error,message_handler_format_error,message_handler_limit_error) - { - device() << error_message(id, arg1, arg2); - m_body->error_increment(); - if (m_body->limit_reached()) throw message_handler_limit_error(m_body->limit()); - return false; - } - - bool message_handler::error(const std::string& id, - const std::string& arg1, - const std::string& arg2, - const std::string& arg3) - throw(message_handler_id_error,message_handler_format_error,message_handler_limit_error) - { - device() << error_message(id, arg1, arg2, arg3); - m_body->error_increment(); - if (m_body->limit_reached()) throw message_handler_limit_error(m_body->limit()); - return false; - } - - bool message_handler::error(const message_position& position, - const std::string& id, - const std::vector& args) - throw(message_handler_id_error,message_handler_format_error,message_handler_limit_error) - { - device() << error_message(position, id, args); - m_body->error_increment(); - if (m_body->limit_reached()) throw message_handler_limit_error(m_body->limit()); - return false; - } - - bool message_handler::error(const message_position& position, - const std::string& id) - throw(message_handler_id_error,message_handler_format_error,message_handler_limit_error) - { - device() << error_message(position, id); - m_body->error_increment(); - if (m_body->limit_reached()) throw message_handler_limit_error(m_body->limit()); - return false; - } - - bool message_handler::error(const message_position& position, - const std::string& id, - const std::string& arg1) - throw(message_handler_id_error,message_handler_format_error,message_handler_limit_error) - { - device() << error_message(position, id, arg1); - m_body->error_increment(); - if (m_body->limit_reached()) throw message_handler_limit_error(m_body->limit()); - return false; - } - - bool message_handler::error(const message_position& position, - const std::string& id, - const std::string& arg1, - const std::string& arg2) - throw(message_handler_id_error,message_handler_format_error,message_handler_limit_error) - { - device() << error_message(position, id, arg1, arg2); - m_body->error_increment(); - if (m_body->limit_reached()) throw message_handler_limit_error(m_body->limit()); - return false; - } - - bool message_handler::error(const message_position& position, - const std::string& id, - const std::string& arg1, - const std::string& arg2, - const std::string& arg3) - throw(message_handler_id_error,message_handler_format_error,message_handler_limit_error) - { - device() << error_message(position, id, arg1, arg2, arg3); - m_body->error_increment(); - if (m_body->limit_reached()) throw message_handler_limit_error(m_body->limit()); - return false; - } - - //////////////////////////////////////////////////////////////////////////////// - // fatal messages - - bool message_handler::fatal(const std::string& id, - const std::vector& args) - throw(message_handler_id_error,message_handler_format_error,message_handler_fatal_error) - { - device() << fatal_message(id, args); - throw message_handler_fatal_error(id); - } - - bool message_handler::fatal(const std::string& id) - throw(message_handler_id_error,message_handler_format_error,message_handler_fatal_error) - { - device() << fatal_message(id); - throw message_handler_fatal_error(id); - } - - bool message_handler::fatal(const std::string& id, - const std::string& arg1) - throw(message_handler_id_error,message_handler_format_error,message_handler_fatal_error) - { - device() << fatal_message(id, arg1); - throw message_handler_fatal_error(id); - } - - bool message_handler::fatal(const std::string& id, - const std::string& arg1, - const std::string& arg2) - throw(message_handler_id_error,message_handler_format_error,message_handler_fatal_error) - { - device() << fatal_message(id, arg1, arg2); - throw message_handler_fatal_error(id); - } - - bool message_handler::fatal(const std::string& id, - const std::string& arg1, - const std::string& arg2, - const std::string& arg3) - throw(message_handler_id_error,message_handler_format_error,message_handler_fatal_error) - { - device() << fatal_message(id, arg1, arg2, arg3); - throw message_handler_fatal_error(id); - } - - bool message_handler::fatal(const message_position& position, - const std::string& id, - const std::vector& args) - throw(message_handler_id_error,message_handler_format_error,message_handler_fatal_error) - { - device() << fatal_message(position, id, args); - throw message_handler_fatal_error(id); - } - - bool message_handler::fatal(const message_position& position, - const std::string& id) - throw(message_handler_id_error,message_handler_format_error,message_handler_fatal_error) - { - device() << fatal_message(position, id); - throw message_handler_fatal_error(id); - } - - bool message_handler::fatal(const message_position& position, - const std::string& id, - const std::string& arg1) - throw(message_handler_id_error,message_handler_format_error,message_handler_fatal_error) - { - device() << fatal_message(position, id, arg1); - throw message_handler_fatal_error(id); - } - - bool message_handler::fatal(const message_position& position, - const std::string& id, - const std::string& arg1, - const std::string& arg2) - throw(message_handler_id_error,message_handler_format_error,message_handler_fatal_error) - { - device() << fatal_message(position, id, arg1, arg2); - throw message_handler_fatal_error(id); - } - - bool message_handler::fatal(const message_position& position, - const std::string& id, - const std::string& arg1, - const std::string& arg2, - const std::string& arg3) - throw(message_handler_id_error,message_handler_format_error,message_handler_fatal_error) - { - device() << fatal_message(position, id, arg1, arg2, arg3); - throw message_handler_fatal_error(id); - } - - /////////////////////////////////////////////////////////////////////////////// - // plain text - - bool message_handler::plaintext(const std::string& text) - { - device() << text << std::endl; - return true; - } - - //////////////////////////////////////////////////////////////////////////////// - -} // end namespace stlplus diff --git a/src/stlplus/subsystems/message_handler.hpp b/src/stlplus/subsystems/message_handler.hpp deleted file mode 100644 index 26b8ed6..0000000 --- a/src/stlplus/subsystems/message_handler.hpp +++ /dev/null @@ -1,1015 +0,0 @@ -#ifndef STLPLUS_MESSAGE_HANDLER -#define STLPLUS_MESSAGE_HANDLER -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// A general-purpose message handler using a message file as the source of all text - -//////////////////////////////////////////////////////////////////////////////// -#include "subsystems_fixes.hpp" -#include "smart_ptr.hpp" -#include -#include -#include -#include - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - // Internals - - class message_handler_base; - class message_handler_base_body; - - class message_handler; - class message_handler_body; - - class message_context_body; - - //////////////////////////////////////////////////////////////////////////////// - // an object representing a file position - // used for example when reporting errors when parsing a text file - - class message_position - { - public: - message_position(void); - message_position(const std::string& filename, unsigned line, unsigned column); - ~message_position(void); - - // access the elements of the position - const std::string& filename(void) const; - // line number in the range 1..n - // so line 0 means uninitialised - unsigned line(void) const; - // column number in the range 0..m-1 - unsigned column(void) const; - - // add a column offset to a position - message_position operator + (unsigned) const; - message_position& operator += (unsigned); - - // tests for valid position - bool empty(void) const; - bool valid(void) const; - - // vector of two strings - // - the first reproducing the source line - // - the second an arrow pointing to the correct column - // the vector will be empty if the position can't be found - std::vector show(void) const; - - private: - std::string m_filename; - unsigned m_line; - unsigned m_column; - }; - - std::string to_string(const message_position& where); - - ////////////////////////////////////////////////////////////////////////////// - // an object representing an message context - // used to control the context stack - // on initialisation, the message_context stores the state of the context stack - // on destruction it restores the state by popping any context that has been pushed since creation - - class message_context - { - public: - message_context(message_handler_base& handler); - - void set(message_handler_base& handler); - void pop(void); - - private: - friend class message_context_body; - friend class message_handler_base; - smart_ptr_nocopy m_body; - }; - - //////////////////////////////////////////////////////////////////////////////// - // exception classes which can be thrown by the message handler - - // read_error is thrown if the message file read fails - class message_handler_read_error : public std::runtime_error - { - public: - message_handler_read_error(const message_position& position, const std::string& reason); - ~message_handler_read_error(void) throw(); - - const message_position& where(void) const; - - private: - message_position m_position; - }; - - // format_error is thrown if a formatting error occurs trying to create the text for the message - - class message_handler_format_error : public std::runtime_error - { - public: - message_handler_format_error(const std::string& format, unsigned offset); - message_handler_format_error(const message_position& pos, const std::string& format, unsigned offset); - ~message_handler_format_error(void) throw(); - - const message_position& where(void) const; - const std::string& format(void) const; - unsigned offset(void) const; - - private: - message_position m_position; - std::string m_format; - unsigned m_offset; - }; - - // id_error is thrown if an error id is requested that could not be found in the message file - - class message_handler_id_error : public std::runtime_error - { - public: - message_handler_id_error(const std::string& id); - ~message_handler_id_error(void) throw(); - - const std::string& id(void) const; - - private: - std::string m_id; - }; - - // limit_error is thrown when the number of errors reaches the error limit - - class message_handler_limit_error : public std::runtime_error - { - public: - message_handler_limit_error(unsigned limit); - ~message_handler_limit_error(void) throw(); - - unsigned limit(void) const; - - private: - unsigned m_limit; // the limit that was exceeded - }; - - // fatal_error is thrown when a fatal error is reported - - class message_handler_fatal_error : public std::runtime_error - { - public: - message_handler_fatal_error(const std::string& id); - ~message_handler_fatal_error(void) throw(); - - const std::string& id(void) const; - - private: - std::string m_id; - }; - - //////////////////////////////////////////////////////////////////////////////// - // base version returns message objects as vectors of strings - // - it is then up to the user to decide what to do with them - // - suitable for use in a GUI for example where the message is displayed in a dialog - - class message_handler_base - { - public: - ////////////////////////////////////////////////////////////////////////////// - // constructor - - // The first form sets the show flag but doesn't load any message files. - // The second and third forms also read message file(s) by calling - // add_message_file and therefore can throw exceptions. The first form - // defers file reading to explicit calls of add_message_file so does not - // throw any exceptions. - - // show determines whether the source file line containing the source of a problem should also be shown - - message_handler_base(bool show = true) - throw(); - - message_handler_base(const std::string& message_file, bool show = true) - throw(message_handler_read_error); - - message_handler_base(const std::vector& message_files, bool show = true) - throw(message_handler_read_error); - - virtual ~message_handler_base(void) - throw(); - - ////////////////////////////////////////////////////////////////////////////// - // message file handling - - // The message file format contains lines of the form: - // - // - // - // In is a unique mnemonic for the message. It starts with an - // alphabetic character and may contain alphanumerics and underscores only. - // The can be one or more space or tab characters. The is the - // remainder of the line and is plain text (not a quoted string). All lines - // starting with a non-alphabetic character are assumed to be comments and are - // ignored - - // If the message file is missing the function throws read_error with no line - // number. If formatting errors were found in the file,then it throws a - // read_error with valid line information. - - // Any number of message files can be added and they accumulate - - void add_message_file(const std::string& message_file) - throw(message_handler_read_error); - - void add_message_files(const std::vector& message_files) - throw(message_handler_read_error); - - void add_message(const std::string& id, const std::string& text) - throw(); - - bool message_present(const std::string& id) const - throw(); - - ////////////////////////////////////////////////////////////////////////////// - // format control - - // The status formats - that is, information/warning/error/fatal/context/supplement - // formats take a single argument which is the formatted message - // For example: "warning: @0" - // - // Messages may be printed as either simple or positional - // simple: just a text message, such as a progress report - // positional: a message relating to a source file line - // The formatted message text is generated directly for simple messages - // However, for positional messages, this text is further substituted - // into a positional format string. - // The positional format string takes up to 4 arguments: - // @0: simple message text - // @1: filename - // @2: line number - // @3: column number - // You can miss out a part of this (e.g. the column number) - // by simply not including the argument number in the format string - // For example: "file: @1, line: @2: @0" - // - // The default formats are: - // information: "@0" - // warning: "warning: @0" - // error: "error: @0" - // fatal: "FATAL: @0" - // context: "context: @0" - // supplement: "supplement: @0" - // - // positional: "\"@1\" (@2,@3) : @0" - - void set_information_format(const std::string& format) - throw(); - - void set_warning_format(const std::string& format) - throw(); - - void set_error_format(const std::string& format) - throw(); - - void set_fatal_format(const std::string& format) - throw(); - - void set_context_format(const std::string& format) - throw(); - - void set_supplement_format(const std::string& format) - throw(); - - void set_position_format(const std::string& format) - throw(); - - ////////////////////////////////////////////////////////////////////////////// - // source file position display control - // show_position indicates that the source file line containing the error - // should be shown with the message on subsequent lines - // hide_position indicates that the source file line should not be shown - - void show_position(void) - throw(); - - void hide_position(void) - throw(); - - ////////////////////////////////////////////////////////////////////////////// - // Message formatting functions - // These functions return a vector of strings containing the completed message - - // There are 6 classes of message: information, context, supplement, warning, error, fatal - // The 4 main classes are: - // - information: progress messages, status messages etc. - // - warning: a problem has been found but there is a sensible way of proceeding - // - error: a problem has been found and the operation will fail - // processing may continue but only to find further errors - // - fatal: an internal (programming) error has been found and the operation is stopping NOW - // The remaining two always follow one of the above - // - context: give stack-like information of the error context - // e.g. if processing include files, the sequence of includes forms a stack - // - supplement: give extra information of the error context - // e.g. give the set of possible solutions to the problem - - // There are 2 kinds of message: simple, positional - // - simple: just a text message - // - positional: a message relating to a source file and a specific position in that file - // This gives 8 variants. - // Note: a positional message with an empty position is treated as a simple message - - // Messages can have arguments. - // All arguments are strings. - // For each variant there are 5 functions relating to different numbers of arguments. - // - general form: takes any number of arguments as a vector of strings - // - 0 arguments: takes no arguments - // - 1 argument: allows a single argument - // - 2 arguments: allows two arguments - // - 3 arguments: allows three arguments - // For more than 3 arguments, use the general form - - // information messages - - // simple messages - std::vector information_message(const std::string& id, - const std::vector& args) - throw(message_handler_id_error,message_handler_format_error); - - std::vector information_message(const std::string& id) - throw(message_handler_id_error,message_handler_format_error); - - std::vector information_message(const std::string& id, - const std::string& arg1) - throw(message_handler_id_error,message_handler_format_error); - - std::vector information_message(const std::string& id, - const std::string& arg1, - const std::string& arg2) - throw(message_handler_id_error,message_handler_format_error); - - std::vector information_message(const std::string& id, - const std::string& arg1, - const std::string& arg2, - const std::string& arg3) - throw(message_handler_id_error,message_handler_format_error); - - // positional messages - std::vector information_message(const message_position&, - const std::string& id, - const std::vector& args) - throw(message_handler_id_error,message_handler_format_error); - - std::vector information_message(const message_position&, - const std::string& id) - throw(message_handler_id_error,message_handler_format_error); - - std::vector information_message(const message_position&, - const std::string& id, - const std::string& arg1) - throw(message_handler_id_error,message_handler_format_error); - - std::vector information_message(const message_position&, - const std::string& id, - const std::string& arg1, - const std::string& arg2) - throw(message_handler_id_error,message_handler_format_error); - - std::vector information_message(const message_position&, - const std::string& id, - const std::string& arg1, - const std::string& arg2, - const std::string& arg3) - throw(message_handler_id_error,message_handler_format_error); - - // warning messages - - // simple messages - std::vector warning_message(const std::string& id, - const std::vector& args) - throw(message_handler_id_error,message_handler_format_error); - - std::vector warning_message(const std::string& id) - throw(message_handler_id_error,message_handler_format_error); - - std::vector warning_message(const std::string& id, - const std::string& arg1) - throw(message_handler_id_error,message_handler_format_error); - - std::vector warning_message(const std::string& id, - const std::string& arg1, - const std::string& arg2) - throw(message_handler_id_error,message_handler_format_error); - - std::vector warning_message(const std::string& id, - const std::string& arg1, - const std::string& arg2, - const std::string& arg3) - throw(message_handler_id_error,message_handler_format_error); - - // positional messages - std::vector warning_message(const message_position&, - const std::string& id, - const std::vector& args) - throw(message_handler_id_error,message_handler_format_error); - - std::vector warning_message(const message_position&, - const std::string& id) - throw(message_handler_id_error,message_handler_format_error); - - std::vector warning_message(const message_position&, - const std::string& id, - const std::string& arg1) - throw(message_handler_id_error,message_handler_format_error); - - std::vector warning_message(const message_position&, - const std::string& id, - const std::string& arg1, - const std::string& arg2) - throw(message_handler_id_error,message_handler_format_error); - - std::vector warning_message(const message_position&, - const std::string& id, - const std::string& arg1, - const std::string& arg2, - const std::string& arg3) - throw(message_handler_id_error,message_handler_format_error); - - // error messages - - // simple messages - std::vector error_message(const std::string& id, - const std::vector& args) - throw(message_handler_id_error,message_handler_format_error); - - std::vector error_message(const std::string& id) - throw(message_handler_id_error,message_handler_format_error); - - std::vector error_message(const std::string& id, - const std::string& arg1) - throw(message_handler_id_error,message_handler_format_error); - - std::vector error_message(const std::string& id, - const std::string& arg1, - const std::string& arg2) - throw(message_handler_id_error,message_handler_format_error); - - std::vector error_message(const std::string& id, - const std::string& arg1, - const std::string& arg2, - const std::string& arg3) - throw(message_handler_id_error,message_handler_format_error); - - // positional messages - std::vector error_message(const message_position&, - const std::string& id, - const std::vector& args) - throw(message_handler_id_error,message_handler_format_error); - - std::vector error_message(const message_position&, - const std::string& id) - throw(message_handler_id_error,message_handler_format_error); - - std::vector error_message(const message_position&, - const std::string& id, - const std::string& arg1) - throw(message_handler_id_error,message_handler_format_error); - - std::vector error_message(const message_position&, - const std::string& id, - const std::string& arg1, - const std::string& arg2) - throw(message_handler_id_error,message_handler_format_error); - - std::vector error_message(const message_position&, - const std::string& id, - const std::string& arg1, - const std::string& arg2, - const std::string& arg3) - throw(message_handler_id_error,message_handler_format_error); - - // fatal messages - // Note that these do not throw the fatal_error exception because that would prevent the message being reported - // the caller should throw the exception after reporting the message - - // simple messages - std::vector fatal_message(const std::string& id, - const std::vector& args) - throw(message_handler_id_error,message_handler_format_error); - - std::vector fatal_message(const std::string& id) - throw(message_handler_id_error,message_handler_format_error); - - std::vector fatal_message(const std::string& id, - const std::string& arg1) - throw(message_handler_id_error,message_handler_format_error); - - std::vector fatal_message(const std::string& id, - const std::string& arg1, - const std::string& arg2) - throw(message_handler_id_error,message_handler_format_error); - - std::vector fatal_message(const std::string& id, - const std::string& arg1, - const std::string& arg2, - const std::string& arg3) - throw(message_handler_id_error,message_handler_format_error); - - // positional messages - std::vector fatal_message(const message_position&, - const std::string& id, - const std::vector& args) - throw(message_handler_id_error,message_handler_format_error); - - std::vector fatal_message(const message_position&, - const std::string& id) - throw(message_handler_id_error,message_handler_format_error); - - std::vector fatal_message(const message_position&, - const std::string& id, - const std::string& arg1) - throw(message_handler_id_error,message_handler_format_error); - - std::vector fatal_message(const message_position&, - const std::string& id, - const std::string& arg1, - const std::string& arg2) - throw(message_handler_id_error,message_handler_format_error); - - std::vector fatal_message(const message_position&, - const std::string& id, - const std::string& arg1, - const std::string& arg2, - const std::string& arg3) - throw(message_handler_id_error,message_handler_format_error); - - // supplement messages - these must be pushed *before* the message that they apply to - - // simple messages - void push_supplement(const std::string& id, - const std::vector& args) - throw(message_handler_id_error,message_handler_format_error); - - void push_supplement(const std::string& id) - throw(message_handler_id_error,message_handler_format_error); - - void push_supplement(const std::string& id, - const std::string& arg1) - throw(message_handler_id_error,message_handler_format_error); - - void push_supplement(const std::string& id, - const std::string& arg1, - const std::string& arg2) - throw(message_handler_id_error,message_handler_format_error); - - void push_supplement(const std::string& id, - const std::string& arg1, - const std::string& arg2, - const std::string& arg3) - throw(message_handler_id_error,message_handler_format_error); - - // positional messages - void push_supplement(const message_position&, - const std::string& id, - const std::vector& args) - throw(message_handler_id_error,message_handler_format_error); - - void push_supplement(const message_position&, - const std::string& id) - throw(message_handler_id_error,message_handler_format_error); - - void push_supplement(const message_position&, - const std::string& id, - const std::string& arg1) - throw(message_handler_id_error,message_handler_format_error); - - void push_supplement(const message_position&, - const std::string& id, - const std::string& arg1, - const std::string& arg2) - throw(message_handler_id_error,message_handler_format_error); - - void push_supplement(const message_position&, - const std::string& id, - const std::string& arg1, - const std::string& arg2, - const std::string& arg3) - throw(message_handler_id_error,message_handler_format_error); - - ////////////////////////////////////////////////////////////////////////////// - // context stack - allows supplementary messages to be printed after each message showing where it came from - // for example, an message whilst inlining a function could be followed by a "function called from..." message - - // simple context messages - void push_context(const std::string& id, - const std::vector& args) - throw(message_handler_id_error,message_handler_format_error); - - void push_context(const std::string& id) - throw(message_handler_id_error,message_handler_format_error); - - void push_context(const std::string& id, - const std::string& arg1) - throw(message_handler_id_error,message_handler_format_error); - - void push_context(const std::string& id, - const std::string& arg1, - const std::string& arg2) - throw(message_handler_id_error,message_handler_format_error); - - void push_context(const std::string& id, - const std::string& arg1, - const std::string& arg2, - const std::string& arg3) - throw(message_handler_id_error,message_handler_format_error); - - // positional context messages - void push_context(const message_position&, - const std::string& id, - const std::vector& args) - throw(message_handler_id_error,message_handler_format_error); - - void push_context(const message_position&, - const std::string& id) - throw(message_handler_id_error,message_handler_format_error); - - void push_context(const message_position&, - const std::string& id, - const std::string& arg1) - throw(message_handler_id_error,message_handler_format_error); - - void push_context(const message_position&, - const std::string& id, - const std::string& arg1, - const std::string& arg2) - throw(message_handler_id_error,message_handler_format_error); - - void push_context(const message_position&, - const std::string& id, - const std::string& arg1, - const std::string& arg2, - const std::string& arg3) - throw(message_handler_id_error,message_handler_format_error); - - unsigned context_depth(void) const - throw(); - - // remove the last level of context if there is one - void pop_context(void) - throw(); - // remove context messages to the specified depth - void pop_context(unsigned) - throw(); - - // push the context and save it in the message_context handle. When the - // message_context handle goes out of scope, the context is popped - // automatically - - // simple context messages - message_context auto_push_context(const std::string& id, - const std::vector& args) - throw(message_handler_id_error,message_handler_format_error); - - message_context auto_push_context(const std::string& id) - throw(message_handler_id_error,message_handler_format_error); - - message_context auto_push_context(const std::string& id, - const std::string& arg1) - throw(message_handler_id_error,message_handler_format_error); - - message_context auto_push_context(const std::string& id, - const std::string& arg1, - const std::string& arg2) - throw(message_handler_id_error,message_handler_format_error); - - message_context auto_push_context(const std::string& id, - const std::string& arg1, - const std::string& arg2, - const std::string& arg3) - throw(message_handler_id_error,message_handler_format_error); - - // positional context messages - message_context auto_push_context(const message_position&, - const std::string& id, - const std::vector& args) - throw(message_handler_id_error,message_handler_format_error); - - message_context auto_push_context(const message_position&, - const std::string& id) - throw(message_handler_id_error,message_handler_format_error); - - message_context auto_push_context(const message_position&, - const std::string& id, - const std::string& arg1) - throw(message_handler_id_error,message_handler_format_error); - - message_context auto_push_context(const message_position&, - const std::string& id, - const std::string& arg1, - const std::string& arg2) - throw(message_handler_id_error,message_handler_format_error); - - message_context auto_push_context(const message_position&, - const std::string& id, - const std::string& arg1, - const std::string& arg2, - const std::string& arg3) - throw(message_handler_id_error,message_handler_format_error); - - private: - friend class message_handler_base_body; - smart_ptr_nocopy m_base_body; - }; - - //////////////////////////////////////////////////////////////////////////////// - // iostream-based derivative uses the above base class to generate messages then uses iostream to print them - // Note: since this is a public derivative, all message_handler_base operations are also available - - class message_handler : public message_handler_base - { - public: - ////////////////////////////////////////////////////////////////////////////// - // constructor - - // The device is the output on which to print the error. For command-line tools - // it will be either std::cout (standard output) or std::cerr (standard error) from - // . - - // The second and third form also reads a message file by calling - // add_message_file and therefore can throw exceptions. The first form - // defers file reading to explicit calls of add_message_file so does not - // throw any exceptions. - - // limit sets the error limit - zero disables this feature - // show determines whether the source file line containing the error should also be shown - - message_handler(std::ostream& device,unsigned limit = 0,bool show = true) - throw(); - - message_handler(std::ostream& device, - const std::string& message_file,unsigned limit = 0,bool show = true) - throw(message_handler_read_error); - - message_handler(std::ostream& device, - const std::vector& message_files,unsigned limit = 0,bool show = true) - throw(message_handler_read_error); - - ~message_handler(void) - throw(); - - ////////////////////////////////////////////////////////////////////////////// - // error count and error limits - - void set_error_limit(unsigned error_limit) - throw(); - - unsigned error_limit(void) const - throw(); - - void reset_error_count(void) - throw(); - - unsigned error_count(void) const - throw(); - - ////////////////////////////////////////////////////////////////////////////// - // access the output device for whatever reason (for example, to ensure that - // text output goes wherever the messages go) - - std::ostream& device(void); - - ////////////////////////////////////////////////////////////////////////////// - // Message reporting functions - // These are based on the error formatting functions in the baseclass - - // information messages - - // simple messages - bool information(const std::string& id, - const std::vector& args) - throw(message_handler_id_error,message_handler_format_error); - - bool information(const std::string& id) - throw(message_handler_id_error,message_handler_format_error); - - bool information(const std::string& id, - const std::string& arg1) - throw(message_handler_id_error,message_handler_format_error); - - bool information(const std::string& id, - const std::string& arg1, - const std::string& arg2) - throw(message_handler_id_error,message_handler_format_error); - - bool information(const std::string& id, - const std::string& arg1, - const std::string& arg2, - const std::string& arg3) - throw(message_handler_id_error,message_handler_format_error); - - // positional messages - bool information(const message_position&, - const std::string& id, - const std::vector& args) - throw(message_handler_id_error,message_handler_format_error); - - bool information(const message_position&, - const std::string& id) - throw(message_handler_id_error,message_handler_format_error); - - bool information(const message_position&, - const std::string& id, - const std::string& arg1) - throw(message_handler_id_error,message_handler_format_error); - - bool information(const message_position&, - const std::string& id, - const std::string& arg1, - const std::string& arg2) - throw(message_handler_id_error,message_handler_format_error); - - bool information(const message_position&, - const std::string& id, - const std::string& arg1, - const std::string& arg2, - const std::string& arg3) - throw(message_handler_id_error,message_handler_format_error); - - // warning messages - - // simple messages - bool warning(const std::string& id, - const std::vector& args) - throw(message_handler_id_error,message_handler_format_error); - - bool warning(const std::string& id) - throw(message_handler_id_error,message_handler_format_error); - - bool warning(const std::string& id, - const std::string& arg1) - throw(message_handler_id_error,message_handler_format_error); - - bool warning(const std::string& id, - const std::string& arg1, - const std::string& arg2) - throw(message_handler_id_error,message_handler_format_error); - - bool warning(const std::string& id, - const std::string& arg1, - const std::string& arg2, - const std::string& arg3) - throw(message_handler_id_error,message_handler_format_error); - - // positional messages - bool warning(const message_position&, - const std::string& id, - const std::vector& args) - throw(message_handler_id_error,message_handler_format_error); - - bool warning(const message_position&, - const std::string& id) - throw(message_handler_id_error,message_handler_format_error); - - bool warning(const message_position&, - const std::string& id, - const std::string& arg1) - throw(message_handler_id_error,message_handler_format_error); - - bool warning(const message_position&, - const std::string& id, - const std::string& arg1, - const std::string& arg2) - throw(message_handler_id_error,message_handler_format_error); - - bool warning(const message_position&, - const std::string& id, - const std::string& arg1, - const std::string& arg2, - const std::string& arg3) - throw(message_handler_id_error,message_handler_format_error); - - // error messages - - // simple messages - bool error(const std::string& id, - const std::vector& args) - throw(message_handler_id_error,message_handler_format_error,message_handler_limit_error); - - bool error(const std::string& id) - throw(message_handler_id_error,message_handler_format_error,message_handler_limit_error); - - bool error(const std::string& id, - const std::string& arg1) - throw(message_handler_id_error,message_handler_format_error,message_handler_limit_error); - - bool error(const std::string& id, - const std::string& arg1, - const std::string& arg2) - throw(message_handler_id_error,message_handler_format_error,message_handler_limit_error); - - bool error(const std::string& id, - const std::string& arg1, - const std::string& arg2, - const std::string& arg3) - throw(message_handler_id_error,message_handler_format_error,message_handler_limit_error); - - // positional messages - bool error(const message_position&, - const std::string& id, - const std::vector& args) - throw(message_handler_id_error,message_handler_format_error,message_handler_limit_error); - - bool error(const message_position&, - const std::string& id) - throw(message_handler_id_error,message_handler_format_error,message_handler_limit_error); - - bool error(const message_position&, - const std::string& id, - const std::string& arg1) - throw(message_handler_id_error,message_handler_format_error,message_handler_limit_error); - - bool error(const message_position&, - const std::string& id, - const std::string& arg1, - const std::string& arg2) - throw(message_handler_id_error,message_handler_format_error,message_handler_limit_error); - - bool error(const message_position&, - const std::string& id, - const std::string& arg1, - const std::string& arg2, - const std::string& arg3) - throw(message_handler_id_error,message_handler_format_error,message_handler_limit_error); - - // fatal messages - // These report the error and then always throw the fatal_error exception - - // simple messages - bool fatal(const std::string& id, - const std::vector& args) - throw(message_handler_id_error,message_handler_format_error,message_handler_fatal_error); - - bool fatal(const std::string& id) - throw(message_handler_id_error,message_handler_format_error,message_handler_fatal_error); - - bool fatal(const std::string& id, - const std::string& arg1) - throw(message_handler_id_error,message_handler_format_error,message_handler_fatal_error); - - bool fatal(const std::string& id, - const std::string& arg1, - const std::string& arg2) - throw(message_handler_id_error,message_handler_format_error,message_handler_fatal_error); - - bool fatal(const std::string& id, - const std::string& arg1, - const std::string& arg2, - const std::string& arg3) - throw(message_handler_id_error,message_handler_format_error,message_handler_fatal_error); - - // positional messages - bool fatal(const message_position&, - const std::string& id, - const std::vector& args) - throw(message_handler_id_error,message_handler_format_error,message_handler_fatal_error); - - bool fatal(const message_position&, - const std::string& id) - throw(message_handler_id_error,message_handler_format_error,message_handler_fatal_error); - - bool fatal(const message_position&, - const std::string& id, - const std::string& arg1) - throw(message_handler_id_error,message_handler_format_error,message_handler_fatal_error); - - bool fatal(const message_position&, - const std::string& id, - const std::string& arg1, - const std::string& arg2) - throw(message_handler_id_error,message_handler_format_error,message_handler_fatal_error); - - bool fatal(const message_position&, - const std::string& id, - const std::string& arg1, - const std::string& arg2, - const std::string& arg3) - throw(message_handler_id_error,message_handler_format_error,message_handler_fatal_error); - - ////////////////////////////////////////////////////////////////////////////// - // plain text output - // provides a simple way of outputting text from the program to the same device as the messages - // Each call of plaintext is treated as a line of text and has a newline appended - - bool plaintext (const std::string& text); - - private: - friend class message_handler_body; - smart_ptr_nocopy m_body; - }; - - //////////////////////////////////////////////////////////////////////////////// - -} // end namespace stlplus - -#endif diff --git a/src/stlplus/subsystems/subsystems.hpp b/src/stlplus/subsystems/subsystems.hpp deleted file mode 100644 index 2bc41b5..0000000 --- a/src/stlplus/subsystems/subsystems.hpp +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef STLPLUS_SUBSYSTEMS -#define STLPLUS_SUBSYSTEMS -//////////////////////////////////////////////////////////////////////////////// - -// 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 subsystems to be included in one go - -//////////////////////////////////////////////////////////////////////////////// - -#include "cli_parser.hpp" -#include "ini_manager.hpp" -#include "library_manager.hpp" -#include "message_handler.hpp" -#include "timer.hpp" - -//////////////////////////////////////////////////////////////////////////////// -#endif diff --git a/src/stlplus/subsystems/subsystems_fixes.hpp b/src/stlplus/subsystems/subsystems_fixes.hpp deleted file mode 100644 index 8fd9f91..0000000 --- a/src/stlplus/subsystems/subsystems_fixes.hpp +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef STLPLUS_SUBSYSTEMS_FIXES -#define STLPLUS_SUBSYSTEMS_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 - -//////////////////////////////////////////////////////////////////////////////// - -//////////////////////////////////////////////////////////////////////////////// -// 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) -// 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 - -//////////////////////////////////////////////////////////////////////////////// -#endif diff --git a/src/stlplus/subsystems/timer.cpp b/src/stlplus/subsystems/timer.cpp deleted file mode 100644 index 9faac0f..0000000 --- a/src/stlplus/subsystems/timer.cpp +++ /dev/null @@ -1,59 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -//////////////////////////////////////////////////////////////////////////////// -#include "timer.hpp" -#include "dprintf.hpp" -#include "time.hpp" - -//////////////////////////////////////////////////////////////////////////////// - -namespace stlplus -{ - -//////////////////////////////////////////////////////////////////////////////// - - timer::timer(void) - { - reset(); - } - - timer::~timer(void) - { - } - - void timer::reset(void) - { - m_clock = clock(); - m_time = time(0); - } - - float timer::cpu(void) const - { - return ((float)(clock() - m_clock)) / ((float)CLOCKS_PER_SEC); - } - - float timer::elapsed(void) const - { - return ((float)(time(0) - m_time)); - } - - std::string timer::text(void) const - { - return dformat("%4.2fs CPU, %s elapsed", cpu(), delaytime_string(time(0)-m_time).c_str()); - } - -//////////////////////////////////////////////////////////////////////////////// - - std::ostream& operator << (std::ostream& str, const timer& t) - { - return str << t.text(); - } - -//////////////////////////////////////////////////////////////////////////////// - -} // end namespace stlplus diff --git a/src/stlplus/subsystems/timer.hpp b/src/stlplus/subsystems/timer.hpp deleted file mode 100644 index a3ad38d..0000000 --- a/src/stlplus/subsystems/timer.hpp +++ /dev/null @@ -1,56 +0,0 @@ -#ifndef STLPLUS_TIMER -#define STLPLUS_TIMER -//////////////////////////////////////////////////////////////////////////////// - -// Author: Andy Rushton -// Copyright: (c) Southampton University 1999-2004 -// (c) Andy Rushton 2004-2009 -// License: BSD License, see ../docs/license.html - -// A CPU timer encapsulated as a class. Measures the CPU time used since its -// construction and allows this cumulative time to be reported at any time. - -//////////////////////////////////////////////////////////////////////////////// -#include "subsystems_fixes.hpp" -#include -#include -#include - -namespace stlplus -{ - - //////////////////////////////////////////////////////////////////////////////// - - class timer - { - private: - clock_t m_clock; - time_t m_time; - - public: - // constructor resets the timer to zero - timer(void); - ~timer(void); - - // reset the timer to zero without destroying it - void reset(void); - - // get the elapsed time in seconds, expressed as a float - float elapsed(void) const; - // get the CPU time in seconds, expressed as a float - float cpu(void) const; - - // get a printable string representing the elapsed time and CPU time - std::string text(void) const; - }; - - //////////////////////////////////////////////////////////////////////////////// - - // print the elapsed time and CPU time using the same representation as the text method - std::ostream& operator << (std::ostream&, const timer&); - - //////////////////////////////////////////////////////////////////////////////// - -} // end namespace stlplus - -#endif