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