summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Wiegley <johnw@newartisans.com>2007-06-16 01:50:14 +0000
committerJohn Wiegley <johnw@newartisans.com>2008-04-13 03:39:09 -0400
commitb73b3e0fd8db2480f295aa36ee1a314eff76ca1d (patch)
treeac854bace2b7afb60b7fe62b1d1d4dba4f656e35
parent6080b8ea00de6312f81f3be06bf1c5bbb9fd2700 (diff)
downloadfork-ledger-b73b3e0fd8db2480f295aa36ee1a314eff76ca1d.tar.gz
fork-ledger-b73b3e0fd8db2480f295aa36ee1a314eff76ca1d.tar.bz2
fork-ledger-b73b3e0fd8db2480f295aa36ee1a314eff76ca1d.zip
Added documentation for value_t
-rw-r--r--src/numerics/value.cc138
-rw-r--r--src/numerics/value.h427
2 files changed, 323 insertions, 242 deletions
diff --git a/src/numerics/value.cc b/src/numerics/value.cc
index ea84a5a8..236d0582 100644
--- a/src/numerics/value.cc
+++ b/src/numerics/value.cc
@@ -118,6 +118,33 @@ void value_t::shutdown()
false_value = intrusive_ptr<storage_t>();
}
+void value_t::_dup()
+{
+ assert(storage);
+ if (storage->refc > 1) {
+ storage = new storage_t(*storage.get());
+
+ // If the data referenced by storage is an allocated pointer, we
+ // need to create a new object in order to achieve duplication.
+ switch (storage->type) {
+ case BALANCE:
+ *(balance_t **) storage->data =
+ new balance_t(**(balance_t **) storage->data);
+ break;
+ case BALANCE_PAIR:
+ *(balance_pair_t **) storage->data =
+ new balance_pair_t(**(balance_pair_t **) storage->data);
+ break;
+ case SEQUENCE:
+ *(sequence_t **) storage->data =
+ new sequence_t(**(sequence_t **) storage->data);
+ break;
+ default:
+ break; // everything else has been duplicated
+ }
+ }
+}
+
value_t::operator bool() const
{
switch (type()) {
@@ -1482,115 +1509,4 @@ void value_t::print(std::ostream& out, const int first_width,
}
}
-std::ostream& operator<<(std::ostream& out, const value_t& val)
-{
- switch (val.type()) {
- case value_t::VOID:
- out << "VOID";
- break;
- case value_t::BOOLEAN:
- out << (val.as_boolean() ? "true" : "false");
- break;
- case value_t::INTEGER:
- out << val.as_long();
- break;
- case value_t::DATETIME:
- out << val.as_datetime();
- break;
- case value_t::AMOUNT:
- out << val.as_amount();
- break;
- case value_t::BALANCE:
- out << val.as_balance();
- break;
- case value_t::BALANCE_PAIR:
- out << val.as_balance_pair();
- break;
- case value_t::STRING:
- out << val.as_string();
- break;
- case value_t::XML_NODE:
- if (val.as_xml_node()->has_flags(XML_NODE_IS_PARENT))
- out << '<' << val.as_xml_node()->name() << '>';
- else
- out << val.as_xml_node()->to_value();
- break;
-
- case value_t::POINTER:
- throw_(value_error, "Cannot output a pointer value");
-
- case value_t::SEQUENCE: {
- out << '(';
- bool first = true;
- for (value_t::sequence_t::const_iterator i = val.as_sequence().begin();
- i != val.as_sequence().end();
- i++) {
- if (first)
- first = false;
- else
- out << ", ";
- out << *i;
- }
- out << ')';
- break;
- }
-
- default:
- assert(false);
- break;
- }
- return out;
-}
-
-#if 0
-value_context::value_context(const value_t& _bal,
- const string& _desc) throw()
- : error_context(_desc), bal(new value_t(_bal)) {}
-
-value_context::~value_context() throw()
-{
- checked_delete(bal);
-}
-
-void value_context::describe(std::ostream& out) const throw()
-{
- if (! desc.empty())
- out << desc << std::endl;
-
- balance_t * ptr = NULL;
-
- out << std::right;
- out.width(20);
-
- switch (bal->type()) {
- case value_t::BOOLEAN:
- out << (*((bool *) bal->data) ? "true" : "false");
- break;
- case value_t::INTEGER:
- out << *((long *) bal->data);
- break;
- case value_t::DATETIME:
- out << *((moment_t *) bal->data);
- break;
- case value_t::AMOUNT:
- out << *((amount_t *) bal->data);
- break;
- case value_t::BALANCE:
- ptr = (balance_t *) bal->data;
- // fall through...
-
- case value_t::BALANCE_PAIR:
- if (! ptr)
- ptr = &((balance_pair_t *) bal->data)->quantity;
-
- ptr->print(out, 20);
- break;
- default:
- assert(false);
- break;
- }
- out << std::endl;
-}
-#endif
-
} // namespace ledger
diff --git a/src/numerics/value.h b/src/numerics/value.h
index a70bf33e..b175a886 100644
--- a/src/numerics/value.h
+++ b/src/numerics/value.h
@@ -29,6 +29,16 @@
* 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 can also change its
+ * type dynamically based on how it is used.
+ */
#ifndef _VALUE_H
#define _VALUE_H
@@ -40,61 +50,106 @@ namespace xml {
class node_t;
}
-// 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,
- ordered_field_operators<value_t, balance_pair_t,
- ordered_field_operators<value_t, balance_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,
ordered_field_operators<value_t, double,
ordered_field_operators<value_t, unsigned long,
- ordered_field_operators<value_t, long> > > > > > >
+ ordered_field_operators<value_t, long> > > > > > > > > > >
{
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,
- BOOLEAN,
- DATETIME,
- INTEGER,
- AMOUNT,
- BALANCE,
- BALANCE_PAIR,
- STRING,
- SEQUENCE,
- XML_NODE,
- POINTER
+ 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
+ XML_NODE, // a pointer to an ledger::xml::node_t
+ 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, "");
}
- explicit storage_t(const storage_t& rhs)
- : type(rhs.type), refc(0) {
- TRACE_CTOR(value_t::storage_t, "");
- std::memcpy(data, rhs.data, sizeof(data));
- }
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);
@@ -102,15 +157,29 @@ private:
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, "");
+ std::memcpy(data, rhs.data, sizeof(data));
+ }
storage_t& operator=(const storage_t& rhs) {
type = rhs.type;
std::memcpy(data, rhs.data, sizeof(data));
return *this;
}
- mutable int refc;
-
+ /**
+ * 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);
@@ -125,10 +194,6 @@ private:
checked_delete(this);
}
- void destroy();
-
- friend class value_t;
-
friend inline void intrusive_ptr_add_ref(value_t::storage_t * storage) {
storage->acquire();
}
@@ -137,26 +202,65 @@ private:
}
};
+ /**
+ * 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;
- // jww (2007-05-03): Make this private, and then make
- // ledger::initialize into a member function of session_t.
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 value_t& val) {
- TRACE_CTOR(value_t, "copy");
- *this = val;
- }
value_t(const bool val) {
TRACE_CTOR(value_t, "const bool");
set_boolean(val);
@@ -216,10 +320,25 @@ public:
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;
@@ -227,50 +346,63 @@ public:
}
/**
- * _dup() makes a private copy of the current value so that it can
- * subsequently be modified.
+ * Comparison operators.
+ */
+ bool operator==(const value_t& val) const;
+ bool operator<(const value_t& val) const;
+#if 0
+ bool operator>(const value_t& val) const;
+#endif
+
+ /**
+ * Binary arithmetic operators.
*
- * _clear() removes our pointer to the current value and initializes
- * a new value for things to be stored in.
+ * 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.
*/
- void _dup() {
- assert(storage);
- if (storage->refc > 1) {
- storage = new storage_t(*storage.get());
-
- // If the data referenced by storage is an allocated pointer, we
- // need to create a new object in order to achieve duplication.
- switch (storage->type) {
- case BALANCE:
- *(balance_t **) storage->data =
- new balance_t(**(balance_t **) storage->data);
- break;
- case BALANCE_PAIR:
- *(balance_pair_t **) storage->data =
- new balance_pair_t(**(balance_pair_t **) storage->data);
- break;
- case SEQUENCE:
- *(sequence_t **) storage->data =
- new sequence_t(**(sequence_t **) storage->data);
- break;
- default:
- break; // everything else has been duplicated
- }
- }
+ 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);
+
+ 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 _clear() {
- if (! storage || storage->refc > 1)
- storage = new storage_t;
- else
- storage->destroy();
+ void in_place_negate();
+
+ value_t operator-() const {
+ return negate();
}
- void _reset() {
- if (storage)
- storage = intrusive_ptr<storage_t>();
+
+ 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();
+ value_t value(const optional<moment_t>& moment = none) const;
+
+ /**
+ * Truth tests.
+ */
operator bool() const;
+ bool is_realzero() const;
bool is_null() const {
if (! storage) {
return true;
@@ -279,16 +411,17 @@ public:
return false;
}
}
+
type_t type() const {
type_t result = storage ? storage->type : VOID;
assert(result >= VOID && result <= POINTER);
return result;
}
-private:
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) {
@@ -302,6 +435,36 @@ private:
}
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_xml_node()
+ * is_pointer()
+ *
+ * There are corresponding as_*() methods that represent a value as
+ * a reference to its underlying type. For example, as_integer()
+ * 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);
}
@@ -499,6 +662,11 @@ public:
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;
moment_t to_datetime() const;
@@ -508,6 +676,29 @@ public:
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();
@@ -515,6 +706,20 @@ public:
}
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())
@@ -583,17 +788,9 @@ public:
return 1;
}
- 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);
-
- bool operator==(const value_t& val) const;
- bool operator<(const value_t& val) const;
-#if 0
- bool operator>(const value_t& val) const;
-#endif
-
+ /**
+ * Informational methods.
+ */
string label(optional<type_t> the_type = none) const {
switch (the_type ? *the_type : type()) {
case VOID:
@@ -626,52 +823,17 @@ public:
return "<invalid>";
}
- value_t operator-() const {
- return negate();
- }
- value_t negate() const {
- value_t temp = *this;
- temp.in_place_negate();
- return temp;
- }
- void in_place_negate();
-
- bool is_realzero() const;
- value_t abs() const;
- void in_place_cast(type_t cast_type);
value_t cost() const;
- value_t annotated_price() const;
- value_t annotated_date() const;
- value_t annotated_tag() const;
-
- value_t cast(type_t cast_type) const {
- value_t temp(*this);
- temp.in_place_cast(cast_type);
- return temp;
- }
-
- 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 optional<amount_t>& cost = none);
- value_t value(const optional<moment_t>& moment = none) const;
-
- void in_place_reduce();
- value_t reduce() const {
- value_t temp(*this);
- temp.in_place_reduce();
- return temp;
- }
-
- value_t round() const;
- value_t unround() const;
+ /**
+ * Printing methods.
+ */
void print(std::ostream& out, const int first_width,
const int latter_width = -1) const;
- friend std::ostream& operator<<(std::ostream& out, const value_t& val);
+ /**
+ * Debugging methods.
+ */
};
#define NULL_VALUE (value_t())
@@ -680,7 +842,10 @@ inline value_t string_value(const string& str) {
return value_t(str, true);
}
-std::ostream& operator<<(std::ostream& out, const value_t& val);
+inline std::ostream& operator<<(std::ostream& out, const value_t& val) {
+ val.print(out, 12);
+ return out;
+}
DECLARE_EXCEPTION(value_error);