diff options
Diffstat (limited to 'value.h')
-rw-r--r-- | value.h | 1115 |
1 files changed, 771 insertions, 344 deletions
@@ -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 |