Core/Misc: update g3dlite lib (#2904)

* Core/Misc: update g3dlite lib

* update

Co-authored-by: Francesco Borzì <borzifrancesco@gmail.com>
This commit is contained in:
Viste
2020-07-30 13:35:45 +03:00
committed by GitHub
parent 91bbbf08eb
commit fcaf91b8b2
183 changed files with 13258 additions and 8022 deletions
+419 -155
View File
@@ -1,13 +1,13 @@
/**
@file Any.h
\file Any.h
@author Morgan McGuire, Shawn Yarbrough, and Corey Taylor
@maintainer Morgan McGuire
\author Morgan McGuire, Shawn Yarbrough, and Corey Taylor
\maintainer Morgan McGuire
@created 2006-06-11
@edited 2010-03-16
\created 2006-06-11
\edited 2013-03-29
Copyright 2000-2010, Morgan McGuire.
Copyright 2000-2013, Morgan McGuire.
All rights reserved.
*/
@@ -17,13 +17,13 @@
#include "G3D/platform.h"
#include "G3D/Table.h"
#include "G3D/Array.h"
#include "G3D/Set.h"
#include "G3D/AtomicInt32.h"
#include "G3D/stringutils.h"
#include <string>
// needed for Token
#include "G3D/TextInput.h"
#include "G3D/TextOutput.h"
#ifdef verify
#undef verify
@@ -38,7 +38,8 @@ class TextOutput;
Any encodes typed, structured data and can serialize it to a human
readable format that is very similar to the Python language's data
syntax. It is well-suited for quickly creating human-readable file
syntax, and fully supports Python's data syntax as well.
It is well-suited for quickly creating human-readable file
formats, especially since deserialization and serialization preserve
comments and an Any can tell you what file and line it came from. The
syntax allows most C++ editors to properly highlight Any files, and
@@ -61,7 +62,7 @@ Sample File:
position = Vector3(1.0, -1.0, 0.0),
video = { format = "RGB8", size = (320, 200)},
material = #include("rocks.mat")
material = \#include("rocks.mat")
}
</pre>
@@ -79,7 +80,8 @@ The custom serialization format was chosen to be terse, easy for
humans to read, and easy for machines to parse. It was specifically
chosen over formats like XML, YAML, JSON, S-expressions, and Protocol
Buffers, although there is no reason you could not write readers and
writers for G3D::Any that support those.
writers for G3D::Any that support those. Any also currently supports
the JSON format.
G3D::Any assumes that structures do not contain cycles; it is an
error to create a structure like:
@@ -91,6 +93,17 @@ x.array().append(x); // don't do this!
although no exception will be thrown at runtime during that append.
\section includes
When parsing an Any from a file, the syntax
<code>\#include(<i>filename</i>)</code> allows subsitution of the contents of
<i>filename</i> for any single expression in an Any. The filename is interpreted
relative to the file being parsed, and inside of the included file, relative filenames
are interpreted with respect to the included file (and so on, recursively for nested
inclusion).
Filenames are resolved with System::resolve and then System::findDataFile if not found,
so they may contain environment variables.
\section Parsing
@@ -117,24 +130,12 @@ Vector3::Vector3(const Any& any) {
}
</pre>
It is often convenient to iterate through the table portion:
<pre>
for (Any::AnyTable::Iterator it = any.table().begin(); it.hasMore(); ++it) {
const std::string& k = toLower(it->key);
if (key == "hello") {
...
} else if (key == "goodbye") {
...
} else {
any.verify(false, "Unsupported key: " + it->key);
}
}
</pre>
It is often convenient to iterate through the table portion using G3D::AnyTableReader.
\section BNF
Serialized format BNF:
\htmlonly
<pre>
identifier ::= (letter | "_") (letter | digit | "_")*
identifier-op ::= "::" | "->" | "."
@@ -145,18 +146,19 @@ comment ::= C++ single or multi-line comments
separator ::= "," | ";"
number ::= <legal C printf number format>
string ::= <legal C double-quoted string; backslashes must be escaped>
string ::= <legal C double-quoted or unquoted string; backslashes must be escaped>
boolean ::= "True" | "False"
none ::= "None"
array ::= ("(" | "[") [ value (separator value)* [separator] ] (")" | "]")
pair ::= (identifier | string) "=" value
table ::= "{" [ pair (separator pair)* [separator] ] "}"
nil ::= "Nil" <not case sensitive> | "None" <case sensitive>
array ::= ("(" | "[" | "{") [value (separator value)* [separator] ] (")" | "]" | "}")
pair ::= (identifier | string) ("=" | ":") value
table ::= ("(" | "[" | "{") [ pair (separator pair)* [separator] ] (")" | "]" | "}")
named-array ::= identifier-exp array
named-table ::= identifier-exp table
include ::= "#" "include" "(" string ")"
value ::= [comment] (none | number | boolean | string | array | table | named-array | named-table | include)
value ::= [comment] (nil | number | boolean | string | array | table | named-array | named-table | include)
</pre>
\endhtmlonly
Except for single-line comments, whitespace is not significant.
All parsing is case-insensitive.
@@ -167,7 +169,7 @@ can only appear in the locations where a value is expected. This means
that it cannot yield more than one element of an array and cannot serve
as the pair in a table.
The deserializer allows the substitution of [] for () when writing
The deserializer allows the substitution of [] or {} for () when writing
tuples and ";" for ",". These are convenient when mimicing a
programming language, e.g., <code>"[ printf("hello world."); clearScreen();]"</code>
parses as an array containing two named arrays within it. The
@@ -176,11 +178,13 @@ which also convenient when commenting out the last element.
The serializer indents four spaces for each level of nesting.
Tables are written with the keys in alphabetic order.
\sa G3D::AnyTableReader
*/
class Any {
public:
enum Type {NONE, BOOLEAN, NUMBER, STRING, ARRAY, TABLE};
enum Type {NIL, BOOLEAN, NUMBER, STRING, ARRAY, TABLE, EMPTY_CONTAINER};
static std::string toString(Type t);
@@ -208,7 +212,7 @@ private:
/** Called from deserialize() */
static void deserializeComment(TextInput& ti, Token& token, std::string& comment);
/** NONE, BOOLEAN, and NUMBER are stored directly in the Any */
/** NIL, BOOLEAN, and NUMBER are stored directly in the Any */
union SimpleValue {
bool b;
double n;
@@ -218,6 +222,11 @@ private:
inline SimpleValue(double x) : n(x) {}
};
/** The three options for Data::bracket */
static const char* PAREN;
static const char* BRACKET;
static const char* BRACE;
class Data {
public:
/** ARRAY, TABLE, or STRING value only. NULL otherwise. */
@@ -240,6 +249,16 @@ private:
std::string name;
/** If this Any was created by parsing an #include expression and
has not been modified since, this is the original comment and
include statement, as
it originally appeared in the file (e.g., it may contain a relative
filename). If this is non-empty, then when serialized, this
Any will turn into an #include expression instead of unparsing
its contents of the any.
*/
std::string includeLine;
/** For STRING, ARRAY and TABLE types, m_value is shared between
multiple instances. Mutation is allowed only if the reference
count is exactly 1, otherwise the mutating instance must copy
@@ -249,10 +268,16 @@ private:
Source source;
/** Two-character string of "{}", "[]", or "()"; to be used when unparsing. */
const char* bracket;
/** ';' or ',' separator to be used when unparsing */
char separator;
private:
/** Called by create() */
inline Data(Type t) : type(t), referenceCount(1) {}
inline Data(Type t, const char* b, char s) : type(t), referenceCount(1), bracket(b), separator(s) {}
/** Called by destroy */
~Data();
@@ -261,12 +286,11 @@ private:
/** Clones the argument */
static Data* create(const Data* d);
static Data* create(Type t);
static Data* create(Type t, const char* b = NULL, char s = '\0');
/** Free d, invoking its destructor and freeing the memory for
the value. */
static void destroy(Data* d);
};
/** If not empty, this Any was created from operator[] on a table
@@ -283,12 +307,11 @@ private:
SimpleValue m_simpleValue;
mutable Data* m_data;
/** Called before every read operation to ensure that this object
is not a placeholder. */
/** Called before every read operation. */
void beforeRead() const;
/** Called before every write operation to wipe the placeholder
status. */
/** Called before every write operation to this Any. Wipes the placeholder
status and includedFrom entry. */
void beforeWrite();
/** Decrements the reference count (if there is one). If the
@@ -316,43 +339,61 @@ private:
/** Read the name of a named Array or Table. */
static void deserializeName(TextInput& ti, Token& token, std::string& name);
/** Read until a comma is consumed or a close paren is hit, and
/** Read until a separator is consumed or a close paren is hit, and
return that token. Considers the passed in token to be the first
value read. */
static void readUntilCommaOrClose(TextInput& ti, Token& token);
static void readUntilSeparatorOrClose(TextInput& ti, Token& token);
/** Construct an Any that is a proxy for a table fetch from \a data.
This proxy can be copied exactly once on return from operator[].*/
Any(const std::string& key, Data* data);
inline bool isPlaceholder() const {
bool isPlaceholder() const {
return ! m_placeholderName.empty();
}
void _append(const Any& v0);
void _append(const Any& v0, const Any& v1);
void _append(const Any& v0, const Any& v1, const Any& v2);
void _append(const Any& v0, const Any& v1, const Any& v2, const Any& v3);
Any _get(const std::string& key, const Any& defaultVal) const;
void _set(const std::string& key, const Any& val);
void _parse(const std::string& src);
public:
/** Base class for all Any exceptions.*/
class Exception {
public:
virtual ~Exception() {}
};
/** Thrown by operator[] when a key is not present in a const table. */
class KeyNotFound : public ParseError {
public:
std::string key;
KeyNotFound(const Data* data) {
if (data) {
filename = data->source.filename;
line = data->source.line;
character = data->source.character;
}
}
};
/** Thrown by operator[] when an array index is not present. */
class IndexOutOfBounds : public Exception {
class IndexOutOfBounds : public ParseError {
public:
int index;
int size;
inline IndexOutOfBounds() : index(0), size(0) {}
inline IndexOutOfBounds(int i, int s) : index(i), size(s) {}
IndexOutOfBounds() : index(0), size(0) {}
IndexOutOfBounds(const Data* data, int i, int s) : index(i), size(s) {
if (data) {
filename = data->source.filename;
line = data->source.line;
character = data->source.character;
}
message = format("Index out of bounds: index = %d, array size = %d", i, s);
}
};
/** NONE constructor */
/** NIL constructor */
Any();
/** Deserialize */
@@ -361,108 +402,133 @@ public:
Any(const Any& x);
/** NUMBER constructor */
Any(double x);
explicit Any(double x);
#ifdef G3D_32BIT
/** NUMBER constructor */
Any(int64 x);
#endif // G3D_32BIT
explicit Any(float x);
#if 0
#if defined(G3D_32Bit) || defined(_MSC_VER)
/** NUMBER constructor */
Any(int32 x);
#endif // 0
explicit Any(int64 x) : m_type(NUMBER), m_simpleValue((double)x), m_data(NULL) {}
#endif
/** NUMBER constructor */
Any(long x);
explicit Any(long x);
/** NUMBER constructor */
Any(int x);
explicit Any(int x);
/** NUMBER constructor */
Any(short x);
explicit Any(char x);
/** NUMBER constructor */
explicit Any(short x);
/** BOOLEAN constructor */
Any(bool x);
explicit Any(bool x);
/** STRING constructor */
Any(const std::string& x);
explicit Any(const std::string& x);
/** STRING constructor */
Any(const char* x);
explicit Any(const char* x);
/** \a t must be ARRAY or TABLE
\param brackets must be "" (defaults to {} for table, () for array), "[]", "()", or "{}"
\param separator must be ';', ',', or '\0' (defaults to ',' for array and ';' for table)
*/
explicit Any(Type t, const std::string& name = "", const std::string& brackets = "", const char separator = '\0');
/** Extensible constructor: call the toAny() method of any class. */
template<class T>
explicit Any(const T& v) : m_type(NIL), m_data(NULL) {
*this = v.toAny();
}
/** \a t must be ARRAY or TABLE */
Any(Type t, const std::string& name = "");
~Any();
/** Removes the comment and name */
Any& operator=(const Any& x);
/** Removes the comment and name */
Any& operator=(double x);
/** Removes the comment and name */
Any& operator=(int x);
/** Removes the comment and name */
Any& operator=(bool x);
/** Removes the comment and name */
Any& operator=(const std::string& x);
/** Removes the comment and name */
Any& operator=(const char* x);
/** \a t must be ARRAY, TABLE, or NONE. Removes the comment and name */
/** \a t must be ARRAY, TABLE, or NIL. Removes the comment and name */
Any& operator=(Type t);
/** Assigns from an array. Assumes that T can be converted to Any. Removes the comment and name */
template<class T>
Any& operator=(const Array<T>& array) {
*this = Any::ARRAY;
resize(array.size());
for (int i = 0; i < array.size(); ++i) {
this->operator [](i) = array[i];
}
return *this;
}
/** Removes the comment and name */
template<class T>
Any& operator=(const T& v) {
*this = Any(v);
return *this;
}
Type type() const;
/** Same as deserialize or load, but operates on a string instead
of a stream or file.
\sa deserialize, load
\sa deserialize, load, unparse
*/
void parse(const std::string& src);
static Any parse(const std::string& src);
std::string unparse() const;
std::string unparse(const TextOutput::Settings& s = TextOutput::Settings()) const;
/** \param allowCoercion If false, throws an error if the Any uses features that are not
supported by JSON such as named arrays. Otherwise, silently coerces to JSON. */
std::string unparseJSON(const TextOutput::Settings& s = TextOutput::Settings(), bool allowCoercion = true) const;
/** Comments appear before values when they are in serialized form.*/
const std::string& comment() const;
void setComment(const std::string& c);
/** True if this is the NONE value */
bool isNone() const;
/** True if this is the NIL value */
bool isNil() const;
/** Throws a ParseError exception if this is not a number */
double number() const;
float floatValue() const;
const std::string& string() const;
bool boolean() const;
/** If a valid string, takes the string value and creates a fully qualified filename.
If not found, the returned string is empty.
/** If a valid string, takes the string value and creates a fully
qualified filename.
The file is searched for the following ways:
- In the directory from which the Any was loaded.
- By calling System::findDataFile as you would with other data files.
Strings that begin with '<' and end with '>' are treated as
escape sequences and are returned unmodifed.
*/
std::string resolveStringAsFilename() const;
std::string resolveStringAsFilename(bool errorIfNotFound = true) const;
/** If this is named ARRAY or TABLE, returns the name. */
const std::string& name() const;
/** If this is named ARRAY or TABLE, returns true if the name begins with \a s. The comparision is case insensitive. */
/** If this is named ARRAY or TABLE, returns true if the name
begins with \a s. The comparision is case insensitive. */
bool nameBeginsWith(const std::string& s) const;
/** If this is named ARRAY or TABLE, returns true if the name begins with \a s. The comparision is case insensitive. */
/** If this is named ARRAY or TABLE, returns true if the name
begins with \a s. The comparision is case insensitive. */
bool nameBeginsWith(const char* s) const;
/** If this is named ARRAY or TABLE, returns true if the name is \a s. The comparision is case insensitive. */
/** If this is named ARRAY or TABLE, returns true if the name is
\a s. The comparision is case insensitive. */
bool nameEquals(const std::string& s) const;
/** If this is named ARRAY or TABLE, returns true if the name is\a s. The comparision is case insensitive. */
/** If this is named ARRAY or TABLE, returns true if the name is\a
s. The comparision is case insensitive. */
bool nameEquals(const char* s) const;
/** \brief Set the name used when serializing an ARRAY or TABLE.
@@ -504,12 +570,29 @@ public:
/** Directly exposes the underlying data structure for an ARRAY. */
const Array<Any>& array() const;
void append(const Any& v0);
void append(const Any& v0, const Any& v1);
void append(const Any& v0, const Any& v1, const Any& v2);
void append(const Any& v0, const Any& v1, const Any& v2, const Any& v3);
/** Directly exposes the underlying data structure for table.*/
template<class T0>
void append(const T0& v0) {
_append(Any(v0));
}
template<class T0, class T1>
void append(const T0& v0, const T1& v1) {
_append(Any(v0), Any(v1));
}
template<class T0, class T1, class T2>
void append(const T0& v0, const T1& v1, const T2& v2) {
_append(Any(v0), Any(v1), Any(v2));
}
template<class T0, class T1, class T2, class T3>
void append(const T0& v0, const T1& v1, const T2& v2, const T3& v3) {
_append(Any(v0), Any(v1), Any(v2), Any(v3));
}
/** Directly exposes the underlying data structure for table.
\sa G3D::AnyTableReader*/
const Table<std::string, Any>& table() const;
/** For a table, returns the element for \a key. Throws KeyNotFound
@@ -549,19 +632,25 @@ public:
Any& operator[](const std::string& key);
/** \copydoc Any::operator[](const std::string&) */
Any& operator[](const char* key) {
inline Any& operator[](const char* key) {
return operator[](std::string(key));
}
/** For a table, returns the element for key \a x and \a
defaultVal if it does not exist. */
const Any& get(const std::string& key, const Any& defaultVal) const;
template<class T>
Any get(const std::string& key, const T& defaultVal) const {
return _get(key, Any(defaultVal));
}
/** Returns true if this key is in the TABLE. Illegal to call on an object that is not a TABLE. */
bool containsKey(const std::string& key) const;
/** For a table, assigns the element for key k. */
void set(const std::string& key, const Any& val);
template<class T>
void set(const std::string& key, const T& val) {
_set(key, Any(val));
}
/** for an ARRAY, resizes and returns the last element */
Any& next();
@@ -573,14 +662,64 @@ public:
/** True if the Anys are exactly equal, ignoring comments. Applies deeply on arrays and tables. */
bool operator==(const Any& x) const;
bool operator==(const std::string& s) const {
return *this == Any(s);
}
bool operator==(const double& v) const {
return *this == Any(v);
}
bool operator==(int v) const {
return *this == Any(v);
}
bool operator==(bool v) const {
return *this == Any(v);
}
bool operator!=(const Any& x) const;
bool operator!=(const std::string& s) const {
return *this != Any(s);
}
bool operator!=(const double& v) const {
return *this != Any(v);
}
bool operator!=(int v) const {
return *this != Any(v);
}
bool operator!=(bool v) const {
return *this != Any(v);
}
operator int() const;
operator uint32() const;
operator float() const;
operator double() const;
operator bool() const;
operator std::string() const;
operator char() const {
return char(int(*this));
}
operator uint8() const {
return uint8(int(*this));
}
operator int16() const {
return int16(int(*this));
}
operator uint16() const {
return uint16(int(*this));
}
/** Resize to \a n elements, where new elements are NIL
It is an error to call this method if this is not an Any::ARRAY */
void resize(int n);
@@ -591,33 +730,88 @@ public:
void clear();
/** Parse from a file.
\sa deserialize, parse */
\sa deserialize, parse, fromFile, loadIfExists
*/
void load(const std::string& filename);
/** Load a new Any from \a filename. \sa load, save, loadIfExists */
static Any fromFile(const std::string& filename);
/** Load \a filename file if it exists, otherwise do not modify this */
void loadIfExists(const std::string& filename);
/** Uses the serialize method. */
void save(const std::string& filename) const;
void serialize(TextOutput& to) const;
void serialize(TextOutput& to, bool json = false, bool coerce = false) const;
void serialize(class BinaryOutput& b) const;
/** Parse from a stream.
\sa load, parse */
void deserialize(TextInput& ti);
void deserialize(class BinaryInput& b);
const Source& source() const;
/** Removes this key from the Any, which must be a table. */
void remove(const std::string& key);
/** Removes this key from the Any, which must be an array, and
shifts other elements down to maintain order. */
void remove(int index);
/** Throws a ParseError if \a value is false. Useful for quickly
creating parse rules in classes that deserialize from Any.
*/
void verify(bool value, const std::string& message = "") const;
/** Verifies that the name <i>begins with</i> identifier \a n (case insensitive).
/** Verifies that the name is identifier \a n (case sensitive).
It may contain identifier operators after this */
void verifyName(const std::string& n) const;
/** Verifies that the name <i>begins with</i> identifier \a n or \a m (case insensitive).
/** Verifies that the name is identifier \a n or \a m (case sensitive).
It may contain identifier operators after this */
void verifyName(const std::string& n, const std::string& m) const;
/** Verifies that the name is identifier \a n or \a m or \a p (case sensitive).
It may contain identifier operators after this */
void verifyName(const std::string& n, const std::string& m, const std::string& p) const;
/** Verifies that the name is identifier \a n or \a m or \a p or \a q (case sensitive).
It may contain identifier operators after this */
void verifyName(const std::string& n, const std::string& m, const std::string& p, const std::string& q) const;
/** Verifies that the name <i>begins with</i> identifier \a n (case sensitive).
It may contain identifier operators after this */
void verifyNameBeginsWith(const std::string& n) const;
/** Verifies that the name <i>begins with</i> identifier \a n or \a m (case sensitive).
It may contain identifier operators after this */
void verifyNameBeginsWith(const std::string& n, const std::string& m) const;
/** Verifies that the name <i>begins with</i> identifier \a n or \a m or \a p (case sensitive).
It may contain identifier operators after this */
void verifyNameBeginsWith(const std::string& n, const std::string& m, const std::string& p) const;
/** Verifies that the name <i>begins with</i> identifier \a n or \a m or \a p or \a q (case sensitive).
It may contain identifier operators after this */
void verifyNameBeginsWith(const std::string& n, const std::string& m, const std::string& p, const std::string& q) const;
/** Verifies that the name <i>begins with</i> identifier \a n or \a m or \a p or \a q or \a r (case sensitive).
It may contain identifier operators after this */
void verifyNameBeginsWith(const std::string& n, const std::string& m, const std::string& p, const std::string& q, const std::string& r) const;
/** Verifies that the name <i>begins with</i> identifier \a n or \a m or \a p or \a q or \a r or \a s (case sensitive).
It may contain identifier operators after this */
void verifyNameBeginsWith(const std::string& n, const std::string& m, const std::string& p, const std::string& q, const std::string& r, const std::string& s) const;
/** Verifies that the name <i>begins with</i> identifier \a n or \a m or \a p or \a q or \a r or \a s or \a t(case sensitive).
It may contain identifier operators after this */
void verifyNameBeginsWith(const std::string& n, const std::string& m, const std::string& p, const std::string& q, const std::string& r, const std::string& s, const std::string& t) const;
/** Verifies that the type is \a t. */
void verifyType(Type t) const;
@@ -630,14 +824,49 @@ public:
/** Verifies that the size is exactly \a s */
void verifySize(int s) const;
/** Assumes that Any(T) is well-defined, e.g., by T defining a
T::toAny() method. */
template<class T>
explicit Any(const Array<T>& array, const std::string& name = "") : m_type(ARRAY), m_data(NULL) {
setName(name);
resize(array.size());
for (int i = 0; i < array.size(); ++i) {
(*this)[i] = Any(array[i]);
}
}
/** Assumes that T defines T(const Any&) */
template<class T>
void getArray(Array<T>& array) const {
verifyType(ARRAY);
array.resize(size());
for (int i = 0; i < array.size(); ++i) {
array[i] = T((*this)[i]);
}
}
/** Assumes that T defines T(const Any&) */
template<class T>
void getTable(Table<std::string, T>& table) const {
verifyType(TABLE);
for (Table<std::string, Any>::Iterator it = this->table().begin(); it.isValid(); ++it) {
table.set(it.key(), T(it.value()));
}
}
private:
void deserializeTable(TextInput& ti);
void deserializeArray(TextInput& ti,const std::string& term);
/** Turns an empty container into a table or an array */
void become(const Type& t);
}; // class Any
/**
Convenient iteration over the keys of a Any::TABLE, usually
for implementing construction of an object from an Any.
@@ -647,14 +876,14 @@ private:
It is an error to consume the same element more than once from
the same iterator.
<pre>
AnyKeyIterator r(a);
r.getIfPresent("enabled", enabled);
r.getIfPresent("showSamples", showSamples);
r.getIfPresent("showTiles", showTiles);
\code
AnyTableReader r(a);
r.getIfPresent("enabled", enabled);
r.getIfPresent("showSamples", showSamples);
r.get("showTiles", showTiles);
r.verifyDone();
</pre>
\endcode
\beta
*/
@@ -665,44 +894,21 @@ private:
public:
/** Verifies that \a is a TABLE with the given \a name. */
AnyTableReader(const std::string& name, const Any& a) : m_any(a) {
try {
m_any.verifyType(Any::TABLE);
m_any.verifyName(name);
} catch (const ParseError& e) {
// If an exception is thrown, the destructors will not be
// invoked automatically.
m_any.~Any();
m_alreadyRead.~Set();
throw e;
}
}
AnyTableReader(const std::string& name, const Any& a);
/** Verifies that \a is a TABLE. */
AnyTableReader(const Any& a) : m_any(a) {
try {
m_any.verifyType(Any::TABLE);
} catch (const ParseError& e) {
// If an exception is thrown, the destructors will not be
// invoked automatically.
m_any.~Any();
m_alreadyRead.~Set();
throw e;
}
}
AnyTableReader(const Any& a);
bool hasMore() const {
return m_any.size() > m_alreadyRead.size();
}
/** Verifies that all keys have been read. */
void verifyDone() const {
if (hasMore()) {
// Generate all keys
// Remove the ones we've read
// Assert the rest
// any.verify("");
}
void verifyDone() const;
/** Return the underlying Any. */
const Any& any() const {
return m_any;
}
#if 0
@@ -714,30 +920,67 @@ public:
AnyKeyIterator& operator++();
#endif
/** \copydoc get(const std::string& s, ValueType& v) */
void get(const std::string& s, std::string& v) {
v = m_any[s].string();
m_alreadyRead.insert(s);
}
/** \copydoc get(const std::string& s, ValueType& v) */
void get(const std::string& s, uint8& v) {
v = uint8(m_any[s].number());
m_alreadyRead.insert(s);
}
/** \copydoc get(const std::string& s, ValueType& v) */
void get(const std::string& s, uint16& v) {
v = uint16(m_any[s].number());
m_alreadyRead.insert(s);
}
/** Read an entire array at once.*/
template<class T>
void get(const std::string& s, Array<T>& v) {
m_any[s].getArray(v);
m_alreadyRead.insert(s);
}
/** If key \s appears in the any, reads its value into \a v and
template<class T>
void get(const std::string& s, Table<std::string, T>& v) {
m_any[s].getTable(v);
m_alreadyRead.insert(s);
}
/** If key \a s appears in the any, reads its value into \a v and
removes that key from the ones available to iterate over.
If key \s does not appear in the any, throws a G3D::ParseError.
If key \a s does not appear in the any, throws a G3D::ParseError.
Assumes that if key \s appears in the any it has not already been extracted
Assumes that if key \a s appears in the any it has not already been extracted
by this iterator. If it has been read before, an assertion will fail in debug mode.
*/
template<class ValueType>
void get(const std::string& s, ValueType& v) {
v = m_any[s];
m_alreadyRead.insert(toLower(s));
v = ValueType(m_any[s]);
m_alreadyRead.insert(s);
}
/** Same as get() */
const Any& operator[](const std::string& s) {
m_alreadyRead.insert(s);
return m_any[s];
}
/** Get the value associated with a key only if the key is actually present.
If key \s appears in the any, reads its value into \a v and
If key \a s appears in the any, reads its value into \a v and
removes that key from the ones available to iterate over.
If key \s does not appear in the any, does nothing.
If key \a s does not appear in the any, does nothing.
Assumes that if key \s appears in the any it has not already been extracted
Assumes that if key \a s appears in the any it has not already been extracted
by this iterator. If it has been read before, an assertion will fail in debug mode.
\return True if the value was read.
@@ -745,7 +988,7 @@ public:
template<class ValueType>
bool getIfPresent(const std::string& s, ValueType& v) {
if (m_any.containsKey(s)) {
debugAssertM(! m_alreadyRead.contains(toLower(s)), "read twice");
debugAssertM(! m_alreadyRead.contains(s), "read twice");
get(s, v);
return true;
@@ -753,8 +996,29 @@ public:
return false;
}
}
/** \return True if \a s is in the table and has not yet been read
using get() or getIfPresent(). */
bool containsUnread(const std::string& s) const {
return m_any.containsKey(s) && ! m_alreadyRead.contains(s);
}
};
} // namespace G3D
/**
\def PARSE_ANY(expression)
\brief Create an G3D::Any from an unquoted string.
e.g.,
\code
Any x = PARSE_ANY( { a = 3.0; b = false; } );
\endcode
*/
#define PARSE_ANY(x) Any::parse(#x)
#endif