GIT repositories

Index page of all the GIT repositories that are clonable form this server via HTTPS. Übersichtsseite aller GIT-Repositories, die von diesem Server aus über git clone (HTTPS) erreichbar sind.

Services

A bunch of service scripts to convert, analyse and generate data. Ein paar Services zum Konvertieren, Analysieren und Generieren von Daten.

GNU octave web interface

A web interface for GNU Octave, which allows to run scientific calculations from netbooks, tables or smartphones. The interface provides a web form generator for Octave script parameters with pre-validation, automatic script list generation, as well presenting of output text, figures and files in a output HTML page. Ein Webinterface für GNU-Octave, mit dem wissenschaftliche Berechnungen von Netbooks, Tablets oder Smartphones aus durchgeführt werden können. Die Schnittstelle beinhaltet einen Formulargenerator für Octave-Scriptparameter, mit Einheiten und Einfabevalidierung. Textausgabe, Abbildungen und generierte Dateien werden abgefangen und in einer HTML-Seite dem Nutzer als Ergebnis zur Verfügung gestellt.

C++ "Variant"/"Any"-Datentyp Klassentemplate

C++ "Variant"/"any" type variables class template

Eine flexible "Container-Klasse", welche weder RTTI noch Exceptions benötigt und die Datentypen bool, int, double, string, vector und map annehmen kann (für manche Kleinplatformen muss ohne RTTI und Exceptions kompiliert werden). Zusätzlich kann der Typ auf null und unset (undefined) gesetzt werden. Außer der STL hat sie keine Abhängigkeiten. Als Template Argumente kann angegeben werden, welche Datentypen intern für string, integer und float verwendet werden sollen. Für die primitiven Datentypen sowie wie genannten Container stehen typenspezifische Getter/Setter sowie die Operatoren (falls zutreffend) =, ==, !=, <, >, <=, >=, [] zur Verfügung. Weitere Methoden wie size(), push_back(), push_front(), pop_back(), pop_front(), clear() etc. verhalten sich wie die entsprechenden STL-Methoden, wobei immer Range-Checks durchgeführt werden. Um die Komplexität begrenzt zu halten (und wegen no-rtti) ist das Modell nicht polymorphisch, sondern basiert auf Typenprüfung in den Methoden.

