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
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");
}


