ledger/src/value.h
2008-08-05 19:49:01 -04:00

928 lines
26 KiB
C++

/*
* Copyright (c) 2003-2008, 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.
*/
/**
* @defgroup numerics Core numerics
*/
/**
* @file value.h
* @author John Wiegley
* @date Thu Jun 14 21:54:00 2007
*
* @brief Abstract dynamic type representing various numeric types.
*
* @ingroup numerics
*
* 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 "balpair.h" // pulls in balance.h and amount.h
namespace ledger {
DECLARE_EXCEPTION(value_error, std::runtime_error);
/**
* @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:
/**
* 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 {
VOID, // a null value (i.e., uninitialized)
BOOLEAN, // a boolean
DATETIME, // a date and time (Boost posix_time)
DATE, // a date (Boost gregorian::date)
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, "");
}
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();
}
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 (if necessary)
* so it can subsequently be modified.
*
* _clear() removes our pointer to the current value and initializes
* a new storage bin for things to be stored in.
*
* _reset() makes the current object appear as if it were
* uninitialized.
*/
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 datetime_t& val) {
TRACE_CTOR(value_t, "const datetime_t&");
set_datetime(val);
}
value_t(const date_t& val) {
TRACE_CTOR(value_t, "const date_t&");
set_date(val);
}
value_t(const long val) {
TRACE_CTOR(value_t, "const long");
set_long(val);
}
value_t(const unsigned long val) {
TRACE_CTOR(value_t, "const unsigned long");
set_amount(val);
}
#ifdef HAVE_GDTOA
value_t(const double val) {
TRACE_CTOR(value_t, "const double");
set_amount(val);
}
#endif
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);
}
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 sequence_t& val) {
TRACE_CTOR(value_t, "const sequence_t&");
set_sequence(val);
}
template <typename T>
explicit value_t(T * item) {
TRACE_CTOR(value_t, "T *");
set_pointer(item);
}
/**
* 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);
}
/**
* 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& operator=(const value_t& val) {
if (! (this == &val || storage == val.storage))
storage = val.storage;
return *this;
}
/**
* 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);
}
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);
// This special form of add is use to produce a balance pair by
// simultaneously adding both an amount and its cost.
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(); // exists for efficiency's sake
value_t operator-() const {
return negate();
}
value_t abs() const;
value_t round() const;
value_t unround() const;
value_t reduce() const {
value_t temp(*this);
temp.in_place_reduce();
return temp;
}
void in_place_reduce(); // exists for efficiency's sake
// Return the "market value" of a given value at a specific time.
value_t value(const optional<datetime_t>& moment = none) const;
value_t cost() const;
/**
* Truth tests.
*/
operator bool() const;
bool is_realzero() const;
bool is_zero() const;
bool is_null() const {
if (! storage) {
assert(is_type(VOID));
return true;
} else {
assert(! is_type(VOID));
return false;
}
}
type_t type() const {
type_t result = storage ? storage->type : VOID;
assert(result >= VOID && result <= POINTER);
return result;
}
bool is_type(type_t _type) const {
return type() == _type;
}
private:
void set_type(type_t new_type) {
assert(new_type >= VOID && new_type <= POINTER);
if (new_type == VOID) {
_reset();
assert(is_null());
} else {
_clear();
storage->type = new_type;
assert(is_type(new_type));
}
}
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_date()
* 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 *reinterpret_cast<bool *>(storage->data);
}
const bool& as_boolean() const {
assert(is_boolean());
return *reinterpret_cast<bool *>(storage->data);
}
void set_boolean(const bool val) {
set_type(BOOLEAN);
storage = val ? true_value : false_value;
}
bool is_datetime() const {
return is_type(DATETIME);
}
datetime_t& as_datetime_lval() {
assert(is_datetime());
_dup();
return *reinterpret_cast<datetime_t *>(storage->data);
}
const datetime_t& as_datetime() const {
assert(is_datetime());
return *reinterpret_cast<datetime_t *>(storage->data);
}
void set_datetime(const datetime_t& val) {
set_type(DATETIME);
new(reinterpret_cast<datetime_t *>(storage->data)) datetime_t(val);
}
bool is_date() const {
return is_type(DATE);
}
date_t& as_date_lval() {
assert(is_date());
_dup();
return *reinterpret_cast<date_t *>(storage->data);
}
const date_t& as_date() const {
assert(is_date());
return *reinterpret_cast<date_t *>(storage->data);
}
void set_date(const date_t& val) {
set_type(DATE);
new(reinterpret_cast<date_t *>(storage->data)) date_t(val);
}
bool is_long() const {
return is_type(INTEGER);
}
long& as_long_lval() {
assert(is_long());
_dup();
return *reinterpret_cast<long *>(storage->data);
}
const long& as_long() const {
assert(is_long());
return *reinterpret_cast<long *>(storage->data);
}
void set_long(const long val) {
set_type(INTEGER);
*reinterpret_cast<long *>(storage->data) = val;
}
bool is_amount() const {
return is_type(AMOUNT);
}
amount_t& as_amount_lval() {
assert(is_amount());
_dup();
amount_t& amt(*reinterpret_cast<amount_t *>(storage->data));
assert(amt.valid());
return amt;
}
const amount_t& as_amount() const {
assert(is_amount());
amount_t& amt(*reinterpret_cast<amount_t *>(storage->data));
assert(amt.valid());
return amt;
}
void set_amount(const amount_t& val) {
assert(val.valid());
set_type(AMOUNT);
new(reinterpret_cast<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(**reinterpret_cast<balance_t **>(storage->data));
assert(bal.valid());
return bal;
}
const balance_t& as_balance() const {
assert(is_balance());
balance_t& bal(**reinterpret_cast<balance_t **>(storage->data));
assert(bal.valid());
return bal;
}
void set_balance(const balance_t& val) {
assert(val.valid());
set_type(BALANCE);
*reinterpret_cast<balance_t **>(storage->data) = new balance_t(val);
}
bool is_balance_pair() const {
return is_type(BALANCE_PAIR);
}
balance_pair_t& as_balance_pair_lval() {
assert(is_balance_pair());
_dup();
balance_pair_t& bal_pair(**reinterpret_cast<balance_pair_t **>(storage->data));
assert(bal_pair.valid());
return bal_pair;
}
const balance_pair_t& as_balance_pair() const {
assert(is_balance_pair());
balance_pair_t& bal_pair(**reinterpret_cast<balance_pair_t **>(storage->data));
assert(bal_pair.valid());
return bal_pair;
}
void set_balance_pair(const balance_pair_t& val) {
assert(val.valid());
set_type(BALANCE_PAIR);
*reinterpret_cast<balance_pair_t **>(storage->data) = new balance_pair_t(val);
}
bool is_string() const {
return is_type(STRING);
}
string& as_string_lval() {
assert(is_string());
_dup();
return *reinterpret_cast<string *>(storage->data);
}
const string& as_string() const {
assert(is_string());
return *reinterpret_cast<string *>(storage->data);
}
void set_string(const string& val = "") {
set_type(STRING);
new(reinterpret_cast<string *>(storage->data)) string(val);
}
void set_string(const char * val = "") {
set_type(STRING);
new(reinterpret_cast<string *>(storage->data)) string(val);
}
bool is_sequence() const {
return is_type(SEQUENCE);
}
sequence_t& as_sequence_lval() {
assert(is_sequence());
_dup();
return **reinterpret_cast<sequence_t **>(storage->data);
}
const sequence_t& as_sequence() const {
assert(is_sequence());
return **reinterpret_cast<sequence_t **>(storage->data);
}
void set_sequence(const sequence_t& val) {
set_type(SEQUENCE);
*reinterpret_cast<sequence_t **>(storage->data) = new sequence_t(val);
}
/**
* Dealing with pointers is bit involved because we actually deal
* with typed pointers. For example, if you call as_pointer it
* returns a boost::any object, but if you use as_pointer<void>,
* then it returns a void *. The latter form only succeeds if the
* stored pointers was assigned to the value as a void*, otherwise
* it throws an exception.
*/
bool is_pointer() const {
return is_type(POINTER);
}
boost::any& as_any_pointer_lval() {
assert(is_pointer());
_dup();
return *reinterpret_cast<boost::any *>(storage->data);
}
template <typename T>
T * as_pointer_lval() {
assert(is_pointer());
_dup();
return any_cast<T *>(*reinterpret_cast<boost::any *>(storage->data));
}
template <typename T>
T& as_ref_lval() {
assert(is_pointer());
_dup();
return *any_cast<T *>(*reinterpret_cast<boost::any *>(storage->data));
}
const boost::any& as_any_pointer() const {
assert(is_pointer());
return *reinterpret_cast<boost::any *>(storage->data);
}
template <typename T>
T * as_pointer() const {
assert(is_pointer());
return any_cast<T *>(*reinterpret_cast<boost::any *>(storage->data));
}
template <typename T>
T& as_ref() const {
assert(is_pointer());
return *any_cast<T *>(*reinterpret_cast<boost::any *>(storage->data));
}
void set_any_pointer(const boost::any& val) {
set_type(POINTER);
new(reinterpret_cast<boost::any *>(storage->data)) boost::any(val);
}
template <typename T>
void set_pointer(T * val) {
set_type(POINTER);
new(reinterpret_cast<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;
date_t to_date() 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);
value_t simplify() const {
value_t temp = *this;
temp.in_place_simplify();
return temp;
}
void in_place_simplify();
/**
* Annotated commodity methods.
*/
#if 0
// These helper methods only apply to AMOUNT values.
value_t annotated_price() const;
value_t annotated_date() const;
value_t annotated_tag() const;
#endif
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 for SEQUENCE values.
*/
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();
}
}
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 "a boolean";
case DATETIME:
return "a date/time";
case DATE:
return "a date";
case INTEGER:
return "an integer";
case AMOUNT:
return "an amount";
case BALANCE:
return "a balance";
case BALANCE_PAIR:
return "a balance pair";
case STRING:
return "a string";
case SEQUENCE:
return "a sequence";
case POINTER:
return "a pointer";
default:
assert(false);
break;
}
assert(false);
return "<invalid>";
}
/**
* Printing methods.
*/
void dump(std::ostream& out, const int first_width,
const int latter_width = -1) const;
void print(std::ostream& out, const bool relaxed = true) const;
/**
* Serialization methods. A value may be deserialized from an input
* stream or a character pointer, and it may be serialized to an
* output stream. The methods used are:
*/
void read(const char *& data);
void write(std::ostream& out) const;
/**
* Debugging methods.
*/
bool valid() const;
};
#define NULL_VALUE (value_t())
inline value_t string_value(const string& str) {
return value_t(str, true);
}
inline std::ostream& operator<<(std::ostream& out, const value_t& val) {
val.print(out, 12);
return out;
}
inline string value_context(const value_t& val) {
std::ostringstream buf;
buf << std::right;
buf.width(20);
val.print(buf);
buf << std::endl;
return buf.str();
}
} // namespace ledger
#endif // _VALUE_H