Durch Inlining kann werden dem Compiler gute Optimierungsmöglichkeiten geboten, (hab' das mit -O3 -g -save-temps -fverbose-asm geprüft) ich rate aber (aus Performancegründen) davon ab diese Klasse "generell für alles" zu verwenden. Sie ist als Zwischencontainer für Datenimport/-export gedacht (und dafür habe ich sie ursprünglich implementiert).

Siehe auch: any Datentype der boost Bibliothek. Für "normale Plattformen" ist diese Klasse flexibler.

A flexible type "container class" that does not require RTTI nor exception support enabled (which is not possible on some small platforms). It can represent the types bool, int, double, string, vector and map. Additionally the type can be set to null and unset (undefined). The class has no dependencies except the STL, and the template arguments allow you to define which types are internally used for integer, floating point and string. For the primitive c++ data types and the named container types, it provides specific getters and setters, as well as the operators =, ==, !=, <, >, <=, >=, []. Other methods are named and operate equivalent to the known STL container methods: size(), push_back(), push_front(), pop_back(), pop_front(), clear() etc, except that the class methods always perform range checks. In order not to blow up the complexity of this template (and for the sake of no-rtti) the model is not polymorphic but based on current-type checks in the corresponding methods.

Using inline definitions the compiler has a lot of freedom to optimise (I checked the -O3 -g -save-temps -fverbose-asm output). However, I do not recommend to use this class as general solution for everything. The intention is to have a flexible intermediate container for import and export of data with unknown type/structure (and this is what I originally implemented it for).

See: any type of the boost library, which is more flexible and suitable for "normal platforms".

Dateien

Files

var.hh microtest

Beispiele

Examples

#include <sw/var.hh>
#include <iostream>
 
using namespace std;
using namespace sw;
 
#define print(V) cout << #V << "=" << (V) << endl;
 
int main(int argc, char** argv)
{
  var a;                    print(a);     // --> "a=null"
  a = 1;                    print(a);     // --> "a=1"
  a = 1.1e-2;               print(a);     // --> "a=0.011"
  a = "Foo";                print(a);     // --> "a=Foo"
  a = true;                 print(a);     // --> "a=true"
  a = false;                print(a);     // --> "a=false"
  a = var::empty_vector();  print(a);     // --> "a=vector(0)"
  a = var::empty_map();     print(a);     // --> "a=map(0)"
  a = var::nul();           print(a);     // --> "a=null"
  a = var::undef();         print(a);     // --> "a=undef"
 
  // From `null` and `undef` implicit conversion to map/vector
  a["a"] = 10;              print(a);     // --> "a=map(1)"
  a["b"] = 20;              print(a);     // --> "a=map(2)"
  a["c"] = 30;              print(a);     // --> "a=map(3)"
  a.dump(cout);                           //  map(3) =
                                          //  {
                                          //    "a": 10
                                          //    "b": 20
                                          //    "c": 30
                                          //  }
  a.clear();                print(a);     // --> "a=undef"
  a.push_back(10);          print(a);     // --> "a=vector(1)"
  a.push_back(20);          print(a);     // --> "a=vector(2)"
  a.push_back(30);          print(a);     // --> "a=vector(3)"
  a.push_back(40);   print(a.size());     // --> "a.size()=4"
  a.pop_back();      print(a.size());     // --> "a.size()=3"
  print(a.back());                        // --> "a.back()=30"
  print(a.front());                       // --> "a.front()=10"
  a.pop_front();    print(a.front());     // --> "a.front()=20"
  print(a[100]["a"][0]);                  // --> "a[100]["a"][0]=undef"
 
  a.clear(); var b=10, c=10;
  print(a==b);                            // --> "a==b=0"
  print(c==b);                            // --> "c==b=1"
  print(a!=10);                           // --> "a!=10=1"
  print(b==10);                           // --> "b==10=1"
  print(b< 10);                           // --> "b< 10=0"
  print(b> 10);                           // --> "b> 10=0"
 
  a["a"].push_back(1);
  a["a"].push_back(2);
  a["a"].push_back(var::empty_map());
  a["a"][2]["c"]="Hello";
  a["a"][2]["d"]="Foo";
  a["a"][2]["e"]["f"]["g"]["h"].push_back(1000);
  a.dump(cout);
  // ---> 
  //    map(1) =
  //    {
  //      "a": vector(3) =
  //      [
  //        0: 1
  //        1: 2
  //        2: map(3) =
  //        {
  //          "c": Hello
  //          "d": Foo
  //          "e": map(1) =
  //          {
  //            "f": map(1) =
  //            {
  //              "g": map(1) =
  //              {
  //                "h": vector(1) =
  //                [
  //                  0: 1000
  //                ]
  //              }
  //            }
  //          }
  //        }
  //      ]
  //    }
 
  // etc, etc, etc ...
}

Klassenquelltext

Class source code

/**
 * @package de.atwillys.cc.swl
 * @license BSD (simplified)
 * @author Stefan Wilhelm (stfwi)
 *
 * @file var.hh
 * @ccflags
 * @ldflags
 * @platform linux, bsd, windows
 * @standard >= c++98
 *
 * -----------------------------------------------------------------------------
 *
 * Simple "any-type" class template that works without runtime type information
 * (`-fno-rtti`).
 *
 * -----------------------------------------------------------------------------
 * +++ BSD license header +++
 * Copyright (c) 2010-2014, Stefan Wilhelm (stfwi, <cerbero s@atwilly s.de>)
 * All rights reserved.
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met: (1) Redistributions
 * of source code must retain the above copyright notice, this list of conditions
 * and the following disclaimer. (2) Redistributions in binary form must reproduce
 * the above copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the distribution.
 * (3) Neither the name of atwillys.de nor the names of its contributors may be
 * used to endorse or promote products derived from this software without specific
 * prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS
 * AND CONTRIBUTORS "AS IS" AND VAR EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
 * OR CONTRIBUTORS BE LIABLE FOR VAR DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON VAR THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN VAR
 * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 * -----------------------------------------------------------------------------
 */
#ifndef SW_ANY_HH
#define SW_ANY_HH
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <map>
#include <cmath>
#include <limits>
#include <cstring>
#if !(defined(DEBUG)) || (DEBUG==0)
#define asrt(X)
#define dbg1(X)
#define dbg2(X)
#define dbg3(X)
#else
#include <cassert>
#define asrt(X) assert(X);
#define dbg1(X) std::cerr << X << std::endl
#if DEBUG > 1
#define dbg2(X) std::cerr << X << std::endl
#else
#define dbg2(X)
#endif
#if DEBUG > 2
#define dbg3(X) std::cerr << X << std::endl
#else
#define dbg3(X)
#endif
#endif
 
namespace sw { namespace detail {
 
/**
 * Class class basic_var<...>
 * - no-rtti compatible
 */
template <typename StringType, typename FloatType, typename IntType>
class basic_var
{
public:
 
  typedef StringType string_type;
  typedef FloatType float_type;
  typedef IntType int_type;
  typedef typename string_type::value_type char_t;
  typedef std::map<string_type, basic_var> map_t;
  typedef std::vector<basic_var> vect_t;
  typedef enum { tnull=0, tbool, tint, tfloat, tstr, tvect, tmap, tunset } type_t;
 
public:
 
  /**
   * Standard constructor
   */
  inline basic_var() : t_(tnull), d_()
  { ; }
 
  /**
   * Typespec constructor
   */
  explicit inline basic_var(type_t t) : t_(t), d_()
  { ; } // Note: Pointers for empty containers can be NULL.
 
  /**
   * Assignment constructor. Sets null type on caught exceptions.
   * @param const T& v
   */
  template<typename T>
  inline basic_var(const T& v) : t_(tnull), d_()
  { try { assign(v); } catch(...) { clear(); } }
 
  /**
   * Assignment constructor. Sets null type on caught exceptions.
   * @param const T& v
   */
  template<typename T>
  explicit inline basic_var(const T* v) : t_(tnull), d_()
  { try { assign(v); } catch(...) { clear(); } }
 
  /**
   * Standard operator=. Returns reference, not const reference.
   * @param const basic_var& v
   * @return basic_var&
   */
  inline basic_var& operator=(const basic_var& v)
  { assign(v); return *this; }
 
  /**
   * Destructor
   * Clears (containers recursively) before deallocating.
   */
  virtual ~basic_var()
  { clear(); }
 
  /**
   * Returns if the content is empty (value 0, no children, empty text)
   * @return bool
   */
  inline bool operator ! ()
  { return empty(); }
 
  /**
   * Returns if equal to another var. Applies to numeric types and string,
   * map and vector always return false.
   * @param const basic_var &b
   * @return bool
   */
  inline bool operator==(const basic_var &b) const
  {
    if((type() <= tint)   && (b.type() <= tint))   return i() == b.i();
    if((type() <= tfloat) && (b.type() <= tfloat)) return f() == b.f();
    if((type() == tstr)   && (b.type() == tstr))   return s() == b.s();
    return false; // Todo: Add map/vector element compare ???
  }
 
  /**
   * Returns if not equal to another var. T.i. !operator==
   * @param const basic_var &b
   * @return bool
   */
  inline bool operator!=(const basic_var &b) const
  { return !((*this)==b); }
 
  /**
   * Less-equal. Checked for numeric types and string, always false for map
   * and vector.
   * @param const basic_var &b
   * @return bool
   */
  inline bool operator<=(const basic_var &b) const
  {
    if(type()<=tint   && b.type()<=tint)   return i() <= b.i();
    if(type()<=tfloat && b.type()<=tfloat) return f() <= b.f();
    if(type()==tstr   && b.type()==tstr)   return s() <= b.s();
    return false;
  }
 
  /**
   * Greater-equal. Checked for numeric types and string, always false for map
   * and vector.
   * @param const basic_var &b
   * @return bool
   */
  inline bool operator>=(const basic_var &b) const
  {
    if(type()<=tint   && b.type()<=tint)   return i() >= b.i();
    if(type()<=tfloat && b.type()<=tfloat) return f() >= b.f();
    if(type()==tstr   && b.type()==tstr)   return s() >= b.s();
    return false;
  }
 
  /**
   * Less than. Checked for numeric types and string, always false for map
   * and vector.
   * @param const basic_var &b
   * @return bool
   */
  inline bool operator<(const basic_var &b) const
  {
    if(type()<=tint   && b.type()<=tint)   return i() < b.i();
    if(type()<=tfloat && b.type()<=tfloat) return f() < b.f();
    if(type()==tstr   && b.type()==tstr)   return s() < b.s();
    return false;
  }
 
  /**
   * Greater than. Checked for numeric types and string, always false for map
   * and vector.
   * @param const basic_var &b
   * @return bool
   */
  inline bool operator>(const basic_var &b) const
  {
    if(type()<=tint   && b.type()<=tint)   return i() > b.i();
    if(type()<=tfloat && b.type()<=tfloat) return f() > b.f();
    if(type()==tstr   && b.type()==tstr)   return s() > b.s();
    return false;
  }
 
public:
 
  //////////////////////////////////////////////////////////////////////////////
  // General information (methods const)
 
  /**
   * Returns the current type as enumeration value
   * @return type_t
   */
  inline type_t type() const throw()
  { return t_; }
 
  /**
   * Returns the type name as string
   * @return const char_t*
   */
  inline const char_t * typen() const throw()
  { static const char_t* tnames[] = { "null", "bool", "int", "float", "string",
    "vector", "map", "(unknown)" }; return t_>=0 && t_<tunset ?
    tnames[(unsigned)t_] : tnames[(unsigned)tunset]; }
 
  /**
   * Returns true if the current data type is null
   * @return bool
   */
  inline bool is_null() const throw()
  { return t_ == tnull; }
 
  /**
   * Returns true if the current data type is int
   * @return bool
   */
  inline bool is_int() const throw()
  { return t_ == tint; }
 
  /**
   * Returns true if the current data type is bool
   * @return bool
   */
  inline bool is_bool() const throw()
  { return t_ == tbool; }
 
  /**
   * Returns true if the current data type is float_type
   * @return bool
   */
  inline bool is_float() const throw()
  { return t_ == tfloat; }
 
  /**
   * Returns true if the current data type is string_type
   * @return bool
   */
  inline bool is_string() const throw()
  { return t_ == tstr; }
 
  /**
   * Returns true if the current data type is vector
   * @return bool
   */
  inline bool is_vector() const throw()
  { return t_ == tvect; }
 
  /**
   * Returns true if the current data type is map
   * @return bool
   */
  inline bool is_map() const throw()
  { return t_ == tmap; }
 
  /**
   * True if the data type is `unset`
   * @return bool
   */
  inline bool is_unset() const throw()
  { return t_<0 || t_ >= tunset; }
 
  /**
   * Returns of the value is 0 or the container empty.
   * @return bool
   */
  inline bool empty() const throw()
  { return d_.d==0 || t_<=tnull || t_>=tunset
      || (t_==tstr  && ((!d_.s) || d_.s->empty()))
      || (t_==tvect && ((!d_.v) || d_.v->empty()))
      || (t_==tmap  && ((!d_.m) || d_.m->empty()));
  }
 
  /**
   * Returns the size of the container/length of string or -1 if scalar/erroneous.
   * @return int
   */
  inline int size() const throw()
  { return (t_< tstr || t_>tmap || (!d_.s)) ? (-1)
         : (t_==tstr  ? d_.s->length()
         : (t_==tvect ? d_.v->size()
         : (t_==tmap  ? d_.m->size()
         : (-1) )));
  }
 
public:
 
  //////////////////////////////////////////////////////////////////////////////
  // General modification
 
  /**
   * Clearing: Numeric/bool value=0, pointer=NULL, optionally delete container.
   * After clearing the type is `unset`.
   * @return void
   */
  inline void clear()
  {
    if(t_ >= tstr && t_<= tmap && d_.s) {
      t_ = tnull;
      switch(t_) {
        case tstr: delete  d_.s; break;
        case tvect: delete d_.v; break;
        case tmap: delete  d_.m; break;
        default: ; // throw ?
      }
    }
    ::memset(&d_, 0, sizeof(union data_t));
    t_ = tunset;
  }
 
  /**
   * Filters out undefined basic_var elements in the vector/map.
   * @return basic_var & *this
   */
  basic_var & erase_undefs()
  {
    if(t_==tvect && (d_.v)) {
      vect_t& rv = *d_.v;
      if(!rv.empty()) {
        int i = (int)(rv.size()-1);
        while(rv[i].is_unset()) --i;
        if(i<0) { v(empty_vector()); return *this; }
        rv.resize(i+1);
        while(i>=0) {
          if(rv[i].is_unset()) rv.erase(rv.begin()+i);
          else rv[i].erase_undefs();
          --i;
        }
        if(rv.empty()) v(empty_vector()); // Reallocate
      }
      return *this;
    }
    if(t_ == tmap && (d_.m)) {
      map_t& rm = *d_.m;
      for(typename map_t::iterator it=rm.begin(); it!=rm.end(); ++it) {
        if(it->second.is_unset()) {
          rm.erase(it); // stfwi: double check if iterator still points to the correct next after erasing
        } else {
          it->second.erase_undefs();
        }
      }
    }
    return *this;
  }
 
public:
 
  //////////////////////////////////////////////////////////////////////////////
  // Specific type getters
 
  /**
   * Return integer value or 0 if inappropriate
   * @return int_type
   */
  inline int_type i() const
  { return t_ <= tint ? d_.i : (t_==tfloat ? sat_cast<float_type, int_type>(d_.d) : 0); }
 
  /**
   * Return integer value or 0 if inappropriate
   * @return int_type
   */
  inline bool b() const
  { return i() != 0; }
 
  /**
   * Return floating point value or NaN if inappropriate
   * @return float_type
   */
  inline float_type f() const
  { return t_ <= tint ? (float_type)d_.i : (t_==tfloat ? d_.d : std::numeric_limits<double>::quiet_NaN()); }
 
  /**
   * String value, no type conversion, empty if inappropriate
   * @return const string_type &
   */
  inline const string_type & s() const
  { return (t_==tstr && d_.s!=0) ? *d_.s : empty_string(); }
 
  /**
   * Vector value, no type conversion, empty if inappropriate
   * @return const vect_t &
   */
  inline const vect_t & v() const
  { return (t_==tvect && d_.v!=0) ? *d_.v : empty_vector(); }
 
  /**
   * Map value, no type conversion, empty if inappropriate
   * @return const map_t &
   */
  inline const map_t & m() const
  { return (t_==tmap && d_.m!=0) ? *d_.m : empty_map(); }
 
 
public:
 
  //////////////////////////////////////////////////////////////////////////////
  // Specific type setters
 
  /**
   * Assign integer value
   * @return float_type
   */
  inline basic_var& i(int_type v)
  { if(t_ >= tstr) clear(); t_ = tint; d_.i = v; return *this; }
 
  /**
   * Assign integer value
   * @return float_type
   */
  inline basic_var& b(bool v)
  { if(t_ >= tstr) clear(); t_ = tbool; d_.i = (int_type) v; return *this; }
 
  /**
   * Assign floating point value
   * @float_type v
   * @return basic_var&
   */
  inline basic_var& f(float_type v)
  { if(t_ >= tstr) clear(); t_ = tfloat; d_.d = v; return *this; }
 
  /**
   * Assign string value (copy)
   * @param const string_type &
   * @return basic_var&
   */
  inline basic_var& s(const string_type & s)
  {
    if(t_ > tstr) clear();
    if(t_ != tstr || !d_.s) {
      try { d_.s = new string_type(s); } catch(...) { d_.s = 0; }
      t_ = !d_.s ? tunset : tstr;
    } else {
      d_.s->assign(s);
    }
    return *this;
  }
 
  /**
   * Assign vector value (copy)
   * @param const vect_t &
   * @return basic_var&
   */
  inline basic_var& v(const vect_t & v__)
  { if(t_ >= tstr) clear(); d_.v = new vect_t(v__); t_ = ((!d_.v) ? tunset : tvect);
    return *this; }
 
  /**
   * Assign map value (copy)
   * @param const map_t &
   * @return basic_var&
   */
  inline basic_var& m(const map_t & m__)
  { if(t_ >= tstr) clear(); d_.m = new map_t(m__); t_ = !d_.m ? tunset : tmap;
    return *this; }
 
public:
 
  //////////////////////////////////////////////////////////////////////////////
  // Vector specific
 
  /**
   * Vector value by index
   * @return const basic_var &
   */
  inline const basic_var & v(int i) const
  { return (t_!=tvect || (!d_.v) || i<0 || i>=(int)d_.v->size()) ?
      undef() : (d_.v->at(i)); }
 
  /**
   * Writable vector value by index. If the current type is null or `unset`,
   * it will be changed to vector. If the index does not exist returns a reference
   * to the undef() object.
   * @return basic_var &
   */
  inline basic_var & v(int i)
  {
    if(t_==tnull || t_==tunset) { clear(); v(empty_vector()); }
    if(t_!=tvect || (!d_.v) || i<0 || i>=(int)d_.v->size()) return undef_ref();
    return d_.v->at(i);
  }
 
  /**
   * Vector value by index
   * @param const int &i
   * @return const basic_var &
   */
  inline const basic_var & operator[] (int i) const
  { return v(i); }
 
  /**
   * Vector value by index
   * @param const int &i
   * @return basic_var &
   */
  inline basic_var & operator[] (int i)
  { return v(i); }
 
  /**
   * Vector: Return first element or `unset` if empty
   * Other : Return const `unset`
   * @return basic_var &
   */
  inline const basic_var & front() const
  { return (t_!=tvect || (!d_.v) || d_.v->empty()) ? undef() : d_.v->front() ; }
 
  /**
   * Vector: Return first element or `unset` if empty
   * Other : Return const `unset`
   * @return basic_var &
   */
  inline const basic_var & back() const
  { return (t_!=tvect || (!d_.v) || d_.v->empty()) ? undef() : d_.v->back() ; }
 
  /**
   * Vector: Append value at the end
   * Other : Do nothing
   * @return basic_var & *this;
   */
  inline basic_var & push_back (const basic_var & o)
  {
    if(o.is_unset()) return *this;
    if(t_==tnull || t_==tunset) v(empty_vector());
    if(t_!=tvect) return *this;
    d_.v->push_back(o);
    return *this;
  }
 
  /**
   * Vector: Append value at the front
   * Other : Do nothing
   * @return basic_var & *this;
   */
  inline basic_var & push_front (const basic_var & o)
  {
    if(o.is_unset()) return *this;
    if(t_==tnull || t_==tunset) v(empty_vector());
    if(t_!=tvect) return *this;
    d_.v->insert(d_.v->begin(), o);
    return *this;
  }
 
  /**
   * Erase vector element before position `i`
   * @param int i
   * @const basic_var &v
   * @return basic_var & *this
   */
  inline basic_var & insert(int i, const basic_var &o)
  {
    if(o.is_unset()) return *this;
    if(t_==tnull || t_==tunset) v(empty_vector());
    if(t_!=tvect) return *this;
    if(d_.v->empty() || (i>=(int)d_.v->size())) d_.v->push_back(o);
    else d_.v->insert(d_.v->begin()+(i<0 ? 0 : i), o);
    return *this;
  }
 
  /**
   * Vector: Remove first element.
   * Other : Do nothing
   * @return basic_var & *this
   */
  inline basic_var & pop_front()
  {
    if(t_==tvect && (d_.v!=0) && (!d_.v->empty())) {
      if(d_.v->size()==1) v(empty_vector());
      else d_.v->erase(d_.v->begin());
    }
    return *this;
  }
 
  /**
   * Vector: Remove last element.
   * Other : Do nothing
   * @return basic_var & *this
   */
  inline basic_var & pop_back()
  {
    if(t_==tvect && (d_.v!=0) && !(d_.v->empty())) {
      if(d_.v->size()==1) v(empty_vector()); // not d_.v->clear();
      else d_.v->pop_back();
    }
    return *this;
  }
 
  /**
   * Erase vector element at position `i`
   * @param int i
   * @return basic_var & *this
   */
  inline basic_var & erase(int i)
  {
    if(t_==tvect && (d_.v) && i>=0 && i<(int)d_.v->size()) {
      if(i==0 && d_.v->size()==1) v(empty_vector()); // not d_.v->clear();
      else d_.v->erase(d_.v->begin()+(typename vect_t::size_type)i);
    }
    return *this;
  }
 
public:
 
  //////////////////////////////////////////////////////////////////////////////
  // Map specific
 
  /**
   * Map value by key
   * @param const string_type &key
   * @return const basic_var &
   */
  inline const basic_var & m(const string_type& key) const
  {
    if (t_!=tmap || (!d_.m)) return undef();
    typename map_t::const_iterator it = d_.m->find(key);
    return it == d_.m->end() ? undef() : it->second;
  }
 
  /**
   * Map value by key
   * @param const string_type &key
   * @return basic_var &
   */
  inline basic_var & m(const string_type& key)
  {
    if(key.empty()) return undef_ref();
    if(t_==tunset || t_==tnull) m(empty_map());
    if(t_!=tmap || (!d_.m)) return undef_ref();
    typename map_t::iterator it = d_.m->find(key);
    if(it == d_.m->end()) {
      std::pair<typename map_t::iterator, bool> r =
        d_.m->insert(std::pair<string_type, basic_var>(key, undef()));
      if(r.first == d_.m->end() || !r.second) {
        clear();
        return undef_ref();
      } else {
        it = r.first;
      }
    }
    return it->second;
  }
 
  /**
   * Set map value
   * @param const string_type &key
   * @return basic_var & *this
   */
  inline basic_var & m(const string_type& key, const basic_var &v)
  { if(!key.empty() && !v.is_unset()) m(key) = v; return *this; }
 
  /**
   * Map value by key
   * @param const string_type &key
   * @return const basic_var &
   */
  inline const basic_var & operator[] (const string_type& key) const
  { return m(key); }
 
  /**
   * Map value by key
   * @param const char_t *key
   * @return const basic_var &
   */
  inline const basic_var & operator[] (const char_t *key) const
  { return key ? m(string_type(key)) : undef(); }
 
  /**
   * Map value by key
   * @param const string_type &key
   * @return basic_var &
   */
  inline basic_var & operator[] (const string_type& key)
  { return m(key); }
 
  /**
   * Map value by key
   * @param const char_t *key
   * @return basic_var &
   */
  inline basic_var & operator[] (const char_t *key)
  { return key ? m(string_type(key)) : undef_ref(); }
 
  /**
   * Erase map element with key `key`
   * @param const string_type &key
   * @return basic_var & *this
   */
  inline basic_var & erase(const string_type &key)
  {
    if(t_!=tmap || (!d_.m) || key.empty() || d_.m->empty()) return *this;
    if(d_.m->find(key) != d_.m->end()) d_.m->erase(key);
    return *this;
  }
 
  /**
   * Erase map element with key `key`
   * @param const char_t* key
   * @return basic_var & *this
   */
  inline basic_var & erase(const char_t* key)
  { if(key) erase(string_type(key)); return *this; }
 
  //////////////////////////////////////////////////////////////////////////////
  // String specific
 
  /**
   * String value, implicit conversion, empty if inappropriate
   * @return string_type
   */
  string_type str() const
  {
    ss_t ss; // let stringstream handle char/wchar string conversion
    switch(t_) {
      case tnull: return "null";
      case tbool: return d_.i ? "true" : "false";
      case tint: ss << d_.i; break;
      case tfloat: ss << d_.d; break;
      case tstr: return d_.s ? (*d_.s) : "invalid string";
      case tvect:
        if(!d_.v) return "invalid vector";
        ss << "vector(" << (int) d_.v->size() << ")";
        break;
      case tmap:
        if(!d_.m) return "invalid map";
        ss << "map(" << ((int) d_.m->size()) << ")";
        break;
      case tunset:
        return "undef";
      default:
        return "invalid type";
    }
    return ss.str();
  }
 
public:
 
  //////////////////////////////////////////////////////////////////////////////
  // Clause operators
 
  inline bool operator==(const string_type &b) const
  {
    if(type() == tstr) return s() == b;
    if(type() > tstr) return false;
    if(empty()) return b.empty();
    if(b.empty()) return empty();
    std::stringstream ss((string_type)b);
    double d; return (!(ss>>d)) ? false : (f() == d);
  }
 
  inline bool operator!=(const string_type &b) const
  { return !operator==(b); }
 
  inline bool operator<(const string_type &b) const
  {
    if(type() == tstr) return s() < b;
    if(type() > tstr) return false;
    if(empty()) return !b.empty();
    if(b.empty()) return false;
    std::stringstream ss((string_type)b);
    double d; return (!(ss>>d)) ? false : (f() < d);
  }
 
  inline bool operator>(const string_type &b) const
  {
    if(type() > tstr) return false;
    if(empty()) return false;
    if(type() == tstr) return s() > b;
    if(b.empty()) return true;
    std::stringstream ss((string_type)b);
    double d; return (!(ss>>d)) ? false : (f() > d);
  }
 
  inline bool operator>=(const string_type &b) const
  {
    if(type() == tstr) return s() >= b;
    if(type() > tstr) return false;
    if(empty()) return b.empty();
    std::stringstream ss((string_type)b);
    double d; return (!(ss>>d)) ? false : (f() >= d);
  }
 
  inline bool operator<=(const string_type &b) const
  {
    if(type() == tstr) return s() <= b;
    if(type() > tstr) return false;
    if(b.empty()) return empty();
    std::stringstream ss((string_type)b);
    double d; return (!(ss>>d)) ? false : (f() <= d);
  }
 
  inline bool operator==(const char_t *b) const
  { return (!b) ? empty() : operator==(string_type(b)); }
 
  inline bool operator!=(const char_t *b) const
  { return !operator==(b); }
 
  inline bool operator>(const char_t *b) const
  { return (!b) ? !empty() : operator>(string_type(b)); }
 
  inline bool operator<(const char_t *b) const
  { return (!b) ? false : operator<(string_type(b)); }
 
  inline bool operator<=(const char_t *b) const
  { return (!b) ? empty() : operator<=(string_type(b)); }
 
  inline bool operator>=(const char_t *b) const
  { return (!b) ? true : operator>=(string_type(b)); }
 
  #define operator_primitives(T) \
  inline bool operator==(const T &b) const { return ((T)(*this)) == b; } \
  inline bool operator!=(const T &b) const { return ((T)(*this)) != b; } \
  inline bool operator>=(const T &b) const { return ((T)(*this)) >= b; } \
  inline bool operator<=(const T &b) const { return ((T)(*this)) <= b; } \
  inline bool operator> (const T &b) const { return ((T)(*this)) >  b; } \
  inline bool operator< (const T &b) const { return ((T)(*this)) <  b; }
  operator_primitives(bool)
  operator_primitives(char)
  operator_primitives(unsigned char)
  operator_primitives(short)
  operator_primitives(unsigned short)
  operator_primitives(int)
  operator_primitives(unsigned int)
  operator_primitives(long)
  operator_primitives(unsigned long)
  operator_primitives(float)
  operator_primitives(double)
  #undef operator_primitives
 
public:
 
  //////////////////////////////////////////////////////////////////////////////
  // Typecast operators
 
  /**
   * Equivalent to b()
   * @return bool
   */
  inline operator bool () const
  { return b(); }
 
  /**
   * Equivalent to f()
   * @return float
   */
  inline operator float () const
  { return (float) f(); }
 
  /**
   * Equivalent to f()
   * @return double
   */
  inline operator double () const
  { return f(); }
 
  /**
   * To string
   * @return string_type
   */
  inline operator string_type () const
  { return t_==tstr && d_.s ? s() : str(); }
 
  /**
   * Saturated type conversion
   * @return unsigned short
   */
  inline operator char () const
  {
    if(t_==tstr) return (d_.s && !d_.s->empty()) ? d_.s->at(0) : 0;
    return t_==tfloat ? sat_cast<float_type, char>(d_.d) : sat_cast<int_type, char>(i());
  }
 
  /**
   * Saturated type conversion
   * @return unsigned short
   */
  inline operator unsigned char () const
  { return t_==tfloat ? sat_cast<float_type, unsigned char>(d_.d) :
        sat_cast<int_type, unsigned char>(i()); }
 
  /**
   * Saturated type conversion
   * @return short
   */
  inline operator short () const
  { return t_==tfloat ? sat_cast<float_type, short>(d_.d) : sat_cast<int_type, short>(i()); }
 
  /**
   * Saturated type conversion
   * @return unsigned short
   */
  inline operator unsigned short () const
  { return t_==tfloat ? sat_cast<float_type, unsigned short>(d_.d) :
        sat_cast<int_type, unsigned short>(i()); }
 
  /**
   * Saturated type conversion
   * @return int
   */
  inline operator int () const
  { return t_==tfloat ? sat_cast<float_type, int>(d_.d) : sat_cast<int_type, int>(i()); }
 
  /**
   * Saturated type conversion
   * @return unsigned
   */
  inline operator unsigned () const
  { return t_==tfloat ? sat_cast<float_type, unsigned>(d_.d) : sat_cast<int_type, unsigned>(i()); }
 
  /**
   * Saturated type conversion
   * @return long
   */
  inline operator long () const
  { return t_==tfloat ? sat_cast<float_type, long>(d_.d) : sat_cast<int_type, long>(i()); }
 
  /**
   * Saturated type conversion
   * @return unsigned long
   */
  inline operator unsigned long () const
  { return t_==tfloat ? sat_cast<float_type, unsigned long>(d_.d) :
        sat_cast<int_type, unsigned long>(i()); }
 
public:
 
  //////////////////////////////////////////////////////////////////////////////
  // Assignment
 
  /**
   * Allows easier assignment for primitive types. Specialisations of
   * assign() will be applied.
   * @param const T& v
   * @return basic_var& *this;
   */
  template <typename T> inline basic_var& operator=(const T& v)
  { assign(v); return *this; }
 
  /**
   * Allows easier assignment for primitive types. Specialisations of
   * assign() will be applied.
   * @param const T& v
   * @return basic_var& *this;
   */
  template <typename T> inline basic_var& operator=(const T* v)
  { assign(v); return *this; }
 
protected:
 
  //////////////////////////////////////////////////////////////////////////////
  // Assignment
 
  /**
   * @param const basic_var &o
   */
  inline void assign(const basic_var &o)
  {
    if(t_ >= tstr && d_.s) clear();
    if(o.type() == tnull)  { t_=tnull; d_.d=0; return; }
    if(o.type() <= tint)   { i(o.i()); return; }
    if(o.type() == tfloat) { f(o.f()); return; }
    if(o.type() == tstr)   { s(o.s()); return; }
    if(o.type() == tvect)  { v(o.v()); return; }
    if(o.type() == tmap)   { m(o.m()); return; }
    t_ = tunset;
  }
 
  inline void assign(const unsigned long &v)
  { if(v <= (unsigned long) std::numeric_limits<int_type>::max()) i(v); else f(v); }
 
  inline void assign(const long &v)
  { if(v > (long) std::numeric_limits<int_type>::max()
    || v < (long) std::numeric_limits<int_type>::min()) f(v); else i(v); }
 
  inline void assign(const unsigned int &v)
  { if(v <= (unsigned long) std::numeric_limits<int_type>::max()) i(v); else f(v); }
 
  inline void assign(const int &v)
  {
    if((v > (long) std::numeric_limits<int_type>::max())
    || (v < (long) std::numeric_limits<int_type>::min())) f(v); else i(v); }
 
  inline void assign(const string_type &v)
  { s(v); }
 
  inline void assign(const char_t &v)
  { s(string_type(1, v)); }
 
  inline void assign(const char_t *v)
  { if(v) s(v); else { clear(); t_=tunset; } }
 
  inline void assign(const vect_t &v__)
  { v(v__); }
 
  inline void assign(const map_t &v)
  { m(v); }
 
  inline void assign(const bool &v)
  { b(v); }
 
  inline void assign(const unsigned char &v)
  { i((int_type)v); }
 
  inline void assign(const short &v)
  { i((int_type)v); }
 
  inline void assign(const unsigned short &v)
  { i((int_type)v); }
 
  inline void assign(const float &v)
  { f(v); }
 
  inline void assign(const double &v)
  { f(v); }
 
  inline void assign(const volatile unsigned long &v)
  { assign((unsigned long)v); }
 
  inline void assign(const volatile long &v)
  { assign((long)v); }
 
  inline void assign(const volatile unsigned int &v)
  { assign((unsigned int)v); }
 
  inline void assign(const volatile int &v)
  { assign((int)v); }
 
  inline void assign(const volatile bool &v)
  { assign((bool)v); }
 
  inline void assign(const volatile unsigned char &v)
  { assign((unsigned char)v); }
 
  inline void assign(const volatile short &v)
  { assign((short)v); }
 
  inline void assign(const volatile unsigned short &v)
  { assign((unsigned short)v); }
 
  inline void assign(const volatile float &v)
  {  assign((float)v); }
 
  inline void assign(const volatile double &v)
  {  assign((double)v); }
 
public:
 
  //////////////////////////////////////////////////////////////////////////////
  // Static object getters
 
  /**
   * Returns a basic_var that represents `null`
   * @return const basic_var &
   */
  inline static const basic_var & nul()
  { static const basic_var v(tnull); return v; }
 
  /**
   * Returns a basic_var const reference that is `unset`
   * @return const basic_var &
   */
  inline static const basic_var & undef()
  { static const basic_var v(tunset); return v; }
 
  /**
   * Returns a basic_var reference that is `unset`
   * @return basic_var &
   */
  inline static basic_var & undef_ref()
  { static basic_var v; v.d_.d=0; v.t_=tunset; return v; }
 
  /**
   * Returns a basic_var reference that is `unset`
   * @return const map_t &
   */
  inline static const map_t & empty_map()
  { static const map_t v; return v; }
 
  /**
   * Returns a reference to a constant empty vector.
   * @return const vect_t &
   */
  inline static const vect_t & empty_vector()
  { static const vect_t v; return v; }
 
  /**
   * Returns a reference to a constant empty string
   * @return const string_type &
   */
  inline static const string_type & empty_string()
  { static const string_type v; return v; }
 
public:
 
  //////////////////////////////////////////////////////////////////////////////
  // Dump
 
  void dump(std::basic_ostream<char_t>&os, int indent=0) const
  { if(indent<0) indent = 0; os << string_type(indent, ' '); dumpr(os, indent); }
 
protected:
 
  void dumpr(std::basic_ostream<char_t>&os, int indent) const
  {
    os << str();
    if(t_ == tvect) {
      if(!empty()) {
        os << " =" << std::endl << string_type(indent, ' ') << "[" << std::endl;
        for(int i=0; i<size(); ++i) {
          os << string_type(indent+2, ' ') << i << ": ";
          v(i).dumpr(os, indent+2);
        }
        os << string_type(indent, ' ') << "]";
      } else {
        os << "[]";
      }
    } else if(t_ == tmap) {
      if(!empty()) {
        os << " =" << std::endl << string_type(indent, ' ') << "{" << std::endl;
        typename map_t::const_iterator it = m().begin();
        typename map_t::const_iterator e = m().end();
        for(; it!=e; ++it) {
          os << string_type(indent+2, ' ') << "\"" << it->first << "\": ";
          it->second.dumpr(os, indent+2);
        }
        os << string_type(indent, ' ') << "}";
      } else {
        os << "{}";
      }
    }
    os << std::endl;
  }
 
  //////////////////////////////////////////////////////////////////////////////
  // Auxiliary functions / methods
 
  /**
   * Inlined numeric saturated cast
   * @param I v
   * @return O
   */
  template<typename I, typename O> inline static O sat_cast(I v)
  { // A lot of clauses that will be optimised away by the compiler
    #ifdef __MSC_VER
    #pragma warning(push)
    #pragma warning(disable: 4244)
    #endif
    #define i std::numeric_limits<I>
    #define o std::numeric_limits<O>
    if(i::is_integer && o::is_integer) {
      if((sizeof(O) >= sizeof(I)) && (i::is_signed == o::is_signed)) return (O) v;
      if(i::is_signed && !o::is_signed) return (I)v<0 ? (O)0 : (sizeof(O)>=sizeof(I)
           ? (O)v : ( v > (I)o::max() ? o::max() : v));
      if(!i::is_signed && o::is_signed) return (v > (I)o::max()) ? o::max() : (O)v;
      return v < (I)o::min() ? (I)o::min() : (v > (I)o::max() ? (I)o::max() : v);
    }
    if(!o::is_integer) return (O) v;
    return v < (I)o::min() ? (I)o::min() : (v > (I)o::max() ? (I)o::max() : v);
    #undef i
    #undef o
    #ifdef __MSC_VER
    #pragma warning(pop)
    #endif
  }
 
private:
 
  //////////////////////////////////////////////////////////////////////////////
  // Instance variables / private types
 
  typedef std::basic_stringstream<typename string_type::value_type> ss_t;
  type_t t_;
 
  union data_t {
    int_type i; float_type d; string_type* s; map_t* m; vect_t* v;
    data_t() { memset(this, 0, sizeof(*this)); }
  } d_;
 
};
 
////////////////////////////////////////////////////////////////////////////////
// ostream <<
 
/**
 * ostream <<
 * @param std::basic_ostream<typename string_type::value_type> &os
 * @param basic_var<string_type, float_type, int_type>
 * @return std::basic_ostream<typename string_type::value_type>&
 */
template <typename string_type, typename float_type, typename int_type>
std::basic_ostream<typename string_type::value_type>& operator << (
  std::basic_ostream<typename string_type::value_type>& os,
  const basic_var<string_type, float_type, int_type> &o
)
{
  os << o.str(); return os;
}
 
////////////////////////////////////////////////////////////////////////////////
// OPERATORS FOR sw::var    PRIMITIVE OP VAR
 
#define VAR basic_var<std::string, double, long>
inline bool operator==(const VAR::string_type &b, const VAR &a)  { return a == b; }
inline bool operator!=(const VAR::string_type &b, const VAR &a)  { return a != b; }
inline bool operator==(const VAR::char_t *b, const VAR &a) { return a == b; }
inline bool operator!=(const VAR::char_t *b, const VAR &a) { return a != b; }
#define operator_primitives(T) \
inline bool operator==(const T &b, const VAR &a) { return a == b; } \
inline bool operator!=(const T &b, const VAR &a) { return a != b; } \
inline bool operator>=(const T &b, const VAR &a) { return a <  b; } \
inline bool operator<=(const T &b, const VAR &a) { return a >  b; } \
inline bool operator> (const T &b, const VAR &a) { return a <= b; } \
inline bool operator< (const T &b, const VAR &a) { return a >= b; }
operator_primitives(bool)
operator_primitives(char)
operator_primitives(unsigned char)
operator_primitives(short)
operator_primitives(unsigned short)
operator_primitives(int)
operator_primitives(unsigned int)
operator_primitives(long)
operator_primitives(unsigned long)
operator_primitives(float)
operator_primitives(double)
#undef operator_primitives
#undef VAR
}}
 
////////////////////////////////////////////////////////////////////////////////
 
namespace sw {
  typedef detail::basic_var<std::string, double, long> var;
}
 
#endif

Mehr Beispiele

More Examples

#include "test.hh"
#include <var.hh>
 
using namespace sw;
 
#define NaN std::numeric_limits<double>::quiet_NaN()
 
#define test_var_dmp(V) { \
  std::stringstream ss; \
  ss << "result dump: " << #V"=" << "('" << V.typen() << "') " << V << endl; \
  test_comment(ss.str()); \
}
 
