summaryrefslogtreecommitdiff
path: root/value.h
diff options
context:
space:
mode:
Diffstat (limited to 'value.h')
-rw-r--r--value.h1115
1 files changed, 771 insertions, 344 deletions
diff --git a/value.h b/value.h
index fe01786b..fe8c5ecb 100644
--- a/value.h
+++ b/value.h
@@ -1,443 +1,870 @@
+/*
+ * Copyright (c) 2003-2007, John Wiegley. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - 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.
+ *
+ * - Neither the name of New Artisans LLC 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 ANY 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
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY 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 ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file value.h
+ * @author John Wiegley
+ * @date Thu Jun 14 21:54:00 2007
+ *
+ * @brief Abstract dynamic type representing various numeric types.
+ *
+ * A value_t object can be one of many types, and changes its type
+ * dynamically based on how it is used. For example, if you assign
+ * the number 10 to a value object, it's internal type will be
+ * INTEGER.
+ */
#ifndef _VALUE_H
#define _VALUE_H
-#include "amount.h"
-#include "balance.h"
-#include "error.h"
-
-#include <exception>
+#include "balpair.h" // pulls in balance.h and amount.h
namespace ledger {
-// The following type is a polymorphous value type used solely for
-// performance reasons. The alternative is to compute value
-// expressions (valexpr.cc) in terms of the largest data type,
-// balance_t. This was found to be prohibitively expensive, especially
-// when large logic chains were involved, since many temporary
-// allocations would occur for every operator. With value_t, and the
-// fact that logic chains only need boolean values to continue, no
-// memory allocations need to take place at all.
-
+/**
+ * @class value_t
+ *
+ * @brief Dynamic type representing various numeric types.
+ *
+ * The following type is a polymorphous value type used solely for
+ * performance reasons. The alternative is to compute value
+ * expressions (valexpr.cc) in terms of the largest data type,
+ * balance_t. This was found to be prohibitively expensive, especially
+ * when large logic chains were involved, since many temporary
+ * allocations would occur for every operator. With value_t, and the
+ * fact that logic chains only need boolean values to continue, no
+ * memory allocations need to take place at all.
+ */
class value_t
+ : public ordered_field_operators<value_t,
+ equality_comparable<value_t, balance_pair_t,
+ equality_comparable<value_t, balance_t,
+ additive<value_t, balance_pair_t,
+ additive<value_t, balance_t,
+ multiplicative<value_t, balance_pair_t,
+ multiplicative<value_t, balance_t,
+ ordered_field_operators<value_t, amount_t,
+#ifdef HAVE_GDTOA
+ ordered_field_operators<value_t, double,
+#endif
+ ordered_field_operators<value_t, unsigned long,
+ ordered_field_operators<value_t, long> > > > > > > > > >
+#ifdef HAVE_GDTOA
+ >
+#endif
{
- public:
- char data[sizeof(balance_pair_t)];
-
+public:
+ /**
+ * The sequence_t member type abstracts the type used to represent a
+ * resizable "array" of value_t objects.
+ */
+ typedef std::vector<value_t> sequence_t;
+
+ typedef sequence_t::iterator iterator;
+ typedef sequence_t::const_iterator const_iterator;
+ typedef sequence_t::difference_type difference_type;
+
+ /**
+ * type_t gives the type of the data contained or referenced by a
+ * value_t object. Use the type() method to get a value of type
+ * type_t.
+ */
enum type_t {
- BOOLEAN,
- INTEGER,
- DATETIME,
- AMOUNT,
- BALANCE,
- BALANCE_PAIR
- } type;
+ VOID, // a null value (i.e., uninitialized)
+ BOOLEAN, // a boolean
+ DATETIME, // a date and time (Boost posix_time)
+ INTEGER, // a signed integer value
+ AMOUNT, // a ledger::amount_t
+ BALANCE, // a ledger::balance_t
+ BALANCE_PAIR, // a ledger::balance_pair_t
+ STRING, // a string object
+ SEQUENCE, // a vector of value_t objects
+ POINTER // an opaque pointer of any type
+ };
+
+private:
+ class storage_t
+ {
+ friend class value_t;
+
+ /**
+ * The `data' member holds the actual bytes relating to whatever
+ * has been stuffed into this storage object. There is a set of
+ * asserts in value.cc to guarantee that the sizeof expression
+ * used here is indeed at least as big as the largest object that
+ * will ever be copied into `data'.
+ *
+ * The `type' member holds the value_t::type_t value representing
+ * the type of the object stored.
+ */
+ char data[sizeof(amount_t)];
+ type_t type;
+
+ /**
+ * `refc' holds the current reference count for each storage_t
+ * object.
+ */
+ mutable int refc;
+
+ /**
+ * Constructor. Since all storage object are assigned to after
+ * construction, the only constructors allowed are explicit, and
+ * copy (see below). The default starting type is VOID, which
+ * should rarely ever be seen in practice, since the first thing
+ * that value_t typically does is to assign a valid value.
+ */
+ explicit storage_t() : type(VOID), refc(0) {
+ TRACE_CTOR(value_t::storage_t, "");
+ }
- value_t() {
- *((long *) data) = 0;
- type = INTEGER;
- }
+ public: // so `checked_delete' can access it
+ /**
+ * Destructor. Must only be called when the reference count has
+ * reached zero. The `destroy' method is used to do the actual
+ * cleanup of the data, since it's quite possible for `destroy' to
+ * be called while the object is still active -- to clear the
+ * stored data for subsequent reuse of the storage_t object.
+ */
+ ~storage_t() {
+ TRACE_DTOR(value_t::storage_t);
+ DEBUG("value.storage.refcount", "Destroying " << this);
+ assert(refc == 0);
+ destroy();
+ }
- value_t(const value_t& value) : type(INTEGER) {
- *this = value;
- }
- value_t(const bool value) {
- *((bool *) data) = value;
- type = BOOLEAN;
- }
- value_t(const long value) {
- *((long *) data) = value;
- type = INTEGER;
- }
- value_t(const datetime_t value) {
- *((datetime_t *) data) = value;
- type = DATETIME;
- }
- value_t(const unsigned long value) {
- new((amount_t *) data) amount_t(value);
- type = AMOUNT;
+ void destroy();
+
+ private:
+ /**
+ * Assignment and copy operators. These are called when making a
+ * new copy of a storage object in order to modify the copy.
+ */
+ explicit storage_t(const storage_t& rhs)
+ : type(rhs.type), refc(0) {
+ TRACE_CTOR(value_t::storage_t, "copy");
+ *this = rhs;
+ }
+ storage_t& operator=(const storage_t& rhs);
+
+ /**
+ * Reference counting methods. The intrusive_ptr_* methods are
+ * used by boost::intrusive_ptr to manage the calls to acquire and
+ * release.
+ */
+ void acquire() const {
+ DEBUG("value.storage.refcount",
+ "Acquiring " << this << ", refc now " << refc + 1);
+ assert(refc >= 0);
+ refc++;
+ }
+ void release() const {
+ DEBUG("value.storage.refcount",
+ "Releasing " << this << ", refc now " << refc - 1);
+ assert(refc > 0);
+ if (--refc == 0)
+ checked_delete(this);
+ }
+
+ friend inline void intrusive_ptr_add_ref(value_t::storage_t * storage) {
+ storage->acquire();
+ }
+ friend inline void intrusive_ptr_release(value_t::storage_t * storage) {
+ storage->release();
+ }
+ };
+
+ /**
+ * The actual data for each value_t is kept in the `storage' member.
+ * Data is modified using a copy-on-write policy.
+ */
+ intrusive_ptr<storage_t> storage;
+
+ /**
+ * _dup() makes a private copy of the current value so that it can
+ * subsequently be modified.
+ *
+ * _clear() removes our pointer to the current value and initializes
+ * a new value for things to be stored in.
+ *
+ * _reset() makes the current object appear as if it had been
+ * default initialized.
+ */
+ void _dup();
+ void _clear() {
+ if (! storage || storage->refc > 1)
+ storage = new storage_t;
+ else
+ storage->destroy();
+ }
+ void _reset() {
+ if (storage)
+ storage = intrusive_ptr<storage_t>();
+ }
+
+ /**
+ * Because boolean "true" and "false" are so common, a pair of
+ * static references are kept to prevent the creation of throwaway
+ * storage_t objects just to represent these two common values.
+ */
+ static intrusive_ptr<storage_t> true_value;
+ static intrusive_ptr<storage_t> false_value;
+
+public:
+ // jww (2007-05-03): Make these private, and make ledger::initialize
+ // a member function of session_t.
+ static void initialize();
+ static void shutdown();
+
+public:
+ /**
+ * Constructors. value_t objects may be constructed from almost any
+ * value type that they can contain, including variations on those
+ * types (such as long, unsigned long, etc). The ordering of the
+ * methods here reflects the ordering of the constants in type_t
+ * above.
+ *
+ * One constructor of special note is that taking a string or
+ * character pointer as an argument. Because value_t("$100") is
+ * interpreted as a commoditized amount, the form value_t("$100",
+ * true) is required to represent the literal string "$100", and not
+ * the amount "one hundred dollars".
+ */
+ value_t() {
+ TRACE_CTOR(value_t, "");
+ }
+ value_t(const bool val) {
+ TRACE_CTOR(value_t, "const bool");
+ set_boolean(val);
+ }
+ value_t(const long val) {
+ TRACE_CTOR(value_t, "const long");
+ set_long(val);
+ }
+ value_t(const datetime_t val) {
+ TRACE_CTOR(value_t, "const datetime_t");
+ set_datetime(val);
+ }
+#ifdef HAVE_GDTOA
+ value_t(const double val) {
+ TRACE_CTOR(value_t, "const double");
+ set_amount(val);
+ }
+#endif
+ value_t(const unsigned long val) {
+ TRACE_CTOR(value_t, "const unsigned long");
+ set_amount(val);
+ }
+ explicit value_t(const string& val, bool literal = false) {
+ TRACE_CTOR(value_t, "const string&, bool");
+ if (literal)
+ set_string(val);
+ else
+ set_amount(amount_t(val));
+ }
+ explicit value_t(const char * val, bool literal = false) {
+ TRACE_CTOR(value_t, "const char *");
+ if (literal)
+ set_string(val);
+ else
+ set_amount(amount_t(val));
+ }
+ value_t(const amount_t& val) {
+ TRACE_CTOR(value_t, "const amount_t&");
+ set_amount(val);
+ }
+ value_t(const balance_t& val) {
+ TRACE_CTOR(value_t, "const balance_t&");
+ set_balance(val);
+ }
+ value_t(const balance_pair_t& val) {
+ TRACE_CTOR(value_t, "const balance_pair_t&");
+ set_balance_pair(val);
+ }
+ value_t(const sequence_t& val) {
+ TRACE_CTOR(value_t, "const sequence_t&");
+ set_sequence(val);
}
- value_t(const double value) {
- new((amount_t *) data) amount_t(value);
- type = AMOUNT;
+ template <typename T>
+ explicit value_t(T * item) {
+ TRACE_CTOR(value_t, "T *");
+ set_pointer(item);
}
- value_t(const std::string& value) {
- new((amount_t *) data) amount_t(value);
- type = AMOUNT;
+
+ /**
+ * Destructor. This does not do anything, because the intrusive_ptr
+ * that refers to our storage object will decrease its reference
+ * count itself upon destruction.
+ */
+ ~value_t() {
+ TRACE_DTOR(value_t);
}
- value_t(const char * value) {
- new((amount_t *) data) amount_t(value);
- type = AMOUNT;
+
+ /**
+ * Assignment and copy operators. Values are cheaply copied by
+ * simply creating another reference to the other value's storage
+ * object. A true copy is only ever made prior to modification.
+ */
+ value_t(const value_t& val) {
+ TRACE_CTOR(value_t, "copy");
+ *this = val;
}
- value_t(const amount_t& value) {
- new((amount_t *)data) amount_t(value);
- type = AMOUNT;
+ value_t& operator=(const value_t& val) {
+ if (! (this == &val || storage == val.storage))
+ storage = val.storage;
+ return *this;
}
- value_t(const balance_t& value) : type(INTEGER) {
- *this = value;
+
+ /**
+ * Comparison operators. Values can be compared to other values
+ */
+ bool operator==(const value_t& val) const;
+ bool operator<(const value_t& val) const;
+
+ template <typename T>
+ bool operator==(const T& amt) const {
+ return *this == value_t(amt);
}
- value_t(const balance_pair_t& value) : type(INTEGER) {
- *this = value;
+ template <typename T>
+ bool operator<(const T& amt) const {
+ return *this < value_t(amt);
+ }
+
+ /**
+ * Binary arithmetic operators.
+ *
+ * add(amount_t, optional<amount_t>) allows for the possibility of
+ * adding both an amount and its cost in a single operation.
+ * Otherwise, there is no way to separately represent the "cost"
+ * part of an amount addition statement.
+ */
+ value_t& operator+=(const value_t& val);
+ value_t& operator-=(const value_t& val);
+ value_t& operator*=(const value_t& val);
+ value_t& operator/=(const value_t& val);
+
+ // jww (2008-04-24): This could be expensive; perhaps it should be
+ // optional<amount_t&>&?
+ value_t& add(const amount_t& amount,
+ const optional<amount_t>& cost = none);
+
+ /**
+ * Unary arithmetic operators.
+ */
+ value_t negate() const {
+ value_t temp = *this;
+ temp.in_place_negate();
+ return temp;
}
+ void in_place_negate();
- ~value_t() {
- destroy();
+ value_t operator-() const {
+ return negate();
}
- void destroy();
- void simplify();
+ value_t abs() const;
+ value_t round() const;
+ value_t unround() const;
- value_t& operator=(const value_t& value);
- value_t& operator=(const bool value) {
- if ((bool *) data != &value) {
- destroy();
- *((bool *) data) = value;
- type = BOOLEAN;
- }
- return *this;
- }
- value_t& operator=(const long value) {
- if ((long *) data != &value) {
- destroy();
- *((long *) data) = value;
- type = INTEGER;
- }
- return *this;
+ value_t reduce() const {
+ value_t temp(*this);
+ temp.in_place_reduce();
+ return temp;
}
- value_t& operator=(const datetime_t value) {
- if ((datetime_t *) data != &value) {
- destroy();
- *((datetime_t *) data) = value;
- type = DATETIME;
+ void in_place_reduce();
+
+ value_t value(const optional<datetime_t>& moment = none) const;
+
+ /**
+ * Truth tests.
+ */
+ operator bool() const;
+
+ bool is_realzero() const;
+ bool is_zero() const;
+ bool is_null() const {
+ if (! storage) {
+ return true;
+ } else {
+ assert(! is_type(VOID));
+ return false;
}
- return *this;
}
- value_t& operator=(const unsigned long value) {
- return *this = amount_t(value);
- }
- value_t& operator=(const double value) {
- return *this = amount_t(value);
- }
- value_t& operator=(const std::string& value) {
- return *this = amount_t(value);
+
+ type_t type() const {
+ type_t result = storage ? storage->type : VOID;
+ assert(result >= VOID && result <= POINTER);
+ return result;
}
- value_t& operator=(const char * value) {
- return *this = amount_t(value);
+
+ bool is_type(type_t _type) const {
+ return type() == _type;
}
- value_t& operator=(const amount_t& value) {
- if (type == AMOUNT &&
- (amount_t *) data == &value)
- return *this;
-
- if (value.realzero()) {
- return *this = 0L;
+private:
+ void set_type(type_t new_type) {
+ assert(new_type >= VOID && new_type <= POINTER);
+ if (new_type == VOID) {
+ _reset();
+ assert(is_null());
} else {
- destroy();
- new((amount_t *)data) amount_t(value);
- type = AMOUNT;
+ _clear();
+ storage->type = new_type;
+ assert(is_type(new_type));
}
- return *this;
}
- value_t& operator=(const balance_t& value) {
- if (type == BALANCE &&
- (balance_t *) data == &value)
- return *this;
-
- if (value.realzero()) {
- return *this = 0L;
- }
- else if (value.amounts.size() == 1) {
- return *this = (*value.amounts.begin()).second;
- }
- else {
- destroy();
- new((balance_t *)data) balance_t(value);
- type = BALANCE;
- return *this;
- }
+
+public:
+ /**
+ * Data manipulation methods. A value object may be truth tested for the
+ * existence of every type it can contain:
+ *
+ * is_boolean()
+ * is_long()
+ * is_datetime()
+ * is_amount()
+ * is_balance()
+ * is_balance_pair()
+ * is_string()
+ * is_sequence()
+ * is_pointer()
+ *
+ * There are corresponding as_*() methods that represent a value as a
+ * reference to its underlying type. For example, as_long() returns a
+ * reference to a "const long".
+ *
+ * There are also as_*_lval() methods, which represent the underlying data
+ * as a reference to a non-const type. The difference here is that an
+ * _lval() call causes the underlying data to be fully copied before the
+ * resulting reference is returned.
+ *
+ * Lastly, there are corresponding set_*(data) methods for directly
+ * assigning data of a particular type, rather than using the regular
+ * assignment operator (whose implementation simply calls the various set_
+ * methods).
+ */
+ bool is_boolean() const {
+ return is_type(BOOLEAN);
+ }
+ bool& as_boolean_lval() {
+ assert(is_boolean());
+ _dup();
+ return *(bool *) storage->data;
+ }
+ const bool& as_boolean() const {
+ assert(is_boolean());
+ return *(bool *) storage->data;
+ }
+ void set_boolean(const bool val) {
+ set_type(BOOLEAN);
+ storage = val ? true_value : false_value;
+ }
+
+ bool is_long() const {
+ return is_type(INTEGER);
+ }
+ long& as_long_lval() {
+ assert(is_long());
+ _dup();
+ return *(long *) storage->data;
+ }
+ const long& as_long() const {
+ assert(is_long());
+ return *(long *) storage->data;
+ }
+ void set_long(const long val) {
+ set_type(INTEGER);
+ *(long *) storage->data = val;
+ }
+
+ bool is_datetime() const {
+ return is_type(DATETIME);
+ }
+ datetime_t& as_datetime_lval() {
+ assert(is_datetime());
+ _dup();
+ return *(datetime_t *) storage->data;
+ }
+ const datetime_t& as_datetime() const {
+ assert(is_datetime());
+ return *(datetime_t *) storage->data;
+ }
+ void set_datetime(const datetime_t& val) {
+ set_type(DATETIME);
+ new((datetime_t *) storage->data) datetime_t(val);
+ }
+
+ bool is_amount() const {
+ return is_type(AMOUNT);
+ }
+ amount_t& as_amount_lval() {
+ assert(is_amount());
+ _dup();
+ amount_t& amt(*(amount_t *) storage->data);
+ assert(amt.valid());
+ return amt;
+ }
+ const amount_t& as_amount() const {
+ assert(is_amount());
+ amount_t& amt(*(amount_t *) storage->data);
+ assert(amt.valid());
+ return amt;
+ }
+ void set_amount(const amount_t& val) {
+ assert(val.valid());
+ set_type(AMOUNT);
+ new((amount_t *) storage->data) amount_t(val);
+ }
+
+ bool is_balance() const {
+ return is_type(BALANCE);
+ }
+ balance_t& as_balance_lval() {
+ assert(is_balance());
+ _dup();
+ balance_t& bal(**(balance_t **) storage->data);
+ assert(bal.valid());
+ return bal;
+ }
+ const balance_t& as_balance() const {
+ assert(is_balance());
+ balance_t& bal(**(balance_t **) storage->data);
+ assert(bal.valid());
+ return bal;
}
- value_t& operator=(const balance_pair_t& value) {
- if (type == BALANCE_PAIR &&
- (balance_pair_t *) data == &value)
- return *this;
-
- if (value.realzero()) {
- return *this = 0L;
- }
- else if (! value.cost) {
- return *this = value.quantity;
- }
- else {
- destroy();
- new((balance_pair_t *)data) balance_pair_t(value);
- type = BALANCE_PAIR;
- return *this;
- }
+ void set_balance(const balance_t& val) {
+ assert(val.valid());
+ set_type(BALANCE);
+ *(balance_t **) storage->data = new balance_t(val);
}
- value_t& operator+=(const value_t& value);
- value_t& operator-=(const value_t& value);
- value_t& operator*=(const value_t& value);
- value_t& operator/=(const value_t& value);
-
- template <typename T>
- value_t& operator+=(const T& value) {
- return *this += value_t(value);
+ bool is_balance_pair() const {
+ return is_type(BALANCE_PAIR);
}
- template <typename T>
- value_t& operator-=(const T& value) {
- return *this -= value_t(value);
+ balance_pair_t& as_balance_pair_lval() {
+ assert(is_balance_pair());
+ _dup();
+ balance_pair_t& bal_pair(**(balance_pair_t **) storage->data);
+ assert(bal_pair.valid());
+ return bal_pair;
}
- template <typename T>
- value_t& operator*=(const T& value) {
- return *this *= value_t(value);
+ const balance_pair_t& as_balance_pair() const {
+ assert(is_balance_pair());
+ balance_pair_t& bal_pair(**(balance_pair_t **) storage->data);
+ assert(bal_pair.valid());
+ return bal_pair;
}
- template <typename T>
- value_t& operator/=(const T& value) {
- return *this /= value_t(value);
+ void set_balance_pair(const balance_pair_t& val) {
+ assert(val.valid());
+ set_type(BALANCE_PAIR);
+ *(balance_pair_t **) storage->data = new balance_pair_t(val);
}
- value_t operator+(const value_t& value) {
- value_t temp(*this);
- temp += value;
- return temp;
+ bool is_string() const {
+ return is_type(STRING);
}
- value_t operator-(const value_t& value) {
- value_t temp(*this);
- temp -= value;
- return temp;
+ string& as_string_lval() {
+ assert(is_string());
+ _dup();
+ return *(string *) storage->data;
}
- value_t operator*(const value_t& value) {
- value_t temp(*this);
- temp *= value;
- return temp;
+ const string& as_string() const {
+ assert(is_string());
+ return *(string *) storage->data;
}
- value_t operator/(const value_t& value) {
- value_t temp(*this);
- temp /= value;
- return temp;
+ void set_string(const string& val = "") {
+ set_type(STRING);
+ new((string *) storage->data) string(val);
+ }
+ void set_string(const char * val = "") {
+ set_type(STRING);
+ new((string *) storage->data) string(val);
}
- template <typename T>
- value_t operator+(const T& value) {
- return *this + value_t(value);
+ bool is_sequence() const {
+ return is_type(SEQUENCE);
}
- template <typename T>
- value_t operator-(const T& value) {
- return *this - value_t(value);
+ sequence_t& as_sequence_lval() {
+ assert(is_sequence());
+ _dup();
+ return **(sequence_t **) storage->data;
}
- template <typename T>
- value_t operator*(const T& value) {
- return *this * value_t(value);
+ const sequence_t& as_sequence() const {
+ assert(is_sequence());
+ return **(sequence_t **) storage->data;
}
- template <typename T>
- value_t operator/(const T& value) {
- return *this / value_t(value);
+ void set_sequence(const sequence_t& val) {
+ set_type(SEQUENCE);
+ *(sequence_t **) storage->data = new sequence_t(val);
}
- bool operator<(const value_t& value);
- bool operator<=(const value_t& value);
- bool operator>(const value_t& value);
- bool operator>=(const value_t& value);
- bool operator==(const value_t& value);
- bool operator!=(const value_t& value) {
- return ! (*this == value);
+ bool is_pointer() const {
+ return is_type(POINTER);
}
-
- template <typename T>
- bool operator<(const T& value) {
- return *this < value_t(value);
+ boost::any& as_any_pointer_lval() {
+ assert(is_pointer());
+ _dup();
+ return *(boost::any *) storage->data;
}
template <typename T>
- bool operator<=(const T& value) {
- return *this <= value_t(value);
+ T * as_pointer_lval() {
+ assert(is_pointer());
+ _dup();
+ return any_cast<T *>(*(boost::any *) storage->data);
}
template <typename T>
- bool operator>(const T& value) {
- return *this > value_t(value);
+ T& as_ref_lval() {
+ assert(is_pointer());
+ _dup();
+ return *any_cast<T *>(*(boost::any *) storage->data);
}
- template <typename T>
- bool operator>=(const T& value) {
- return *this >= value_t(value);
+ const boost::any& as_any_pointer() const {
+ assert(is_pointer());
+ return *(boost::any *) storage->data;
}
template <typename T>
- bool operator==(const T& value) {
- return *this == value_t(value);
+ T * as_pointer() const {
+ assert(is_pointer());
+ return any_cast<T *>(*(boost::any *) storage->data);
}
template <typename T>
- bool operator!=(const T& value) {
- return ! (*this == value);
+ T& as_ref() const {
+ assert(is_pointer());
+ return *any_cast<T *>(*(boost::any *) storage->data);
+ }
+ void set_any_pointer(const boost::any& val) {
+ set_type(POINTER);
+ new((boost::any *) storage->data) boost::any(val);
}
-
template <typename T>
- operator T() const;
+ void set_pointer(T * val) {
+ set_type(POINTER);
+ new((boost::any *) storage->data) boost::any(val);
+ }
+
+ /**
+ * Data conversion methods. These methods convert a value object to
+ * its underlying type, where possible. If not possible, an
+ * exception is thrown.
+ */
+ bool to_boolean() const;
+ long to_long() const;
+ datetime_t to_datetime() const;
+ amount_t to_amount() const;
+ balance_t to_balance() const;
+ balance_pair_t to_balance_pair() const;
+ string to_string() const;
+ sequence_t to_sequence() const;
+
+ /**
+ * Dynamic typing conversion methods.
+ *
+ * `cast(type_t)' returns a new value whose type has been cast to
+ * the given type, but whose value is based on the original value.
+ * For example, the uncommoditized AMOUNT "100.00" could be cast to
+ * an INTEGER value. If a cast would lose information or is not
+ * meaningful, an exception is thrown.
+ *
+ * `simplify()' is an automatic cast to the simplest type that can
+ * still represent the original value.
+ *
+ * There are also "in-place" versions of these two methods:
+ * in_place_cast
+ * in_place_simplify
+ */
+ value_t cast(type_t cast_type) const {
+ value_t temp(*this);
+ temp.in_place_cast(cast_type);
+ return temp;
+ }
+ void in_place_cast(type_t cast_type);
- void negate();
- value_t negated() const {
+ value_t simplify() const {
value_t temp = *this;
- temp.negate();
+ temp.in_place_simplify();
return temp;
}
- value_t operator-() const {
- return negated();
+ void in_place_simplify();
+
+ /**
+ * Annotated commodity methods.
+ */
+ value_t annotated_price() const;
+ value_t annotated_date() const;
+ value_t annotated_tag() const;
+
+ value_t strip_annotations(const bool keep_price = amount_t::keep_price,
+ const bool keep_date = amount_t::keep_date,
+ const bool keep_tag = amount_t::keep_tag) const;
+
+ /**
+ * Collection-style access methods
+ */
+ value_t& operator[](const int index) {
+ assert(! is_null());
+ if (is_sequence())
+ return as_sequence_lval()[index];
+ else if (index == 0)
+ return *this;
+
+ assert(false);
+ static value_t null;
+ return null;
+ }
+ const value_t& operator[](const int index) const {
+ assert(! is_null());
+ if (is_sequence())
+ return as_sequence()[index];
+ else if (index == 0)
+ return *this;
+
+ assert(false);
+ static value_t null;
+ return null;
+ }
+
+ void push_back(const value_t& val) {
+ if (! val.is_null()) {
+ if (is_null()) {
+ *this = val;
+ } else {
+ if (! is_sequence())
+ in_place_cast(SEQUENCE);
+
+ if (! val.is_sequence()) {
+ if (! val.is_null())
+ as_sequence_lval().push_back(val);
+ } else {
+ const value_t::sequence_t& val_seq(val.as_sequence());
+ std::copy(val_seq.begin(), val_seq.end(),
+ back_inserter(as_sequence_lval()));
+ }
+ }
+ }
+ }
+
+ void pop_back() {
+ assert(! is_null());
+
+ if (! is_sequence()) {
+ _reset();
+ } else {
+ as_sequence_lval().pop_back();
+
+ const value_t::sequence_t& seq(as_sequence());
+ std::size_t new_size = seq.size();
+ if (new_size == 0)
+ _reset();
+ else if (new_size == 1)
+ *this = seq.front();
+ }
}
- bool realzero() const {
- switch (type) {
+ const std::size_t size() const {
+ if (is_null())
+ return 0;
+ else if (is_sequence())
+ return as_sequence().size();
+ else
+ return 1;
+ }
+
+ /**
+ * Informational methods.
+ */
+ string label(optional<type_t> the_type = none) const {
+ switch (the_type ? *the_type : type()) {
+ case VOID:
+ return "an uninitialized value";
case BOOLEAN:
- return ! *((bool *) data);
+ return "a boolean";
case INTEGER:
- return *((long *) data) == 0;
+ return "an integer";
case DATETIME:
- return ! *((datetime_t *) data);
+ return "a date/time";
case AMOUNT:
- return ((amount_t *) data)->realzero();
+ return "an amount";
case BALANCE:
- return ((balance_t *) data)->realzero();
+ return "a balance";
case BALANCE_PAIR:
- return ((balance_pair_t *) data)->realzero();
-
+ return "a balance pair";
+ case STRING:
+ return "a string";
+ case SEQUENCE:
+ return "a sequence";
+ case POINTER:
+ return "a pointer";
default:
- assert(0);
+ assert(false);
break;
}
- assert(0);
- return 0;
+ assert(false);
+ return "<invalid>";
}
- void abs();
- void cast(type_t cast_type);
value_t cost() const;
- value_t price() const;
- value_t date() const;
-
- value_t strip_annotations(const bool keep_price = amount_t::keep_price,
- const bool keep_date = amount_t::keep_date,
- const bool keep_tag = amount_t::keep_tag) const;
- value_t& add(const amount_t& amount, const amount_t * cost = NULL);
- value_t value(const datetime_t& moment) const;
- void reduce();
+ /**
+ * Printing methods.
+ */
+ void print(std::ostream& out, const int first_width,
+ const int latter_width = -1) const;
- value_t reduced() const {
- value_t temp(*this);
- temp.reduce();
- return temp;
- }
+ /**
+ * Debugging methods.
+ */
- void round();
- value_t unround() const;
+ bool valid() const;
};
-#define DEF_VALUE_AUX_OP(OP) \
- inline value_t operator OP(const balance_pair_t& value, \
- const value_t& obj) { \
- return value_t(value) OP obj; \
- } \
- inline value_t operator OP(const balance_t& value, \
- const value_t& obj) { \
- return value_t(value) OP obj; \
- } \
- inline value_t operator OP(const amount_t& value, \
- const value_t& obj) { \
- return value_t(value) OP obj; \
- } \
- template <typename T> \
- inline value_t operator OP(T value, const value_t& obj) { \
- return value_t(value) OP obj; \
- }
-
-DEF_VALUE_AUX_OP(+)
-DEF_VALUE_AUX_OP(-)
-DEF_VALUE_AUX_OP(*)
-DEF_VALUE_AUX_OP(/)
-
-DEF_VALUE_AUX_OP(<)
-DEF_VALUE_AUX_OP(<=)
-DEF_VALUE_AUX_OP(>)
-DEF_VALUE_AUX_OP(>=)
-DEF_VALUE_AUX_OP(==)
-DEF_VALUE_AUX_OP(!=)
-
-template <typename T>
-value_t::operator T() const
-{
- switch (type) {
- case BOOLEAN:
- return *((bool *) data);
- case INTEGER:
- return *((long *) data);
- case DATETIME:
- return *((datetime_t *) data);
- case AMOUNT:
- return *((amount_t *) data);
- case BALANCE:
- return *((balance_t *) data);
- case BALANCE_PAIR:
- return *((balance_pair_t *) data);
-
- default:
- assert(0);
- break;
- }
- assert(0);
- return 0;
-}
-
-template <> value_t::operator long() const;
-template <> value_t::operator datetime_t() const;
-template <> value_t::operator double() const;
+#define NULL_VALUE (value_t())
-inline value_t abs(const value_t& value) {
- value_t temp(value);
- temp.abs();
- return temp;
+inline value_t string_value(const string& str) {
+ return value_t(str, true);
}
-inline std::ostream& operator<<(std::ostream& out, const value_t& value) {
- switch (value.type) {
- case value_t::BOOLEAN:
- out << (*((bool *) value.data) ? "true" : "false");
- break;
- case value_t::INTEGER:
- out << *((long *) value.data);
- break;
- case value_t::DATETIME:
- out << *((datetime_t *) value.data);
- break;
- case value_t::AMOUNT:
- out << *((amount_t *) value.data);
- break;
- case value_t::BALANCE:
- out << *((balance_t *) value.data);
- break;
- case value_t::BALANCE_PAIR:
- out << *((balance_pair_t *) value.data);
- break;
-
- default:
- assert(0);
- break;
- }
+inline std::ostream& operator<<(std::ostream& out, const value_t& val) {
+ val.print(out, 12);
return out;
}
class value_context : public error_context
{
- value_t * bal;
+ value_t bal;
public:
- value_context(const value_t& _bal,
- const std::string& desc = "") throw();
- virtual ~value_context() throw();
+ value_context(const value_t& _bal, const string& desc = "") throw()
+ : error_context(desc), bal(_bal) {}
+ virtual ~value_context() throw() {}
virtual void describe(std::ostream& out) const throw();
};
-class value_error : public error {
- public:
- value_error(const std::string& reason, error_context * ctxt = NULL) throw()
- : error(reason, ctxt) {}
- virtual ~value_error() throw() {}
-};
+DECLARE_EXCEPTION(error, value_error);
} // namespace ledger