513 lines
16 KiB
C++
513 lines
16 KiB
C++
/**
|
|
@file AnyVal.h
|
|
@author Morgan McGuire
|
|
@created 2006-06-11
|
|
@edited 2008-07-14
|
|
*/
|
|
|
|
#ifndef G3D_ANYVAL_H
|
|
#define G3D_ANYVAL_H
|
|
|
|
#include "G3D/platform.h"
|
|
#include <string>
|
|
#include "G3D/Array.h"
|
|
#include "G3D/TextInput.h"
|
|
|
|
namespace G3D {
|
|
// Forward declarations for G3D types
|
|
class Vector2;
|
|
class Vector3;
|
|
class Vector4;
|
|
class Color1;
|
|
class Color3;
|
|
class Color4;
|
|
class Quat;
|
|
class Matrix2;
|
|
class Matrix3;
|
|
class Matrix4;
|
|
class CoordinateFrame;
|
|
class TextInput;
|
|
class TextOutput;
|
|
class BinaryInput;
|
|
class BinaryOutput;
|
|
class Rect2D;
|
|
class AABox;
|
|
|
|
/**
|
|
\deprecated
|
|
<b>Use the G3D::Any class instead. This is only provided for
|
|
backwards compatibility to G3D 7.xx.</b>
|
|
|
|
A generic value, useful for defining property trees that can
|
|
be loaded from and saved to disk. The values are intentionally
|
|
restricted to a small set.
|
|
|
|
When written to files, the syntax is as follows. Note that you can
|
|
nest arrays and tables in order to create full tree (i.e., XML-like)
|
|
structures as configuration files:
|
|
|
|
<table>
|
|
<tr><td>NULL</td><td><code>Nil</code></td></tr>
|
|
<tr><td>double</td><td><i>The number in printf double format</i></td></tr>
|
|
<tr><td>bool</td><td><code>true</code> <i>or</i> <code>false</code></td></tr>
|
|
<tr><td>std::string</td><td><i>The string in double-quotes (</i><code>"</code><i>)</i></td></tr>
|
|
<tr><td>Rect2D</td><td><code>R(</code><i>x<sub>0</sub></i><code>,</code><i>y<sub>0</sub></i><code>,</code><i>x<sub>1</sub></i><code>,</code><i>y<sub>1</sub></i><code>)</code></td></tr>
|
|
<tr><td>Color1</td><td><code>C1(</code><i>value</i><code>)</code></td></tr>
|
|
<tr><td>Color3</td><td><code>C3(</code><i>r</i><code>,</code><i>g</i><code>,</code><i>b</i><code>)</code></td></tr>
|
|
<tr><td>Color4</td><td><code>C4(</code><i>r</i><code>,</code><i>g</i><code>,</code><i>b</i><code>,</code><i>a</i><code>)</code></td></tr>
|
|
<tr><td>Vector2</td><td><code>V2(</code><i>x</i><code>,</code><i>y</i><code>)</code></td></tr>
|
|
<tr><td>Vector3</td><td><code>V3(</code><i>x</i><code>,</code><i>y</i><code>,</code><i>z</i><code>)</code></td></tr>
|
|
<tr><td>Vector4</td><td><code>V4(</code><i>x</i><code>,</code><i>y</i><code>,</code><i>z</i><code>,</code><i>w</i><code>)</code></td></tr>
|
|
<tr><td>Quat</td><td><code>V(</code>x<code>,</code>y<code>,</code>z<code>,</code>w<code>)</code></td></tr>
|
|
<tr><td>AABox</td><td><code>AAB(</code>low Vector3<code>, </code>high Vector3<code>)</code></td></tr>
|
|
<tr><td>Matrix2</td><td><code>M2(</code>r0c0<code>, </code>r0c1<code>,
|
|
<br> </code>r1c0<code>, </code>r1c1<code>)</code></td></tr>
|
|
<tr><td>Matrix3</td><td><code>M3(</code>r0c0<code>, </code>r0c1<code>, </code>r0c2<code>,
|
|
<br> </code>r1c0<code>, </code>r1c1<code>, </code>r1c2<code>,
|
|
<br> </code>r2c0<code>, </code>r2c1<code>, </code>r2c2<code>)</code></td></tr>
|
|
<tr><td>Matrix4</td><td><code>M4(</code>r0c0<code>, </code>r0c1<code>, </code>r0c2<code>, </code>r0c3<code>,
|
|
<br> </code>r1c0<code>, </code>r1c1<code>, </code>r1c2<code>, </code>r1c3<code>,
|
|
<br> </code>r2c0<code>, </code>r2c1<code>, </code>r2c2<code>, </code>r2c3<code>,
|
|
<br> </code>r3c0<code>, </code>r3c1<code>, </code>r3c2<code>, </code>r3c3<code>)</code></td></tr>
|
|
<tr><td>CoordinateFrame</td><td><code>CF(</code>r0c0<code>, </code>r0c1<code>, </code>r0c2<code>, </code>r0c3<code>,
|
|
<br> </code>r1c0<code>, </code>r1c1<code>, </code>r1c2<code>, </code>r1c3<code>,
|
|
<br> </code>r2c0<code>, </code>r2c1<code>, </code>r2c2<code>, </code>r2c3<code>)</code></td></tr>
|
|
<tr><td>CoordinateFrame</td><td><code>CF(V3(</code><i>x</i><code>, </code><i>y</i><code>, </code><i>z</i><code>), </code><i>yaw deg</i><code>, </code><i>pitch deg</i><code>, </code><i>optional roll deg</i><code>)</code></td></tr>
|
|
|
|
<tr><td>Array</td><td><code>[</code><i>element<sub>0</sub></i><code>, </code><i>element<sub>1</sub></i><code>, </code> ... <code>, </code><i>element<sub>n-1</sub></i><code>]</code></td></tr>
|
|
<tr><td>Table</td><td><code>{</code><i>symbol<sub>0</sub></i><code> = </code><i>value<sub>0</sub></i>
|
|
<br><code> </code><i>symbol<sub>1</sub></i><code> = </code><i>value<sub>1</sub></i>
|
|
<br><code> </code>...
|
|
<br><code> </code><i>symbol<sub>n-1</sub></i><code> = </code><i>value<sub>n-1</sub></i><code>}</code></td></tr>
|
|
</table>
|
|
|
|
See also boost::any for a more general purpose but slightly harder to use
|
|
"any" for C++.
|
|
|
|
The semantics of operator[] and the get() methods are slightly different;
|
|
operator[] acts more like a scripting language that automatically extends
|
|
arrays and tables instead of generating errors. get() has more strict semantics,
|
|
like a C++ class.
|
|
|
|
AnyVal uses copy-on-mutate, so that <code>AnyVal a = b</code> semantically copies <code>b</code> (like <code>int a = b</code> would), although in practice
|
|
it delays the copy until one is mutated so that it is still fast to "copy" large arrays and tables.
|
|
|
|
Reading example:
|
|
<pre>
|
|
AnyVal property = AnyVal::fromFile("c:/tmp/test.txt"));
|
|
|
|
Vector3 vel = property["angular velocity"]
|
|
|
|
<i>Using defaults to handle errors:
|
|
If there was no "enabled" value, this will return the default instead of failing</i>
|
|
bool enabled = property["enabled"].boolean(true);
|
|
|
|
</pre>
|
|
|
|
Writing to a file:
|
|
<pre>
|
|
AnyVal dict(AnyVal::TABLE);
|
|
|
|
dict["enabled"] = AnyVal(true);
|
|
dict["weight"] = 100;
|
|
dict["angular velocity"] = Vector3(1, -3, 4.5);
|
|
|
|
TextOutput t("c:/tmp/test.txt");
|
|
dict.serialize(t);
|
|
t.commit();
|
|
</pre>
|
|
|
|
Example of a data file:
|
|
<pre>
|
|
{
|
|
heights = [1, 17, 32]
|
|
model =
|
|
{
|
|
color = C3(1, 1, 1)
|
|
filename = "foo.md2"
|
|
}
|
|
position = V3(23, 14, 0)
|
|
name = "Elmer"
|
|
}
|
|
</pre>
|
|
|
|
<p>
|
|
<b>What's the difference from boost::any?</b>
|
|
<br>I think that AnyVal will be easier for novice C++ users. It addresses the problem that
|
|
even though G3D::TextInput makes reading configuration files extremely simple, many people
|
|
still don't use it. So AnyVal makes it ridiculously simple to read and write a tree of G3D
|
|
types to a file.
|
|
|
|
<i>AnyVal:</i>
|
|
<pre>
|
|
{
|
|
AnyVal tree(TextInput("config.txt"));
|
|
|
|
bool enabled = tree.get("enabled", false);
|
|
Vector3 direction = tree.get("direction", Vector3::zero());
|
|
...
|
|
}
|
|
</pre>
|
|
|
|
<i>boost:</i>
|
|
<pre>
|
|
{
|
|
bool enabled = false;
|
|
Vector3 direction;
|
|
Table<boost::any> tree;
|
|
|
|
...write lots of file parsing code...
|
|
|
|
if (tree.containsKey("enabled")) {
|
|
const boost::any& val = tree["enabled"];
|
|
try {
|
|
enabled = any_cast<bool>(val);
|
|
} catch(const boost::bad_any_cast &) {
|
|
}
|
|
}
|
|
|
|
if (tree.containsKey("direction")) {
|
|
const boost::any& val = tree["direction"];
|
|
try {
|
|
direction = any_cast<Vector3>(val);
|
|
} catch(const boost::bad_any_cast &) {
|
|
}
|
|
}
|
|
...
|
|
}
|
|
</pre>
|
|
|
|
\deprecated
|
|
*/
|
|
class AnyVal {
|
|
public:
|
|
|
|
/** Array and table values are all Any.*/
|
|
enum Type {
|
|
NIL,
|
|
NUMBER,
|
|
BOOLEAN,
|
|
STRING,
|
|
VECTOR2,
|
|
VECTOR3,
|
|
VECTOR4,
|
|
MATRIX2,
|
|
MATRIX3,
|
|
MATRIX4,
|
|
QUAT,
|
|
COORDINATEFRAME,
|
|
COORDINATEFRAME2D,
|
|
CFRAME = COORDINATEFRAME,
|
|
CFRAME2D = COORDINATEFRAME2D,
|
|
COLOR1,
|
|
COLOR3,
|
|
COLOR4,
|
|
RECT2D,
|
|
AABOX2D = RECT2D,
|
|
AABOX,
|
|
ARRAY,
|
|
TABLE};
|
|
|
|
/** Base class for all AnyVal exceptions.*/
|
|
class Exception {
|
|
public:
|
|
virtual ~Exception() {}
|
|
};
|
|
|
|
/** Thrown when an inappropriate operation is performed (e.g., operator[] on a number) */
|
|
class WrongType : public Exception {
|
|
public:
|
|
Type expected;
|
|
Type actual;
|
|
WrongType() : expected(NIL), actual(NIL) {}
|
|
WrongType(Type e, Type a) : expected(e), actual(a) {}
|
|
};
|
|
|
|
/** Thrown by operator[] when a key is not present. */
|
|
class KeyNotFound : public Exception {
|
|
public:
|
|
std::string key;
|
|
KeyNotFound() {}
|
|
KeyNotFound(const std::string& k) : key(k) {}
|
|
};
|
|
|
|
class IndexOutOfBounds : public Exception {
|
|
public:
|
|
int index;
|
|
int size;
|
|
IndexOutOfBounds() : index(0), size(0) {}
|
|
IndexOutOfBounds(int i, int s) : index(i), size(s) {}
|
|
};
|
|
|
|
/** Thrown when deserialize() when the input is incorrectly formatted. */
|
|
class CorruptText : public Exception {
|
|
public:
|
|
std::string message;
|
|
|
|
/** Token where the problem occurred.*/
|
|
G3D::Token token;
|
|
|
|
CorruptText() {}
|
|
CorruptText(const std::string& s, const G3D::Token& t) : message(s), token(t) {}
|
|
};
|
|
|
|
private:
|
|
|
|
Type m_type;
|
|
void* m_value;
|
|
|
|
/** For table and array 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 the
|
|
value. This is not used for other types.
|
|
*/
|
|
int* m_referenceCount;
|
|
|
|
/** Decrements the reference count (if there is one). If the
|
|
reference count is zero or does not exist. Calls delete on @a
|
|
m_value and sets it to NULL.
|
|
*/
|
|
void deleteValue();
|
|
|
|
/** Returns a copy of the value. */
|
|
void* copyValue() const;
|
|
|
|
/** Assumes isSharedType. Ensures that this has a unique reference */
|
|
void makeMutable();
|
|
|
|
/** True if this is a shared value between multiple instances. */
|
|
inline bool isShared() const {
|
|
return m_referenceCount && (*m_referenceCount > 1);
|
|
}
|
|
|
|
/** True when m_value is a double pointer */
|
|
inline bool isSharedType() const {
|
|
return (m_type == TABLE) || (m_type == ARRAY);
|
|
}
|
|
|
|
public:
|
|
|
|
AnyVal();
|
|
|
|
/** Deserialize */
|
|
explicit AnyVal(G3D::TextInput& t);
|
|
|
|
static AnyVal fromFile(const std::string& filename);
|
|
|
|
void load(const std::string& filename);
|
|
|
|
void save(const std::string& filename) const;
|
|
|
|
///** Deserialize */
|
|
//explicit AnyVal(G3D::BinaryInput& t);
|
|
|
|
/** Construct a number */
|
|
AnyVal(double);
|
|
AnyVal(int);
|
|
|
|
// Explicit to avoid ambiguity with the 'double' constructor
|
|
// when an integer type is constructed
|
|
AnyVal(bool);
|
|
AnyVal(const G3D::Vector2&);
|
|
AnyVal(const G3D::Vector3&);
|
|
AnyVal(const G3D::Vector4&);
|
|
|
|
AnyVal(const G3D::Color1&);
|
|
AnyVal(const G3D::Color3&);
|
|
AnyVal(const G3D::Color4&);
|
|
|
|
AnyVal(const std::string&);
|
|
AnyVal(const char*);
|
|
|
|
AnyVal(const G3D::Quat&);
|
|
|
|
AnyVal(const G3D::Rect2D&);
|
|
AnyVal(const G3D::AABox&);
|
|
|
|
AnyVal(const G3D::CoordinateFrame&);
|
|
AnyVal(const G3D::Matrix2&);
|
|
AnyVal(const G3D::Matrix3&);
|
|
AnyVal(const G3D::Matrix4&);
|
|
|
|
AnyVal(const AnyVal&);
|
|
|
|
AnyVal(Type arrayOrTable);
|
|
|
|
AnyVal& operator=(const AnyVal&);
|
|
|
|
/** Frees the underlying storage */
|
|
~AnyVal();
|
|
|
|
Type type() const;
|
|
|
|
bool isNil() const {
|
|
return type() == NIL;
|
|
}
|
|
|
|
void serialize(G3D::TextOutput& t) const;
|
|
//void serialize(G3D::BinaryOutput& t) const;
|
|
void deserialize(G3D::TextInput& t);
|
|
//void deserialize(G3D::BinaryInput& t);
|
|
|
|
/** Array dereference. If the index is out of bounds, IndexOutOfBounds is thrown */
|
|
const AnyVal& operator[](int) const;
|
|
|
|
/** Extend this array by one element. */
|
|
void append(const AnyVal&);
|
|
|
|
/** If the index is out of bounds, the array is resized. If the index is negative,
|
|
IndexOutOfBounds is thrown.*/
|
|
AnyVal& operator[](int);
|
|
|
|
/** If @a i is out of bounds or this is not an ARRAY, defaultVal is returned.*/
|
|
const AnyVal& get(int i, const AnyVal& defaultVal) const;
|
|
|
|
/** If out of bounds, IndexOutOfBounds is thrown. */
|
|
const AnyVal& get(int i) const;
|
|
|
|
/** Returns defaultVal if this is not a TABLE or the key is not found. */
|
|
const AnyVal& get(const std::string& key, const AnyVal& defaultVal) const;
|
|
|
|
/** Throws KeyNotFound exception if the key is not present.*/
|
|
const AnyVal& get(const std::string& key) const;
|
|
|
|
/** Table reference */
|
|
const AnyVal& operator[](const std::string&) const;
|
|
|
|
/** Table reference. If the element does not exist, it is created. */
|
|
AnyVal& operator[](const std::string&);
|
|
|
|
/** Table reference */
|
|
const AnyVal& operator[](const char*) const;
|
|
|
|
/** Table reference. If the element does not exist, it is created. */
|
|
AnyVal& operator[](const char*);
|
|
|
|
/** If this value is not a number throws a WrongType exception. */
|
|
double number() const;
|
|
|
|
/** If this value is not a number, returns defaultVal. */
|
|
double number(double defaultVal) const;
|
|
|
|
operator double () const {
|
|
return number();
|
|
}
|
|
|
|
operator float () const {
|
|
return (float)number();
|
|
}
|
|
|
|
bool boolean() const;
|
|
bool boolean(bool b) const;
|
|
|
|
operator bool() const {
|
|
return boolean();
|
|
}
|
|
|
|
const std::string& string() const;
|
|
const std::string& string(const std::string& defaultVal) const;
|
|
|
|
operator const std::string& () const {
|
|
return string();
|
|
}
|
|
|
|
const G3D::Rect2D& rect2D() const;
|
|
const G3D::Rect2D& rect2D(const G3D::Rect2D& defaultVal) const;
|
|
|
|
operator const Rect2D& () const {
|
|
return rect2D();
|
|
}
|
|
|
|
const G3D::AABox& aabox() const;
|
|
const G3D::AABox& aabox(const G3D::AABox& defaultVal) const;
|
|
|
|
operator const AABox& () const {
|
|
return aabox();
|
|
}
|
|
|
|
const G3D::Vector2& vector2() const;
|
|
const G3D::Vector2& vector2(const G3D::Vector2& defaultVal) const;
|
|
|
|
operator const Vector2& () const {
|
|
return vector2();
|
|
}
|
|
|
|
const G3D::Vector3& vector3() const;
|
|
const G3D::Vector3& vector3(const G3D::Vector3& defaultVal) const;
|
|
|
|
operator const Vector3& () {
|
|
return vector3();
|
|
}
|
|
|
|
const G3D::Vector4& vector4() const;
|
|
const G3D::Vector4& vector4(const G3D::Vector4& defaultVal) const;
|
|
|
|
operator const Vector4& () const {
|
|
return vector4();
|
|
}
|
|
|
|
const G3D::Color1& color1() const;
|
|
const G3D::Color1& color1(const G3D::Color1& defaultVal) const;
|
|
|
|
const G3D::Color3& color3() const;
|
|
const G3D::Color3& color3(const G3D::Color3& defaultVal) const;
|
|
|
|
operator const Color3& () const {
|
|
return color3();
|
|
}
|
|
|
|
const G3D::Color4& color4() const;
|
|
const G3D::Color4& color4(const G3D::Color4& defaultVal) const;
|
|
|
|
operator const Color4& () const {
|
|
return color4();
|
|
}
|
|
|
|
const G3D::CoordinateFrame& coordinateFrame() const;
|
|
const G3D::CoordinateFrame& coordinateFrame(const G3D::CoordinateFrame& defaultVal) const;
|
|
|
|
operator const CoordinateFrame& () const {
|
|
return coordinateFrame();
|
|
}
|
|
|
|
const G3D::Matrix2& matrix2() const;
|
|
const G3D::Matrix2& matrix2(const G3D::Matrix2& defaultVal) const;
|
|
|
|
operator const Matrix2& () const {
|
|
return matrix2();
|
|
}
|
|
|
|
const G3D::Matrix3& matrix3() const;
|
|
const G3D::Matrix3& matrix3(const G3D::Matrix3& defaultVal) const;
|
|
|
|
operator const Matrix3& () const {
|
|
return matrix3();
|
|
}
|
|
|
|
const G3D::Matrix4& matrix4() const;
|
|
const G3D::Matrix4& matrix4(const G3D::Matrix4& defaultVal) const;
|
|
|
|
operator const Matrix4& () const {
|
|
return matrix4();
|
|
}
|
|
|
|
const G3D::Quat& quat() const;
|
|
const G3D::Quat& quat(const G3D::Quat& defaultVal) const;
|
|
|
|
operator const Quat& () const {
|
|
return quat();
|
|
}
|
|
|
|
std::string toString() const;
|
|
|
|
/** Number of elements for an array or table.*/
|
|
int size() const;
|
|
|
|
/** For a table, returns the keys. */
|
|
void getKeys(G3D::Array<std::string>&) const;
|
|
};
|
|
|
|
}
|
|
|
|
#endif
|