#define test_var_expect_do_on_fail(COND, EXC) { if(!test_expect_cond(COND)) {EXC;}; };
 
#define test_var_assign_expect(TYPE, VAL, EXPECT) {     \
  var var_x0((TYPE)VAL);                                \
  var var_x1; var_x1 = ((TYPE)VAL);                     \
  if(!test_expect_cond((TYPE)var_x0 == EXPECT)) test_var_dmp(var_x0); \
  if(!test_expect_cond((TYPE)var_x1 == EXPECT)) test_var_dmp(var_x1); \
}
#define test_var_assign_t_eq(T, v) test_var_assign_expect(T, v, (T)v);
 
void test_assign_num(const std::deque<double> &test_values = std::deque<double>())
{
  test_comment("Assign numeric, read-back");
  std::deque<double> vals(test_values);
  if(vals.empty()) {
    vals.push_back(0);
    vals.push_back(1);
    vals.push_back(-1);
  }
  while(!vals.empty()) {
    double v = vals.front();
    vals.pop_front();
    test_var_assign_t_eq(short, v);
    test_var_assign_t_eq(int, v);
    test_var_assign_t_eq(long, v);
    test_var_assign_t_eq(double, v);
    test_var_assign_t_eq(float, v);
    if(v>0) {
      test_var_assign_t_eq(unsigned char, v);
      test_var_assign_t_eq(unsigned short, v);
      test_var_assign_t_eq(unsigned, v);
      test_var_assign_t_eq(unsigned long, v);
    }
  }
  test_comment("test_assign_num() done");
}
 
