////////////////////////////////////////////////////////////////////////////////// // // InitUtil Version v2.1b March 26, 2004 // Web Page: www.bdsoft.com/tools/InitUtil.html // For the change log, see initutil2-log.txt. // // Utility templates to aid in the initialization of, and assignment to, // standard/extended containers of strings, pointers, numeric primitives (or // any scalar types for which extractors are defined). // // // Checked out under the following platforms: // Comeau C++ 4.3x (libcomo and Dinkum Unabridged Library *) // MSVC 7.1 (Stock lib, Dinkum Unabridged Library v4.02 *) // gcc 3.2 // Intel C++ 7/8 // // Notes: // Use /GX with MSVC 7.1/stock lib to suppress EH warnings // Use /Za with MSVC 7.1/stock lib if using hash-based containers. // // If using the Dinkum Unabridged Library, see the bug note // in the file Dinkum.txt (affects hash-based containers). // // // (C) Copyright Leor Zolman 2002-2004. Permission to copy, use, modify, sell and // distribute this software is granted provided this copyright notice appears // in all copies. This software is provided "as is" without express or implied // warranty, and with no claim as to its suitability for any purpose. // // Acknowledgements: Many, MANY thanks to Thorsten Ottosen for his collaboration // on the container initialization tool concept in general, and his specific // idea of using a conversion object to eliminate the awkward type specification // requirements in previous versions of this library. To see Thorsten's take // on container iniialization, check out: // // http://www.cs.auc.dk/%7Enesotto/init/ // // Several others have contributed their time, effort and coding to improve the // design of InitUtil and help with various platform-specific issues. I'd like // to thank and acknowledge them as well: // // Dave Abrahams // Chris Valkanis // Gianni Mariani // // // Summary of function templates provided in top-level namespace bds: // --------------------------------------------------------------------------- // // Container make_cont(const std::string &intext, // bool stripLeadingSpaces = true, char delim = ','); // // Container &set_cont(Container &cont , const std::string &intext, // bool stripLeadingSpaces = true, char delim = ','); // // Container &app_cont(Container &cont, const std::string &intext, // bool stripLeadingSpaces = true, char delim1 = ','); // // Container make_cont(int size, // Container::value_type val = 1, // Container::value_type incr = 1); // // Container &set_cont(Container &cont, int size, // Container::value_type val = 1, // Container::value_type incr = 1); // // Container &app_cont(Container &cont, int size, // Container::value_type val = 1, // Container::value_type incr = 1); // // Container make_cont_p(pointer1, pointer2, ... , NULL); // // Container make_cont_p(const Container &cont, pointer1, pointer2, ... , NULL); // // Container &set_cont_p(Container &cont, pointer1, pointer2, ..., NULL); // // Container &app_cont_p(Container &cont, pointer1, pointer2, ..., NULL); // // // "Make" vs. "Set"/"App" Functions: // --------------------------------- // // The "make_" functions create a new container and return it by value. // // The "set_" functions operate upon an existing container, clearing it first, and // return a reference to that container. // // The "app_" functions append to existing contents of a container, returning // a reference to that container. // // // String Scanning, Value Scanning, Pointer List and Sequence Generating Functions // ------------------------------------------------------------------------------- // // Functions taking a "intext" string parameter: // // If the container to be returned or set has a value_type of string, the // "intext" string parameter is scanned for a delimited set of string // values (the delimiter defaults to ','), optionally stripping leading spaces. // // If the container to be returned or set has any value_type *other* than // string, the string argument "intext" is scanned for a delimited set // of values, and those value are extracted into the container using the // value_type's native operator>> (and the boolean argument // "stripLeadingSpaces" is ignored). // // For maps and multimaps (including hash_map and hash_multimap), each value is // expected to be of the form "(key, value)". The delimter between the key and // the value may be specified; the delimters around the pair may be either (), // [] or {}. The comma *between *pairs* is optional. // // NOTE: The "stripLeadingSpaces" argument for maps/multimaps/etc. is currently // ignored. For these container types, normal extraction (where leading whitespace // is ignored, and the first trailing whitespace terminates the scan) is the only // flavor available. Custom string extraction may be achieved by creating your own // string-like class that overloads extraction (>>) to behave in the desired // manner. // // I hope to have this fixed in an update soon. But first, someone has to ex- // plain to me how to *really* preserve all whitespace in a string extraction... // // Functions operating on containers of pointers (*_cont_p): // // These functions take a NULL terminated, variable-length list of pointer // values (The container's value_type must match the pointer type). // // Functions taking size, initial value and increment: // // The remaining functions create a sequence of values to put into the // container. The size is required; the last two arguments are of // the same template type and default to 1 (so they must be of a type // convertible from int); they specify the starting value and the // increment for the sequence to be generated. // // // [Hard tab setting for the remainder of this source file is: 4] // //////////////////////////////////////////////////////////////////////////////////// #include #include #include #include #include #include #include #include #if !defined(__INITUTIL_H) #define __INITUTIL_H namespace bds { /////////////////////////////////////////////////////////////////////////////// // // // Helper Templates (used by the "public" templates in later sections) // // // /////////////////////////////////////////////////////////////////////////////// namespace helpers { // // Appender helper functor class for "appending" a value to a container in // the appropriate way. The default is to use push_back(); specializations // for set and multiset use insert(): // // General Appender template: template class Appender_ { public: Appender_(Container &c) : cont_(c) {} void operator()(const typename Container::value_type &value) { cont_.push_back(value); } private: Container &cont_; }; // Specialization of Appender for associative containers: template class Appender_ { public: Appender_(Container &c) : cont_(c) {} void operator()(const typename Container::key_type &value) { cont_.insert(value); } private: Container &cont_; }; // // Container &make_cont_(const T &, Container &c, // const string &intext, // bool, char delim); // // General helper function used by templates below for filling // containers (except map/multimap) of (non-string) values from a delimited // list (one string). // [Note: the bool parameter is ignored] // template Container &make_cont_(const T &, Container &c, const std::string &intext, bool, char delim) { using std::string; using std::isspace; string::const_iterator nextDelim; typename Container::value_type v; // create appropriate append functor Appender_ app(c); string::const_iterator curPos = intext.begin(), end = intext.end(); while (curPos < end) { nextDelim = std::find(curPos, end, delim); while (isspace(*curPos)) // ignore leading space ++curPos; // on each string string source(curPos, nextDelim); std::istringstream is(source); is >> v; app(v); curPos = ++nextDelim; } return c; } // // Container &make_cont_(const string &, Container &c, const string &intext, // bool stripLeadingSpaces, char delim); // // "Specialization" of make_cont_ for string value_type-ed // containers. Fills containers of *strings* from a delimited // list (one string): // template Container &make_cont_(const std::string &, Container &c, const std::string &intext, bool stripLeadingSpaces, char delim) { using std::string; using std::isspace; string::const_iterator nextDelim; // create appropriate append functor Appender_ app(c); string::const_iterator curPos = intext.begin(), end = intext.end(); while (curPos < end) { nextDelim = std::find(curPos, end, delim); if (stripLeadingSpaces) while (isspace(*curPos)) // ignore leading space ++curPos; // on each string app(string(curPos, nextDelim)); curPos = ++nextDelim; } return c; } // // Container &make_cont_(pair, Container &c, // const string &intext, // bool stripLeadingSpaces, char delim); // // map/multimap/hash_map/hash_multimap-specific helper function used by // templates below for filling maps and multimaps from a delimited list (one // string containing pairs). // To group a pair, use () [] or {} (they may be intermixed, but each pair must use // a matching set) // // stripLeadingSpaces only applies to the value of a (key, value) pair (spaces following // the opening pair delimiter are always retained for string values) // [NOTE: stripLeadingSpaces is currently ignored.] // // delim is the delimiter between the key and the value in each pair. // template Container &make_cont_(const std::pair, Container &c, const std::string &intext, bool, char delim) { using std::string; using std::isspace; string::const_iterator nextDelim; K k; V v; char closeDelim; string::const_iterator curPos = intext.begin(), end = intext.end(); while (true) { while (curPos != end && isspace(*curPos)) ++curPos; if (curPos == end) // no more data break; if (*curPos == '(') // make sure we see opening delim closeDelim = ')'; else if (*curPos == '[') closeDelim = ']'; else if (*curPos == '{') closeDelim = '}'; else { std::cerr << "InitUtil error: Missing '(', '[' or '{' at:\n\t" << string(curPos,end) << std::endl; return c; } ++curPos; // advance past '(' if (curPos == end) { std::cerr << "InitUtil error: Last pair incomplete in line:\n\t" << string(intext.begin(), end) << std::endl; return c; } if ((nextDelim = std::find(curPos, end, delim)) == end) { std::cerr << "InitUtil error: '" << delim << "' missing at:\n\t" << string(curPos, end) << std::endl; return c; } if (std::find(curPos, end, closeDelim) < nextDelim) { std::cerr << "InitUtil error: '" << closeDelim << "' precedes '" << delim << "' at:\n\t" << string(curPos, end) << std::endl; return c; } string source(curPos, nextDelim); std::istringstream is(source); is >> k; curPos = ++nextDelim; // advance past intra-pair delimiter if ((nextDelim = std::find(curPos, end, closeDelim)) == end) { std::cerr << "InitUtil error: Trailing '" << closeDelim << "' on pair missing at:\n\t" << string(curPos, end) << std::endl; return c; } source.assign(curPos, nextDelim); std::istringstream is2(source); is2 >> v; c.insert(typename Container::value_type(k,v)); curPos = ++nextDelim; // advance past intra-pair delimiter while (curPos != end && (isspace(*curPos) || *curPos == ',')) ++curPos; // ignore spaces and (optional) inter-pair commas if (curPos != end && *curPos == ',') ++curPos; // and go past it to next pair... } return c; } // // Container &make_cont_(Container &c, int size, // Container::value_type val, // Container::value_type incr); // // Helper function used by sequence generation templates // below for filling a container of values with a numeric sequence. // This version takes a size, initial value, and optional increment: // template Container &make_cont_(Container &c, int size, typename Container::value_type val, typename Container::value_type incr) { // create appropriate append functor Appender_ app(c); for (int i = 1; i <= size; i++) { app(val); val += incr; } return c; } // Conversion helper object for string-based initialization: class Converter_ { const std::string intext; bool stripLeadingSpaces; char delim; public: Converter_(const std::string &s, bool strip, char d) : intext(s), stripLeadingSpaces(strip), delim(d) {} template operator C() { C res; return make_cont_( typename C::value_type(), res, intext, stripLeadingSpaces, delim); } }; // Conversion helper object for sequence-based initialization: template class Converter2_ { std::size_t n_; V1 val_; V2 incr_; public: Converter2_( std::size_t n, V1 val, V2 incr ) : n_( n ), val_( val ), incr_( incr ) { } template operator C() { C res; return make_cont_(res, n_, val_, incr_); } }; } // End namespace helpers /////////////////////////////////////////////////////////////////////////////// // // // The "Public" Functions: // // // /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // // // Container initialization/generation templates given string-encoded data: // // // /////////////////////////////////////////////////////////////////////////////// // // Converter_ make_cont(const string &intext, // bool stripLeadingSpaces = true, char delim = ','); // // Template to generate container with given values // (optionally skipping leading spaces for string values). // [Explicit container specialization required] // [For containers of non-string value_types, the bool arg is // ignored.] // // Example: vector vs = make_cont("this, is, a, test"); // inline helpers::Converter_ make_cont(const std::string &intext, bool stripLeadingSpaces = true, char delim = ',') { return helpers::Converter_(intext, stripLeadingSpaces, delim); } // // Container &set_cont(Container &cont, const std::string &intext, // bool stripLeadingSpaces = true, char delim = ','); // // Function for assigning to a container a list of // values specified in a delimited string (optionally skipping // leading spaces for string values). // First arg is the container to assign to. // [For containers of non-string value_types, the bool arg is // ignored.] // // Example: vector vs; set_cont(vs, "1,2,3,5,500,600,-5"); // template inline Container &set_cont(Container &cont, const std::string &intext, bool stripLeadingSpaces = true, char delim = ',') { cont.clear(); return helpers::make_cont_ (typename Container::value_type(), cont, intext, stripLeadingSpaces, delim); } // // Container &app_cont(Container &cont, const std::string &intext, // bool stripLeadingSpaces = true, char delim = ','); // // Function for appending to a container a list of // values specified in a delimited string (optionally skipping // leading spaces for string values). // First arg is the container to assign to. // [For containers of non-string value_types, the bool arg is // ignored.] // // Example: vector vs; ...; app_cont(vs, "1,2,3,5,500,600,-5"); // template inline Container &app_cont(Container &cont, const std::string &intext, bool stripLeadingSpaces = true, char delim = ',') { // have had default values specified earlier return helpers::make_cont_ (typename Container::value_type(), cont, intext, stripLeadingSpaces, delim); } /////////////////////////////////////////////////////////////////////////////// // // // Container-of-pointers initialization/generation templates given variable- // // length list of pointer values: // // // /////////////////////////////////////////////////////////////////////////////// // // Container make_cont_p(T *p, ..., NULL); // // Template to generate container with given pointer values. // [Explicit container specialization required] // // Example: vector vip = make_cont_p >(&i, &j, &k, 0); // template Container make_cont_p(T *p, ...) { using namespace std; Container c; // create appropriate append functor helpers::Appender_ app(c); va_list ap; va_start(ap, p); while (p != NULL) // NULL ends list { app(p); p = va_arg(ap, T *); } va_end(ap); return c; } // // Container make_cont_p(const Container &, T *p, ..., NULL); // // Template to generate container with given pointer values. // First arg is an instance of the container type to build, but it is // NOT modified (used to convey type information only). // // Example: vector vip = make_cont_p(vip, &i, &j, &k, 0); // template Container make_cont_p(const Container &, T *p, ...) { using namespace std; Container c; // create appropriate append functor helpers::Appender_ app(c); va_list ap; va_start(ap, p); while (p != NULL) // NULL ends list { app(p); p = va_arg(ap, T *); } va_end(ap); return c; } // // Container &set_cont_p(Container &c, T *p, ..., NULL); // // Template to replace container contents with given pointer values. // First arg is the container to assign to. // // Example: vector vip; // // ... // set_cont_p(vip, &i, &j, &k, 0); // template Container &set_cont_p(Container &c, T *p, ...) { using namespace std; va_list ap; // create appropriate append functor helpers::Appender_ app(c); c.clear(); va_start(ap, p); while (p != NULL) // NULL ends list { app(p); p = va_arg(ap, T *); } va_end(ap); return c; } // // Container &app_cont_p(Container &c, T *p, ..., NULL); // // Template to append to container contents with given pointer values. // First arg is the container to assign to. // // Example: vector vip; // // ... // app_cont_p(vip, &i, &j, &k, 0); // template Container &app_cont_p(Container &c, T *p, ...) { using namespace std; va_list ap; // create appropriate append functor helpers::Appender_ app(c); va_start(ap, p); while (p != NULL) // NULL ends list { app(p); p = va_arg(ap, T *); } va_end(ap); return c; } /////////////////////////////////////////////////////////////////////////////// // // // Container initialization/generation templates given size, // // initial value and increment: // // // /////////////////////////////////////////////////////////////////////////////// // // Converter2_ make_cont(size_t n) // // Template to generate a container of values with size elements // in the sequence [val, val+incr, val+2*incr, ...]. // [Explicit container specialization required] // // Example: vector vi = make_cont(100); // inline helpers::Converter2_ make_cont( std::size_t n) { return helpers::Converter2_( n, 1, 1); } // // Converter2_<...> make_cont(size_t n, starting_val, increment = 1) // // Templates to generate a container of values with size elements // in the sequence [val, val+incr, val+2*incr, ...]. // // Example: vector vi = make_cont(100); // template< typename V> inline helpers::Converter2_ make_cont( std::size_t n, V val, V incr = 1 ) { return helpers::Converter2_( n, val, incr ); } template< typename V1, typename V2> inline helpers::Converter2_ make_cont( std::size_t n, V1 val = 1, V2 incr = 1) { return helpers::Converter2_( n, val, incr ); } // // Container &set_cont(Container &c, int size, // Container::value_type val = 1, // Container::value_type incr = 1); // // Template to assign size values to the given container, // in the sequence [val, val+incr, val+2*incr, ...]. // First arg is the container to be assigned. // // Example: vector vi; set_cont(vi, 100, 0, .1); // template inline Container &set_cont(Container &c, int size, typename Container::value_type val = 1, typename Container::value_type incr = 1) { c.clear(); return helpers::make_cont_(c, size, val, incr); } // // Container &app_cont(Container &c, int size, // Container::value_type val = 1, // Container::value_type incr = 1); // // Template to append size values to the given container, // in the sequence [val, val+incr, val+2*incr, ...]. // First arg is the container to be assigned. // // Example: vector vi; ...; app_cont(vi, 100, 0, .1); // template inline Container &app_cont(Container &c, int size, typename Container::value_type val = 1, typename Container::value_type incr = 1) { return helpers::make_cont_(c, size, val, incr); } } // namespace bds #endif // #if defined (INITUTIL_H)