/////////////////////////////////////////////////////////////////////////////////////// /// \file gutil.h /// \brief GUTIL LIBRARY (Fully portable version) /// /// This library is provided as a component of the ecosystem modelling platform /// LPJ-GUESS. It combines the functionality of the XTRING and BENUTIL libraries /// These components are documented separately by commenting in this header file /// and in the source code file gutil.cpp. /// /// FULL PORTABILITY VERSION: tested and should work in any Unix, Linux or Windows /// environment. /// /// Enquiries to: Joe Siltberg, Lund University: joe.siltberg@nateko.lu.se /// All rights reserved, copyright retained by the author. /// /// \author Ben Smith, University of Lund /// $Date: 2014-09-09 10:49:13 +0200 (mar, 09 sep 2014) $ /// /////////////////////////////////////////////////////////////////////////////////////// #ifndef GUTIL_H #define GUTIL_H #include #include #include #include #include void fail(); /// XTRING class - pointer-free manipulation of character strings in C++. /** Declare new xstring objects using one of the following forms: * * \code * xtring s; // equivalent to xtring s="" * xtring s="initial text"; * xtring s='c'; * xtring s(INITSIZE); // reserves space for at least INITSIZE+1 characters * \endcode * * You can also cast a string literal to an xtring the usual way: * * \code * (xtring)"Cast to a xtring" * \endcode * * In general, xtring objects can be used in place of standard C char* strings * without explicit casting, e.g. * * \code * char copy[100]; * xtring original="text"; * strcpy(copy,original); * \endcode * * However, xtring objects must be explicitly casted to char* when specified as * arguments in calls to functions with an ellipsis argument, e.g. * * \code * xtring name="Ben" * printf("My name is: %s",(char*)name); * \endcode * * Casting to char* is useful also if you (unwisely?) choose to write directly to * the internal string buffer of the xtring object: * * \code * xtring name(100); * char* pbuffer=(char*)name; * strcpy(pbuffer,"Ben"); * \endcode * * Note that some of the member functions of xtring can cause the size and memory * position of the internal buffer to change. * * \section ops Operators * * Operator functionality is described mainly by code examples. The examples below * assume the following data types for variables: * * \code * xtring x1,x2,x3; * char* s; * char c; * unsigned long n; * \endcode * * Assignment operator: * * \code * x1=x2; x1=s; x1=c; * \endcode * * Concatenation (appends a char* string, xtring or character to the end of an * xtring string): * * \code * x1=x2+x3; x1=x2+s; x1=x2+c; * x1+=x2; x1+=s; x1+=c; * \endcode * * Concatenation of, for example, two char* strings, or a xtring to the end of * a char* string, is possible by casting one of the operands to an xtring: * * \code * x1=(xtring)"first"+"second"; * x1=(xtring)"before"+x2; * \endcode * * Reference to character as array element: * * \code * c=x1[n]; x1[n]=c; * \endcode * * The size of the internal string buffer is expanded if necessary to ensure * that the specified index is valid (points to a character position within the * internal string buffer). However, the string itself is not expanded (i.e. * the position of the trailing null byte, signifying the end of the string, is * not changed. * * Comparison operators: * * \code * x1==x2 && x2!=x3 || x1c || x2<=x3 || x3>=x1 * \endcode * * Note that the left hand operand must be a xtring (or casted to xtring) * * \author Ben Smith, University of Lund */ class xtring { // MEMBER VARIABLES private: char *ptext; unsigned long nxegment; char *buf; // MEMBER FUNCTIONS private: void init(); void resize(unsigned long nchar); void expand(unsigned long nchar); public: xtring(const xtring& s); xtring(); xtring(char* text); xtring(const char* inittext); xtring(char c); xtring(unsigned long n); xtring(int n); xtring(long n); xtring(unsigned int n); ~xtring(); operator char*(); operator const char*() const; /// Adjusts memory allocation /** Expands or contracts the memory allocation to the current xtring object * to accomodate a string at least n characters in length (not including * the trailing null byte). The currently stored string may be copied to a * new location in memory but is not deleted. This function may be useful * if you intend to write directly to the internal string buffer, whose * address is returned by casting the xtring object to char*. */ void reserve(unsigned long n); /// Length of string /** Returns length of current string in characters * (not including trailing null character). */ unsigned long len(); /// Converts to upper case /** Returns a new xtring equivalent to the current one, but with * lower-case alphabetics 'a'-'z' converted to upper case. */ xtring upper(); /// Converts to lower case /** Returns a new xtring equivalent to the current one, but with * upper-case alphabetics 'A'-'Z' converted to lower case. */ xtring lower(); /// Returns only printable characters /** Returns a new xtring equivalent to the current one, but with * non-printable characters (ASCII code 0-31) removed. */ xtring printable(); /// Returns a prefix of the string /** Returns a new xtring consisting of the leftmost n characters of * the current xtring. A null xtring ("") is returned if n<=0; if n>length * of the current xtring, an identical copy of the current xtring is * returned. */ xtring left(unsigned long n); /// Returns a substring /** Returns a new xtring consisting of the rightmost portion of the current * xtring, starting at character number s (on 0 base). If s<0 an identical * copy of the current xtring is returned; if s>=length of the current * xtring, a null xtring is returned. */ xtring mid(unsigned long s); /// Returns a substring /** Returns a new xtring consisting of up to n characters, starting at * character number s of the current xtring. If s<0, the new string starts * at character 0 of the current xtring; if s>=length of the current * xtring, a null xtring is returned. */ xtring mid(unsigned long s,unsigned long n); /// Returns a postfix of the string /** Returns a new xtring consisting of the rightmost n characters of the * current xtring. A null string is returned if n<=0; if n>length of the * current xtring, an identical copy is returned. */ xtring right(unsigned long n); /// Search for a substring /** Returns the position of the (first character of the) specified character * string if it occurs as a substring of the current xtring. If there are * several occurences, the position of the leftmost occurence is returned. * Returns -1 if string not found. */ long find(const char* s); /// Search for a character /** Returns the position of the (first character of the) specified character * if it occurs in the current xtring. If there are several occurences, the * position of the leftmost occurence is returned. * Returns -1 if character not found. */ long find(char c); /// Search for one of several characters /** Returns the position in this xtring of the first (leftmost) occurrence * any character forming part of the string pointed to by s. Returns -1 if * there are no occurrences. */ long findoneof(const char* s); /// Search for character which isn't one of a given sequence of characters /** Returns the position in this xtring of the first (leftmost) character * NOT forming part of the string pointed to by s. Returns -1 if no such * character is found. */ long findnotoneof(const char* s); /// Convert to a number /** Returns the numerical value of the current xtring, if it is a valid * representation of a double precision floating point number in C++. Call * function isnum() to test whether the returned value is meaningful. */ double num(); /// Checks if the string represents a number /** Returns 1 if the current xtring is a valid representation of a double * precision floating point number in C++, 0 otherwise. */ char isnum(); /// String formatting /** A printf-style function for writing formatted data to this xtring object. * Equivalent to sprintf in the standard C library (stdio.h). */ void printf(const char* fmt,...); xtring& operator=(xtring& s); xtring& operator=(const char* s); xtring& operator=(char c); xtring operator+(xtring& s2); xtring operator+(const char* s2); xtring operator+(char c); xtring& operator+=(xtring& s2); xtring& operator+=(const char* s2); xtring& operator+=(char c); char& operator[](unsigned long n); char& operator[](int n); char& operator[](unsigned int n); char& operator[](long n); bool operator==(xtring& s2) const; bool operator==(char* s2) const; bool operator==(char c) const; bool operator==(const char* s2) const; bool operator!=(xtring& s2) const; bool operator!=(char* s2) const; bool operator!=(char c) const; bool operator!=(const char* s2) const; bool operator<(xtring& s2) const; bool operator<(char* s2) const; bool operator<(char c) const; bool operator<(const char* s2) const; bool operator>(xtring& s2) const; bool operator>(char* s2) const; bool operator>(char c) const; bool operator>(const char* s2) const; bool operator<=(xtring& s2) const; bool operator<=(char* s2) const; bool operator<=(char c) const; bool operator<=(const char* s2) const; bool operator>=(xtring& s2) const; bool operator>=(char* s2) const; bool operator>=(char c) const; bool operator>=(const char* s2) const; }; /////////////////////////////////////////////////////////////////////////////////////// // COLLECTION CLASS TEMPLATES FROM THE BENUTIL LIBRARY const int SEGSIZE=16; /// List Array of objects /** Use this template to produce collection classes of any objects with a valid default * constructor. * * Declare a type using this template as either: * * \code * typedef ListArray MyCollectionClass; * \endcode * * or (for collection classes containing additional members) * * \code * class myclass : public ListArray * { ... }; * \endcode * * Either of the above declarations will produce a class including the public functions * and member variables included in this class. * * This functionality implies that it is possible to loop sequentially through all * items in the list array using code like the following: * * \code * mycollection.firstobj(); * while (mycollection.isobj) { * MyObjectType& obj=mycollection.getobj(); // NB: '&' required unless query only * // query or modify object obj here * mycollection.nextobj(); * } * \endcode * * or alternatively: * * \code * for (i=0;i class ListArray { class Item { public: Item* pnext; Item* pprev; tdata object; }; private: Item** array; Item* pfirstitem; Item* plastitem; Item* pthisitem; unsigned int nseg; unsigned int thisobj; public: /// Whether the internal object pointer points to an object /** This variable (NB: not a function) is true whenever the internal object * pointer points to a MyObjectType object, false otherwise (including * when the list array is empty). */ bool isobj; /// The number of objects currently stored in the list array. unsigned int nobj; public: ListArray() { pfirstitem=NULL; plastitem=NULL; pthisitem=NULL; isobj=false; nobj=0; nseg=0; } /// Clears the entire list array, releasing dynamic memory. void killall() { Item* pitem=pfirstitem; Item* pnext; while (pitem) { pnext=pitem->pnext; delete pitem; pitem=pnext; } if (nseg) delete[] array; pfirstitem=NULL; plastitem=NULL; pthisitem=NULL; isobj=false; nobj=0; nseg=0; } ~ListArray() { killall(); } /// Clears list array (if not empty) /** Clears the array (if not empty) and fills it with nitem MyObjectType * objects. */ void initarray(unsigned int nitem) { unsigned int i; killall(); if (nitem<=0) return; nseg=nitem%SEGSIZE; if (nseg*SEGSIZEpprev=plastitem; pitem->pnext=NULL; if (plastitem) plastitem->pnext=pitem; else pfirstitem=pitem; plastitem=pitem; pthisitem=pitem; isobj=true; thisobj=nobj++; if (nobj>nseg*SEGSIZE) { Item** newarray=new Item*[(nseg+1)*SEGSIZE]; if (!newarray) fail(); if (nseg++) { unsigned int i; for (i=0;iobject; } /// Go to the first object /** Causes the internal object pointer (pthisitem) to point to the first * MyObjectType object in the list array. Returns false if the list array * is empty. */ bool firstobj() { if (pfirstitem) { pthisitem=pfirstitem; isobj=true; thisobj=0; return true; } return false; } /// Go to the next object /** Causes the internal object pointer to point to the next MyObjectType * object in the list array. Returns false if the last item has already * been reached. */ bool nextobj() { if (pthisitem) { pthisitem=pthisitem->pnext; thisobj++; } if (pthisitem) return true; isobj=false; return false; } /// Returns current object /** Returns a reference to the object currently pointed to by the internal * object pointer. Do not call this function unless the pointer is * pointing to a valid object (isobj=true). */ tdata& getobj() { return pthisitem->object; } /// Returns i'th object /** Overload of the array brackets operator which returns a reference to * the i'th MyObjectType item in the list array. Do not call unless the * internal object pointer is pointing to a valid object. Note that this * function does NOT affect the value of the internal pointer. */ tdata& operator[](unsigned int i) { return array[i]->object; } /// Removes the object currently pointed to by the internal pointer. void killobj() { unsigned int i; if (!pthisitem) return; if (pthisitem==pfirstitem) pfirstitem=pthisitem->pnext; if (pthisitem==plastitem) plastitem=pthisitem->pprev; Item* pnextitem=pthisitem->pnext; if (pthisitem->pprev) pthisitem->pprev->pnext=pthisitem->pnext; if (pthisitem->pnext) pthisitem->pnext->pprev=pthisitem->pprev; delete pthisitem; pthisitem=pnextitem; if (!pthisitem) isobj=false; for (i=thisobj+1;i MyCollectionClass; * \endcode * * or (for collection classes containing additional members) * * \code * class myclass : public ListArray_id * { ... }; * \endcode * * Either of the above declarations will produce a class including the public functions * and member variables included in this class. * * This functionality implies that it is possible to loop sequentially through all * items in the list array using code like the following: * * \code * mycollection.firstobj(); * while (mycollection.isobj) { * MyObjectType& obj=mycollection.getobj(); // NB: '&' required unless query only * // query or modify object obj here * mycollection.nextobj(); * } * \endcode * * or alternatively: * * \code * for (i=0;i class ListArray_id { class Item { public: Item* pnext; Item* pprev; tdata object; }; private: Item** array; Item* pfirstitem; Item* plastitem; Item* pthisitem; unsigned int nseg; unsigned int id; unsigned int thisobj; public: /// Whether the internal object pointer points to an object /** This variable (NB: not a function) is true whenever the internal * object pointer points to a MyObjectType object, false otherwise * (including when the list array is empty). */ bool isobj; /// The number of objects currently stored in the list array. unsigned int nobj; public: ListArray_id() { pfirstitem=NULL; plastitem=NULL; pthisitem=NULL; isobj=false; nobj=0; nseg=0; id=0; } /// Clears the entire list array, releasing dynamic memory. /** Resets the id counter to 0. */ void killall() { Item* pitem=pfirstitem; Item* pnext; while (pitem) { pnext=pitem->pnext; delete pitem; pitem=pnext; } if (nseg) delete[] array; pfirstitem=NULL; plastitem=NULL; pthisitem=NULL; isobj=false; nobj=0; nseg=0; id=0; } ~ListArray_id() { killall(); } /// Clears list array (if not empty) /** Clears list array (if not empty), resets id counter to 0 and fills list * array with nitem MyObjectType objects. */ void initarray(unsigned int nitem) { unsigned int i; killall(); if (nitem<=0) return; nseg=nitem%SEGSIZE; if (nseg*SEGSIZEobject.id=id++; pitem->pprev=plastitem; pitem->pnext=NULL; if (plastitem) plastitem->pnext=pitem; else pfirstitem=pitem; plastitem=pitem; pthisitem=pitem; isobj=true; thisobj=nobj++; if (nobj>nseg*SEGSIZE) { Item** newarray=new Item*[(nseg+1)*SEGSIZE]; if (!newarray) fail(); if (nseg++) { unsigned int i; for (i=0;iobject; } /// Go to the first object /** Causes the internal object pointer (pthisitem) to point to the first * MyObjectType object in the list array. Returns false if the list array * is empty. */ bool firstobj() { if (pfirstitem) { pthisitem=pfirstitem; isobj=true; thisobj=0; return true; } return false; } /// Go to the next object /** Causes the internal object pointer to point to the next MyObjectType * object in the list array. Returns false if the last item has already * been reached. */ bool nextobj() { if (pthisitem) { pthisitem=pthisitem->pnext; thisobj++; } if (pthisitem) return true; isobj=false; return false; } /// Returns current object /** Returns a reference to the object currently pointed to by the internal * object pointer. Do not call this function unless the pointer is * pointing to a valid object (isobj=true). */ tdata& getobj() { return pthisitem->object; } /// Returns i'th object /** Overload of the array brackets operator which returns a reference to * the i'th MyObjectType item in the list array. Do not call unless the * internal object pointer is pointing to a valid object. Note that this * function does NOT affect the value of the internal pointer. */ tdata& operator[](unsigned int i) { return array[i]->object; } /// Removes the object currently pointed to by the internal pointer. void killobj() { unsigned int i; if (!pthisitem) return; if (pthisitem==pfirstitem) pfirstitem=pthisitem->pnext; if (pthisitem==plastitem) plastitem=pthisitem->pprev; Item* pnextitem=pthisitem->pnext; if (pthisitem->pprev) pthisitem->pprev->pnext=pthisitem->pnext; if (pthisitem->pnext) pthisitem->pnext->pprev=pthisitem->pprev; delete pthisitem; pthisitem=pnextitem; if (!pthisitem) isobj=false; for (i=thisobj+1;i MyCollectionClass; * \endcode * * or (for collection classes containing additional members) * * \code * class myclass : ListArray_idin1 * { ... }; * \endcode * * Either of the above declarations will produce a class including the public functions * and member variables included in this class. * * This functionality implies that it is possible to loop sequentially through all * items in the list array using code like the following: * * \code * mycollection.firstobj(); * while (mycollection.isobj) { * MyObjectType& obj=mycollection.getobj(); // NB: '&' required unless query only * // query or modify object obj here * mycollection.nextobj(); * } * \endcode * * or alternatively: * * \code * for (i=0;i class ListArray_idin1 { class Item { public: Item* pnext; Item* pprev; tdata object; Item(unsigned int id,tref& ref) : object(id,ref) {} }; private: Item** array; Item* pfirstitem; Item* plastitem; Item* pthisitem; unsigned int nseg; unsigned int id; unsigned int thisobj; public: /// Whether the internal object pointer points to an object /** This variable (NB: not a function) is true whenever the internal object * pointer points to a MyObjectType object, false otherwise (including * when the list array is empty). */ bool isobj; /// The number of objects currently stored in the list array. unsigned int nobj; public: ListArray_idin1() { pfirstitem=NULL; plastitem=NULL; pthisitem=NULL; isobj=false; nobj=0; nseg=0; id=0; } /// Clears the entire list array, releases dynamic memory. /** Resets the id counter to 0. */ void killall() { Item* pitem=pfirstitem; Item* pnext; while (pitem) { pnext=pitem->pnext; delete pitem; pitem=pnext; } if (nseg) delete[] array; pfirstitem=NULL; plastitem=NULL; pthisitem=NULL; isobj=false; nobj=0; nseg=0; id=0; } ~ListArray_idin1() { killall(); } /// Creates a new object of type MyObjectType and returns a reference to it. /** The constructor function MyObjectType(unsigned int,MyRefType&) is * called, with the unsigned int argument set to the value of a counter * which has initial value 0 and is incremented (by 1) on each subsequent * call to createobj; ref is sent as the MyRefType& argument to the * constructor function. */ tdata& createobj(tref& ref) { Item* pitem=new Item(id++,ref); if (!pitem) fail(); pitem->pprev=plastitem; pitem->pnext=NULL; if (plastitem) plastitem->pnext=pitem; else pfirstitem=pitem; plastitem=pitem; pthisitem=pitem; isobj=true; thisobj=nobj++; if (nobj>nseg*SEGSIZE) { Item** newarray=new Item*[(nseg+1)*SEGSIZE]; if (!newarray) fail(); if (nseg++) { unsigned int i; for (i=0;iobject; } /// Go to the first object /** Causes the internal object pointer (pthisitem) to point to the first * MyObjectType object in the list array. Returns false if the list array * is empty. */ bool firstobj() { if (pfirstitem) { pthisitem=pfirstitem; isobj=true; thisobj=0; return true; } return false; } /// Go to the next object /** Causes the internal object pointer to point to the next MyObjectType * object in the list array. Returns false if the last item has already * been reached. */ bool nextobj() { if (pthisitem) { pthisitem=pthisitem->pnext; thisobj++; } if (pthisitem) return true; isobj=false; return false; } /// Returns current object /** Returns a reference to the object currently pointed to by the internal * object pointer. Do not call this function unless the pointer is * pointing to a valid object (isobj=true). */ tdata& getobj() { return pthisitem->object; } /// Returns i'th object /** Overload of the array brackets operator which returns a reference to * the i'th MyObjectType item in the list array. Do not call unless the * internal object pointer is pointing to a valid object. Note that this * function does NOT affect the value of the internal pointer. */ tdata& operator[](unsigned int i) { return array[i]->object; } /// Removes the object currently pointed to by the internal pointer. void killobj() { unsigned int i; if (!pthisitem) return; if (pthisitem==pfirstitem) pfirstitem=pthisitem->pnext; if (pthisitem==plastitem) plastitem=pthisitem->pprev; Item* pnextitem=pthisitem->pnext; if (pthisitem->pprev) pthisitem->pprev->pnext=pthisitem->pnext; if (pthisitem->pnext) pthisitem->pnext->pprev=pthisitem->pprev; delete pthisitem; pthisitem=pnextitem; if (!pthisitem) isobj=false; for (i=thisobj+1;i * MyCollectionClass; * \endcode * * or (for collection classes containing additional members) * * \code * class myclass : * public ListArray_idin2 * { ... }; * \endcode * * Either of the above declarations will produce a class including the public functions * and member variables included in this class. * * This functionality implies that it is possible to loop sequentially through all * items in the list array using code like the following: * * \code * mycollection.firstobj(); * while (mycollection.isobj) { * MyObjectType& obj=mycollection.getobj(); // NB: '&' required unless query only * // query or modify object obj here * mycollection.nextobj(); * } * \endcode * * or alternatively: * * \code * for (i=0;i class ListArray_idin2 { class Item { public: Item* pnext; Item* pprev; tdata object; Item(unsigned int id,tref1& ref1,tref2& ref2) : object(id,ref1,ref2) {} }; private: Item** array; Item* pfirstitem; Item* plastitem; Item* pthisitem; unsigned int nseg; unsigned int id; unsigned int thisobj; public: /// Whether the internal object pointer points to an object /** This variable (NB: not a function) is true whenever the internal object * pointer points to a MyObjectType object, false otherwise (including * when the list array is empty). */ bool isobj; /// The number of objects currently stored in the list array. unsigned int nobj; public: ListArray_idin2() { pfirstitem=NULL; plastitem=NULL; pthisitem=NULL; isobj=false; nobj=0; nseg=0; id=0; } /// Clears the entire list array, releases dynamic memory. /** Resets the id counter to 0. */ void killall() { Item* pitem=pfirstitem; Item* pnext; while (pitem) { pnext=pitem->pnext; delete pitem; pitem=pnext; } if (nseg) delete[] array; pfirstitem=NULL; plastitem=NULL; pthisitem=NULL; isobj=false; nobj=0; nseg=0; id=0; } ~ListArray_idin2() { killall(); } /// Creates a new object of type MyObjectType and returns a reference to it. /** The constructor function MyObjectType(unsigned int,MyRefType1&,MyRefType2&) * is called, with the unsigned int argument set to the value of a counter * which has initial value 0 and is incremented (by 1) on each subsequent * call to createobj; ref1 and ref2 are sent as the MyRefType& arguments * to the constructor function. */ tdata& createobj(tref1& ref1,tref2& ref2) { Item* pitem=new Item(id++,ref1,ref2); if (!pitem) fail(); pitem->pprev=plastitem; pitem->pnext=NULL; if (plastitem) plastitem->pnext=pitem; else pfirstitem=pitem; plastitem=pitem; pthisitem=pitem; isobj=true; thisobj=nobj++; if (nobj>nseg*SEGSIZE) { Item** newarray=new Item*[(nseg+1)*SEGSIZE]; if (!newarray) fail(); if (nseg++) { unsigned int i; for (i=0;iobject; } /// Returns the first object /** Causes the internal object pointer (pthisitem) to point to the first * MyObjectType object in the list array. Returns false if the list array * is empty. */ bool firstobj() { if (pfirstitem) { pthisitem=pfirstitem; isobj=true; thisobj=0; return true; } return false; } /// Returns the next object /** Causes the internal object pointer to point to the next MyObjectType * object in the list array. Returns false if the last item has already * been reached. */ bool nextobj() { if (pthisitem) { pthisitem=pthisitem->pnext; thisobj++; } if (pthisitem) return true; isobj=false; return false; } /// Returns current object /** Returns a reference to the object currently pointed to by the internal * object pointer. Do not call this function unless the pointer is * pointing to a valid object (isobj=true). */ tdata& getobj() { return pthisitem->object; } /// Returns i'th object /** Overload of the array brackets operator which returns a reference to * the i'th MyObjectType item in the list array. Do not call unless the * internal object pointer is pointing to a valid object. Note that this * function does NOT affect the value of the internal pointer. */ tdata& operator[](unsigned int i) { return array[i]->object; } /// Removes the object currently pointed to by the internal pointer. void killobj() { unsigned int i; if (!pthisitem) return; if (pthisitem==pfirstitem) pfirstitem=pthisitem->pnext; if (pthisitem==plastitem) plastitem=pthisitem->pprev; Item* pnextitem=pthisitem->pnext; if (pthisitem->pprev) pthisitem->pprev->pnext=pthisitem->pnext; if (pthisitem->pnext) pthisitem->pnext->pprev=pthisitem->pprev; delete pthisitem; pthisitem=pnextitem; if (!pthisitem) isobj=false; for (i=thisobj+1;i * MyCollectionClass; * \endcode * * or (for collection classes containing additional members) * * \code * class myclass : * public ListArray_idin3 * { ... }; * \endcode * * Either of the above declarations will produce a class including the public functions * and member variables included in this class. * * This functionality implies that it is possible to loop sequentially through all * items in the list array using code like the following: * * \code * mycollection.firstobj(); * while (mycollection.isobj) { * MyObjectType& obj=mycollection.getobj(); // NB: '&' required unless query only * // query or modify object obj here * mycollection.nextobj(); * } * \endcode * * or alternatively: * * \code * for (i=0;i class ListArray_idin3 { class Item { public: Item* pnext; Item* pprev; tdata object; Item(unsigned int id,tref1& ref1,tref2& ref2,tref3& ref3) : object(id,ref1,ref2,ref3) {} }; private: Item** array; Item* pfirstitem; Item* plastitem; Item* pthisitem; unsigned int nseg; unsigned int id; unsigned int thisobj; public: /// Whether the internal object pointer points to an object /** This variable (NB: not a function) is true whenever the internal object * pointer points to a MyObjectType object, false otherwise (including * when the list array is empty). */ bool isobj; /// The number of objects currently stored in the list array. unsigned int nobj; public: ListArray_idin3() { pfirstitem=NULL; plastitem=NULL; pthisitem=NULL; isobj=false; nobj=0; nseg=0; id=0; } /// Clears the entire list array, releases dynamic memory. /** Resets the id counter to 0. */ void killall() { Item* pitem=pfirstitem; Item* pnext; while (pitem) { pnext=pitem->pnext; delete pitem; pitem=pnext; } if (nseg) delete[] array; pfirstitem=NULL; plastitem=NULL; pthisitem=NULL; isobj=false; nobj=0; nseg=0; id=0; } ~ListArray_idin3() { killall(); } /// Creates a new object of type MyObjectType and returns a reference to it. /** The constructor function MyObjectType(unsigned int,MyRefType1&,MyRefType2&, MyRefType3&) * is called, with the unsigned int argument set to the value of a counter * which has initial value 0 and is incremented (by 1) on each subsequent * call to createobj; ref1, ref2 and ref3 are sent as the MyRefType& * arguments to the constructor function. */ tdata& createobj(tref1& ref1,tref2& ref2,tref3& ref3) { Item* pitem=new Item(id++,ref1,ref2,ref3); if (!pitem) fail(); pitem->pprev=plastitem; pitem->pnext=NULL; if (plastitem) plastitem->pnext=pitem; else pfirstitem=pitem; plastitem=pitem; pthisitem=pitem; isobj=true; thisobj=nobj++; if (nobj>nseg*SEGSIZE) { Item** newarray=new Item*[(nseg+1)*SEGSIZE]; if (!newarray) fail(); if (nseg++) { unsigned int i; for (i=0;iobject; } /// Go to the first object /** Causes the internal object pointer (pthisitem) to point to the first * MyObjectType object in the list array. Returns false if the list array * is empty. */ bool firstobj() { if (pfirstitem) { pthisitem=pfirstitem; isobj=true; thisobj=0; return true; } return false; } /// Go to the next object /** Causes the internal object pointer to point to the next MyObjectType * object in the list array. Returns false if the last item has already * been reached. */ bool nextobj() { if (pthisitem) { pthisitem=pthisitem->pnext; thisobj++; } if (pthisitem) return true; isobj=false; return false; } /// Returns current object /** Returns a reference to the object currently pointed to by the internal * object pointer. Do not call this function unless the pointer is * pointing to a valid object (isobj=true). */ tdata& getobj() { return pthisitem->object; } /// Returns i'th object /** Overload of the array brackets operator which returns a reference to * the i'th MyObjectType item in the list array. Do not call unless the * internal object pointer is pointing to a valid object. Note that this * function does NOT affect the value of the internal pointer. */ tdata& operator[](unsigned int i) { return array[i]->object; } /// Removes the object currently pointed to by the internal pointer. void killobj() { unsigned int i; if (!pthisitem) return; if (pthisitem==pfirstitem) pfirstitem=pthisitem->pnext; if (pthisitem==plastitem) plastitem=pthisitem->pprev; Item* pnextitem=pthisitem->pnext; if (pthisitem->pprev) pthisitem->pprev->pnext=pthisitem->pnext; if (pthisitem->pnext) pthisitem->pnext->pprev=pthisitem->pprev; delete pthisitem; pthisitem=pnextitem; if (!pthisitem) isobj=false; for (i=thisobj+1;i59 || elapsed.seconds>59 || elapsed.hours>9999) strcpy(elapsed.str,"#time_err#"); else sprintf(elapsed.str,"%d%d:%d%d:%d%d", elapsed.hours/10,elapsed.hours%10,elapsed.minutes/10, elapsed.minutes%10,elapsed.seconds/10,elapsed.seconds%10); if (remaining.minutes>59 || remaining.seconds>59 || remaining.hours>9999) strcpy(remaining.str,"#time_err#"); sprintf(remaining.str,"%d%d:%d%d:%d%d", remaining.hours/10,remaining.hours%10,remaining.minutes/10, remaining.minutes%10,remaining.seconds/10,remaining.seconds%10); } public: /// Returns time since start of process in milliseconds long timemilli() { long newclock=(long)((double)clock()/(double)CLOCKS_PER_SEC*1000.0); if (newclock>=lastclock) m_clock+=newclock-lastclock; lastclock=newclock; return m_clock; } /// Initialises the timer, called by the constructor void init() { m_clock=0; lastclock=(long)((double)clock()/(double)CLOCKS_PER_SEC*1000.0); timebase=timemilli(); settimer(3600.0); } Timer() { init(); } void update() { unsigned long time=timemilli(); elapsed.time=(double)(time-timebase-start_t)/1000.0; elapsed.hours=(int)(elapsed.time/3600.0); elapsed.minutes=(int)(elapsed.time-elapsed.hours*3600)/60; elapsed.seconds=(int)(elapsed.time-elapsed.hours*3600- elapsed.minutes*60); elapsed.milliseconds=(int)(elapsed.time-elapsed.hours*3600-elapsed.minutes*60- elapsed.seconds); remaining.time=(double)(finish_t-time+timebase)/1000.0; if (remaining.time<0.0) remaining.time=0.0; remaining.hours=(int)(remaining.time/3600.0); remaining.minutes=(int)(remaining.time-remaining.hours*3600)/60; remaining.seconds=(int)(remaining.time-remaining.hours*3600- remaining.minutes*60); remaining.milliseconds=(int)(remaining.time-remaining.hours*3600- remaining.minutes*60-remaining.seconds); progress=elapsed.time*1000.0/(finish_t-start_t); print(); } /// Commences timing /** * \param duration gives number of seconds to "finished" status */ void settimer(double duration) { unsigned long time=timemilli(); start_t=time-timebase; finish_t=start_t+duration*1000.0; update(); } /// Sets one hour to "finished" status void settimer() { settimer(3600.0); } /// Specifies fractional progress (in range 0-1) towards "finished" status /** Modifies time remaining to "finished" */ void setprogress(double p) { if (p>1.0) p=1.0; update(); if (p>1.0e-10) finish_t=start_t+elapsed.time*1000.0/p; update(); } /// Returns fractional progress (in range 0-1) towards "finished" status double getprogress() { update(); return progress; } }; /// Writes date and time in standard Unix format to an xtring argument /** e.g. "Tue Nov 06 10:17:47 2001" */ void unixtime(xtring& result); /// Reads text according to FORTRAN-style format specification /** Function synopsis: bool readfor(FILE* strm, xtring format, ...) * * Reads in ASCII text data from input stream strm, according to a FORTRAN-style * format specification given in the string format. Addresses of the variables to be * assigned to are listed, in order of assignment, in the ellipsis (...) argument list * of the function. By default, any characters remaining on the current line are * discarded when the statement terminates (this behaviour can be overridden by a $ * specifier in the format string - see below). The function returns true if all * specified values could be read in and assigned, or false if an end-of-file * condition prevented some values from being read in and assigned. * * Version modified 2005-09-13 * The only change is that "a" format now reads only as far as next white space * character (or comma, or separator specified in format string), instead of end of * line as previously. The read to end of line specify "a#" in format string. * * Example: The following code opens for reading a text file called "climate.txt", * reads in two values of type double, one integer, and 12 further values of type * double, and assigns these values, respectively, to variables lon, lat, elev, and * the 12 elements of array mdata. Note that the arguments following the format * string are addresses of the variables to be assigned to - use the '&' prefix for * simple variables; omit the '&' if specifying an array name (a pointer). * * \code * #include * #include * * FILE* in; * double lon,lat; * int elev; * double mdata[12]; * * in=fopen("climate.txt","rt"); * if (!readfor(in,"f6.1,f5.1,i4,12f5.1",&lon,&lat,&elev,mdata)) * printf("Warning: not all values could be read in\n"); * \endcode * * \section format Format specifiers * * A subset of the format specifiers defined for input in the FORTRAN-77 language are * supported by function readfor. Note that there are some differences in the way * these are implemented compared to FORTRAN. Specifier fields in the format string * are normally separated by commas. Each instance of an F, I or A specifier is * assumedto correspond to one argument in the ellipsis argument list. Specifier * syntax is case-insensitive and spaces and tabs in the format string are ignored. * * Important: a value (other than 1) for n (number of items), if specified as part of * an F, I or A specification, assumes the values read are to be assigned to * consecutive elements of an array, whose starting address is given by a single * argument in the argument list. Do not use this feature to assign multiple values * with the same input format to multiple consective arguments. * * \subsection float Floating point specifier: nFw.dEe * * Reads one or more floating point numbers, assigning each value to a variable of * type double. * * - n - the number of items to read in; if n is not specified, one value is assumed. * See cautionary note above. * - w - the number of characters to read in for each item; if not specified, * characters are read up to the next space, tab, end-of-line, or the next * instance on the current line of a separator character appearing immediately * after the specifier in the format string (see below). * - d - the number of digits in the fractional part of the number; if a decimal point * is encountered, this overrides the specified value. If d is omitted and no * decimal point is encountered, the input string is interpreted as a whole * number. * - e - the number of digits in the exponent part of the number; if the character 'E' * or 'e' is encountered, this overrides the specified value. If e is omitted, * the exponent 0 (i.e., 10^0 = 1) is assumed. * * Examples: * * \verbatim Input stream: 1234 5.67 Format spec Strings read Values assigned F "1234" 1234.0 F3 "123" 123.0 F3.1 "123" 12.3 F4.2E1 "1234" 1.23E+04 2F4.2 "1234", " 5.6" 12.34, 5.6 \endverbatim * * \subsection integer Integer specifier: nIw * * Reads one or more integers, assigning each value to a variable of type int. * * - n - the number of items to read in; if n is not specified, one value is assumed. * See cautionary note above. * - w - the number of characters to read in for each item; if not specified, * characters are read up to the next space, tab, end-of-line, or the next * instance on the current line of a separator character appearing immediately * after the specifier in the format string (see below). * * Examples: * * \verbatim Input stream: 123 4567 Format spec Strings read Values assigned I "123" 123 I2 "12" 12 2I4 "123 ", "4567" 123, 4567 \endverbatim * \subsection char Character string specifier: nAw * * Reads one or more character strings, assigning each to a variable of type xtring. * * - n - the number of items to read in; if n is not specified, one value is assumed. * * - w - the number of characters to read for each item; if not specified, characters * are read up to the next white space character, comma, or the next instance of * a separator character appearing immediately after the specifier in the format * string (see below). * * Specify # after specified to read to end of line (see below) * * Examples: * * \verbatim Input stream: BERT HIGGINS Format spec Strings read and assigned A "BERT" A# "BERT HIGGINS" A2 "BE" 2A6 "BERT H", "IGGINS" \endverbatim * * \subsection pos Position specifier: nX * * Advances one or more characters on the input stream. * * - n - The number of characters to read and discard; if n is omitted, a single * character is read in and discarded. * * \subsection eol End-of-line specifier: / * * Advances to the end of the current line on the input stream. Input continues from * the start of the next line. * * \subsection suppress Suppress line feed specifier: $ * * If given as the last significant character in the format string, suppresses * reading and discarding of the remainder of the current line on the input stream. * * \subsection readeol Read to end of line specifier: # * * Continues reading to end of line (intended for use with character string (A) * specifier). * * \subsection sep Separator character specifier: * * Any character other than a space, tab or comma, that cannot be interpreted as part * of one of the specifiers above, is interpreted as a separator character, and * causes input up to and including the first instance of the specified separator * character on the current line. If given immediately following a variable-width F, * I or A specification, input continues, for each item, until an instance of the * specified separator character is encountered, or the end of the current line is * reached (otherwise input continues until a space, tab or the end of the line is * reached). If the separator character follows a fixed-width F, I or A specification, * or forms a separate specification field, the characters read are discarded. * * Examples: * * \verbatim Input stream: 123; Hello World!; 3.142 Format spec Strings read Values assigned I;A;F "123", " Hello World!", "3.142" 123, " Hello World!", 3.142 F2.1;;I2,A "12", " 3", ".142" 1.2, 3, ".142" \endverbatim * */ bool readfor(FILE* in, const char* fmt, ...); /// Used by functions with variable number of arguments to print to a string /** Function synopsis: void formatf(xtring& output, xtring& format, void* parglist) * * Translates a printf-style format string (format) and a set of "ellipsis" arguments * whose starting address is given by parglist to an output xtring (output) */ void formatf(xtring& output,char* format,va_list& v); /// Checks if a file exists /** * \returns true if the specified filename exists and can be opened for * reading, otherwise false */ bool fileexists(const xtring& filename); #endif // GUTIL_H