////////////////////////////////////////////////////////////////////////////////
 
void test_assign_readback_standards()
{
  test_comment("Assign, read-back of defined values");
  var a;
  test_expect(a.type() == var::tnull);
  test_expect((bool)a == false);
  test_expect((int)a == 0);
  test_expect((double)a == 0);
  test_expect((unsigned)a == 0);
  test_expect((char)a == 0);
  test_var_expect_do_on_fail((string)a == "null", std::cout << "===" << (string)a);
  a = true;
  test_expect(a.type() == var::tbool);
  test_expect((bool)a == true);
  test_expect((int)a == 1);
  test_expect((double)a == 1);
  test_expect((unsigned)a == 1);
  test_expect((char)a == 1);
  test_var_expect_do_on_fail((string)a == "true", std::cout << "===" << (string)a);
  a = false;
  test_expect(a.type()==var::tbool);
  test_expect((bool)a==false);
  test_expect((int)a==0);
  test_expect((double)a==0);
  test_expect((unsigned)a==0);
  test_expect((char)a==0);
  test_var_expect_do_on_fail((string)a == "false", std::cout << "===" << (string)a);
  a = (int)1;
  test_expect(a.type() == var::tint);
  test_expect((bool)a == true);
  test_expect((int)a == 1);
  test_expect((double)a == 1);
  test_expect((unsigned)a == 1);
  test_expect((char)a == 1);
  test_var_expect_do_on_fail((string)a == "1", std::cout << "===" << (string)a);
  a = 'A';
  test_expect( a.type() == var::tstr);
  test_expect( (bool)a == false);
  test_expect( (int)a == 0);
  test_expect( std::isnan((double)a));
  test_expect( (unsigned)a == 0);
  test_expect( (char) a == 'A');
  test_expect( (string)a == "A");
  a = "Hello";
  test_expect( a.type() == var::tstr);
  test_expect( (bool)a == false);
  test_expect( (int)a == 0);
  test_expect( std::isnan( (double)a ));
  test_expect( (unsigned)a==0);
  test_expect( (char)a == 'H');
  test_expect( (string)a == "Hello");
  test_comment("test_assign_readback_standards() done");
}
 
void test_operator_not()
{
  test_comment("operator!");
  var a;
  test_expect(  (!a));
  test_expect(  (!(a=0)));
  test_expect(  (!(a=false)));
  test_expect(  (!(a="")));
  test_expect(! (!(a=1)));
  test_expect(! (!(a=1.1)));
  test_expect(! (!(a=-1)));
  test_expect(! (!(a='A')));
  test_expect(! (!(a="Hello")));
  test_comment("test_operator_not() done");
}
 
void test_operator_eq()
{
  test_comment("operator==");
  var a;
  test_expect(  (a == (char)0));
  test_expect(  (a == (short)0));
  test_expect(  (a == (int)0));
  test_expect(  (a == (long)0));
  test_expect(  (a == (unsigned char)0));
  test_expect(  (a == (unsigned short)0));
  test_expect(  (a == (unsigned int)0));
  test_expect(  (a == (unsigned long)0));
  test_expect(  (a == (double)0)); // type null shall read back 0, not NAN
  test_expect(  (a == (float)0));
  test_expect(! (a == NaN)); // type null shall read back 0, not NAN
  test_expect(! (a == "null"));
  test_expect(! (a == "NULL"));
  var b;
  test_expect(   (b=0) == (a=0));
  test_expect(   (b=1) == (a=1));
  test_expect(   (b=1.0) == (a=1));
  test_expect(   (b=1.0) == (a=1.0));
  test_expect(   (b=1) == (a=1.0));
  test_expect(   (b="") == (a=""));
  test_expect(   (b="Hallo") == (a="Hallo"));
  test_expect(! ((b="A") == (a="")));
  test_expect(! ((b="") == (a="A")));
  test_expect(! ((b="") == (a=1)));
  test_expect(! ((b=1) == (a="Hello")));
  test_expect(! ((b=1.0) == (a="Hello")));
  test_expect(! ((b="Hello") == (a=1.0)));
  test_expect(! ((b=1) == (a=2.0)));
  test_expect(! ((b=2.0) == (a=1)));
  test_expect(  ((b="Hello") == (string("Hello"))));
  test_expect(0 == (a=0) );
  test_expect(1 == (a=1) );
  test_expect(!(1 == (a=0) ) );
  test_expect(!("A" == (a=1)) );
  test_expect(((a=1000) == "1000" ));
  test_expect(((a=5) == "5"));
  test_expect(((a=1e10) == "1e10"));
  test_comment("test_operator_eq() done");
}
 
void test_operator_ne()
{
  test_comment("operator!=");
  var a;
  test_expect(! (a != (char)0));
  test_expect(! (a != (short)0));
  test_expect(! (a != (int)0));
  test_expect(! (a != (long)0));
  test_expect(! (a != (unsigned char)0));
  test_expect(! (a != (unsigned short)0));
  test_expect(! (a != (unsigned int)0));
  test_expect(! (a != (unsigned long)0));
  test_expect(! (a != (double)0)); // type null shall read back 0, not NAN
  test_expect(! (a != (float)0));
  var b;
  test_expect(!( (b=0) != (a=0) ) );
  test_expect(!( (b=1) != (a=1) ) );
  test_expect(!( (b=1.0) != (a=1) ) );
  test_expect(!( (b=1.0) != (a=1.0) ) );
  test_expect(!( (b=1) != (a=1.0)) );
  test_expect(!( (b="") != (a="")) );
  test_expect(!( (b="Hallo") != (a="Hallo")) );
  test_expect( ((b="A") != (a="")));
  test_expect( ((b="") != (a="A")));
  test_expect( ((b="") != (a=1)));
  test_expect( ((b=1) != (a="Hello")));
  test_expect( ((b=1.0) != (a="Hello")));
  test_expect( ((b="Hello") != (a=1.0)));
  test_expect( ((b=1) != (a=2.0)));
  test_expect( ((b=2.0) != (a=1)));
  test_expect(! ((b="Hello") != (string("Hello"))));
  test_comment("test_operator_ne() done");
}
 
void test_primitive_operators()
{
  test_comment("operator <,>,<=,>=");
  var a;
  test_expect( (a <=  0) );
  test_expect( (a >=  0) );
  test_expect(!(a >   0) );
  test_expect(!(a <   0) );
  test_expect( (a <=  1) );
  test_expect(!(a >=  1) );
  test_expect(!(a >   1) );
  test_expect( (a <   1) );
  test_expect(!(a <= -1) );
  test_expect( (a >= -1) );
  test_expect( (a >  -1) );
  test_expect(!(a <  -1) );
  test_expect(!(a <= -1.1) );
  test_expect( (a >= -1.1) );
  test_expect( (a >  -1.1) );
  test_expect(!(a <  -1.1) );
  test_expect( (a <= 1.1) );
  test_expect(!(a >= 1.1) );
  test_expect(!(a >  1.1) );
  test_expect( (a <  1.1) );
  test_expect( (a <  "string"));
  test_expect(!(a >  "string"));
  test_expect(!(a <= "string"));
  test_expect(!(a >= "string"));
  test_expect( (a <= ""));
  test_expect( (a >= ""));
  test_expect(!(a <  ""));
  test_expect(!(a >  ""));
  test_expect( (a <= "100.1"));
  test_expect(!(a >= "100.1"));
  test_expect( (a <  "100.1"));
  test_expect(!(a >  "100.1"));
  a = "Hello";
  test_expect( (a >  "Gello"));
  test_expect( (a <  "Iello"));
  test_expect( (a >  "G"));
  test_expect( (a <  "I"));
  test_expect( (a >=  "Gello"));
  test_expect( (a <=  "Iello"));
  test_expect( (a >=  "G"));
  test_expect( (a <=  "I"));
  test_expect(!(a <  "Gello"));
  test_expect(!(a >  "Iello"));
  test_expect(!(a <  "G"));
  test_expect(!(a >  "I"));
  a = 5;
  test_expect( (a >  ""));
  test_expect(!(a <  ""));
  test_expect(!(a >  "Hello"));
  test_expect(!(a <  "Hello"));
  test_expect(!(a >= "Hello"));
  test_expect(!(a <= "Hello"));
  test_expect( (a >  "1.1"));
  test_expect(!(a <  "1.1"));
  test_expect( (a >= "1.1"));
  test_expect(!(a <= "1.1"));
  test_expect(!(a >  "10.1"));
  test_expect( (a <  "10.1"));
  test_expect(!(a >= "10.1"));
  test_expect( (a <= "10.1"));
  test_comment("test_primitive_operators() done");
}
 
void test_var_var()
{
  test_comment("var a <--> var b operations");
  var a, b;
  test_expect(!((a=0) == (b=1)));
  test_expect( (a != b));
  test_expect( (a <= b));
  test_expect(!(a >= b));
  test_expect( (a <  b));
  test_expect(!(a >  b));
  test_expect( !((a=0) == (b=1.1)) );
  test_expect(  (a != b) );
  test_expect(  (a <= b) );
  test_expect( !(a >= b) );
  test_expect(  (a <  b) );
  test_expect( !(a >  b) );
  test_expect(!((a=1) == (b=0)));
  test_expect( (a != b));
  test_expect(!(a <= b));
  test_expect( (a >= b));
  test_expect(!(a <  b));
  test_expect( (a >  b));
  test_expect(!((a=1.1) == (b=0)));
  test_expect( (a != b));
  test_expect(!(a <= b));
  test_expect( (a >= b));
  test_expect(!(a <  b));
  test_expect( (a >  b));
  test_expect(!((a=1.1) == (b=0.1)));
  test_expect( (a != b));
  test_expect(!(a <= b));
  test_expect( (a >= b));
  test_expect(!(a <  b));
  test_expect( (a >  b));
  test_expect( ((a=0) == (b=0)));
  test_expect(!(a != b));
  test_expect( (a <= b));
  test_expect( (a >= b));
  test_expect(!(a <  b));
  test_expect(!(a >  b));
  test_expect( ((a="") == (b="")));
  test_expect(!(a != b));
  test_expect( (a <= b));
  test_expect( (a >= b));
  test_expect(!(a <  b));
  test_expect(!(a >  b));
  test_expect( ((a="Hello") == (b="Hello")));
  test_expect(!(a != b));
  test_expect( (a <= b));
  test_expect( (a >= b));
  test_expect(!(a <  b));
  test_expect(!(a >  b));
  test_expect(!((a="A") == (b="B")));
  test_expect( (a != b));
  test_expect( (a <= b));
  test_expect(!(a >= b));
  test_expect( (a <  b));
  test_expect(!(a >  b));
  test_expect(!((a="") == (b="B")));
  test_expect( (a != b));
  test_expect( (a <= b));
  test_expect(!(a >= b));
  test_expect( (a <  b));
  test_expect(!(a >  b));
  test_expect(!((a="B") == (b="A")));
  test_expect( (a != b));
  test_expect(!(a <= b));
  test_expect( (a >= b));
  test_expect(!(a <  b));
  test_expect( (a >  b));
  test_expect(!((a="B") == (b="")));
  test_expect( (a != b));
  test_expect(!(a <= b));
  test_expect( (a >= b));
  test_expect(!(a <  b));
  test_expect( (a >  b));
  test_comment("test_var_var() done");
}
 
void test_statics()
{
  test_comment("Static class functions");
  test_expect( (var::undef().is_unset() ));
  test_expect(!(var::undef().is_null() ));
  test_expect(!(var::undef().is_bool() ));
  test_expect(!(var::undef().is_int() ));
  test_expect(!(var::undef().is_float() ));
  test_expect(!(var::undef().is_string() ));
  test_expect(!(var::undef().is_vector() ));
  test_expect(!(var::undef().is_map() ));
  test_expect( (var::undef_ref().is_unset() ));
  test_expect(!(var::undef_ref().is_bool() ));
  test_expect(!(var::undef_ref().is_null() ));
  test_expect(!(var::undef_ref().is_int() ));
  test_expect(!(var::undef_ref().is_float() ));
  test_expect(!(var::undef_ref().is_string() ));
  test_expect(!(var::undef_ref().is_vector() ));
  test_expect(!(var::undef_ref().is_map() ));
  test_expect( (var::nul().is_null() ));
  test_expect(!(var::nul().is_unset() ));
  test_expect(!(var::nul().is_bool() ));
  test_expect(!(var::nul().is_int() ));
  test_expect(!(var::nul().is_float() ));
  test_expect(!(var::nul().is_string() ));
  test_expect(!(var::nul().is_vector() ));
  test_expect(!(var::nul().is_map() ));
  test_comment("test_statics() done");
}
 
void test_string()
{
  test_comment("String features");
  var a, b;
  test_expect((a="A") == (b="A"));
  test_comment("test_string() done");
}
 
 
void test_vector()
{
  test_comment("Vector features");
  var::vect_t v;
  v.push_back(10);
  v.push_back("A");
  v.push_back(1.1);
  var a = v;
  test_expect(a.type()==var::tvect);
  test_expect(a.v().size() == 3);
  test_expect(a.size() == 3);
  test_expect(a.v(0) == 10);
  test_expect(a.v(1) == "A");
  test_expect(a.v(2) == 1.1);
  test_expect(a[0] == 10);
  test_expect(a[1] == "A");
  test_expect(a[2] == 1.1);
  test_expect(a[3].is_unset());
  test_expect(a[4].is_unset());
  test_expect(a.size() == 3);
  test_expect(a.insert(3, var::nul()).size() == 4);
  test_expect((a[3]=10) == 10);    // Assign existing a[3]
  test_expect((a[4]=100) == 100);  // Assign existing a[3]
  test_expect(a[-1].is_unset());
  test_expect((a=var::nul()).is_null());
  test_expect(a.push_back(10).size()==1);
  test_expect(a.push_back("A").size()==2);
  test_expect(a.pop_front().size()==1);
  test_expect(a.pop_front().size()==0);
  test_expect(a.pop_front().size()==0);
  test_expect(a.push_back(var::nul()).size()==1);
  test_expect(a.push_back(var::undef()).size()==1); // don't insert unset vars
  test_expect(a.push_back(1.1).size()==2);
  test_expect(a.erase(1).size()==1);
  test_expect(a.front().is_null());
  test_expect(a.erase(0).size()==0);
  test_expect(a.insert(0, 1234).front() == 1234);
  test_expect(a.insert(10,2345).back()  == 2345);
  test_expect(a.insert(0, 3456).front() == 3456);
  test_expect(a.insert(-1,4567).front() == 4567);
  a.insert(-1, var::undef());
  a.insert(-1, var::undef());
  a.insert(100, var::undef());
  a.insert(100, var::undef());
  a.insert(1, var::undef());
  a.insert(3, var::undef());
  a.insert(5, var::undef());
  a.insert(7, var::undef());
  test_expect(a.erase_undefs().size() == 4);
  a = var::empty_vector();
  a .push_back(var::undef())
    .push_back(var::undef())
    .push_back(1)
    .push_back(var::undef())
    .push_back(2)
    .push_back(3)
    .push_back(var::undef())
    .push_back(4)
    .push_back(var::undef())
    ;
  test_expect(a.erase_undefs().size() == 4);
  test_comment("test_vector() done");
}
 
void test_map()
{
  test_comment("Map features");
  var a;
  test_expect((a["test"]=10) == 10);
  test_expect((a["test"]=11) == 11);
  test_expect((a["test"]="A") == "A");
  test_expect( a["test"] == "A");
  test_expect( a.size() == 1);
  test_expect( a.m("test2", 10).size() == 2);
  test_expect( a.erase("test2").size() == 1);
  test_expect( a.erase("notinlist").size() == 1);
  test_expect( a["test10"].is_unset());   // as in std::map not found -> auto insert
  test_expect( a.size() == 2);
  test_expect( a["test11"].is_unset());   // as in std::map not found -> auto insert
  test_expect( a["test12"].is_unset());   // as in std::map not found -> auto insert
  test_expect( a.size() == 4);
  test_expect( a.erase_undefs().size() == 1);
  test_comment("test_map() done");
}
 
void test_nested()
{
  var a;
  a["vector1"] = var::empty_vector();
  a["vector1"].push_back(1);
  a["vector1"].push_back(2);
  a["vector1"].push_back(3);
  a["vector1"].push_back(4);
  a["vector1"].push_back(var());
  a["vector1"][4]["nested-map-val-1"] = "Hallo";
  a["vector1"][4]["nested-map-val-2"] = 1.1;
  a["vector1"][4]["nested-map-val-3"] = 1100;
  std::stringstream ss;
  a.dump(ss);
  test_comment(ss.str());
  test_comment("test_nested() done");
}
 
 
void test()
{
  test_statics();
  test_assign_num();
  test_assign_readback_standards();
  test_operator_not();
  test_operator_eq();
  test_operator_ne();
  test_primitive_operators();
  test_var_var();
  test_string();
  test_vector();
  test_map();
  test_nested();
  test_comment("test() done");
}