565 lines
13 KiB
C++
565 lines
13 KiB
C++
#ifndef _VALUE_H
|
|
#define _VALUE_H
|
|
|
|
#include "amount.h"
|
|
#include "balance.h"
|
|
#include "error.h"
|
|
|
|
#include <deque>
|
|
#include <exception>
|
|
|
|
namespace ledger {
|
|
|
|
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
|
|
{
|
|
public:
|
|
typedef std::deque<value_t> sequence_t;
|
|
|
|
char data[sizeof(balance_pair_t)];
|
|
|
|
enum type_t {
|
|
BOOLEAN,
|
|
INTEGER,
|
|
DATETIME,
|
|
AMOUNT,
|
|
BALANCE,
|
|
BALANCE_PAIR,
|
|
STRING,
|
|
XML_NODE,
|
|
POINTER,
|
|
SEQUENCE
|
|
} type;
|
|
|
|
value_t() {
|
|
TRACE_CTOR("value_t()");
|
|
*((long *) data) = 0;
|
|
type = INTEGER;
|
|
}
|
|
|
|
value_t(const value_t& val) : type(INTEGER) {
|
|
TRACE_CTOR("value_t(copy)");
|
|
*this = val;
|
|
}
|
|
value_t(const bool val) {
|
|
TRACE_CTOR("value_t(const bool)");
|
|
*((bool *) data) = val;
|
|
type = BOOLEAN;
|
|
}
|
|
value_t(const long val) {
|
|
TRACE_CTOR("value_t(const long)");
|
|
*((long *) data) = val;
|
|
type = INTEGER;
|
|
}
|
|
value_t(const datetime_t val) {
|
|
TRACE_CTOR("value_t(const datetime_t)");
|
|
*((datetime_t *) data) = val;
|
|
type = DATETIME;
|
|
}
|
|
value_t(const unsigned long val) {
|
|
TRACE_CTOR("value_t(const unsigned long)");
|
|
new((amount_t *) data) amount_t(val);
|
|
type = AMOUNT;
|
|
}
|
|
value_t(const double val) {
|
|
TRACE_CTOR("value_t(const double)");
|
|
new((amount_t *) data) amount_t(val);
|
|
type = AMOUNT;
|
|
}
|
|
value_t(const std::string& val, bool literal = false) {
|
|
TRACE_CTOR("value_t(const std::string&, bool)");
|
|
if (literal) {
|
|
type = INTEGER;
|
|
set_string(val);
|
|
} else {
|
|
new((amount_t *) data) amount_t(val);
|
|
type = AMOUNT;
|
|
}
|
|
}
|
|
value_t(const char * val) {
|
|
TRACE_CTOR("value_t(const char *)");
|
|
new((amount_t *) data) amount_t(val);
|
|
type = AMOUNT;
|
|
}
|
|
value_t(const amount_t& val) {
|
|
TRACE_CTOR("value_t(const amount_t&)");
|
|
new((amount_t *)data) amount_t(val);
|
|
type = AMOUNT;
|
|
}
|
|
value_t(const balance_t& val) : type(INTEGER) {
|
|
TRACE_CTOR("value_t(const balance_t&)");
|
|
*this = val;
|
|
}
|
|
value_t(const balance_pair_t& val) : type(INTEGER) {
|
|
TRACE_CTOR("value_t(const balance_pair_t&)");
|
|
*this = val;
|
|
}
|
|
value_t(xml::node_t * xml_node) : type(INTEGER) { // gets set in =
|
|
TRACE_CTOR("value_t(xml::node_t *)");
|
|
*this = xml_node;
|
|
}
|
|
value_t(void * item) : type(INTEGER) { // gets set in =
|
|
TRACE_CTOR("value_t(void *)");
|
|
*this = item;
|
|
}
|
|
value_t(sequence_t * seq) : type(INTEGER) { // gets set in =
|
|
TRACE_CTOR("value_t(sequence_t *)");
|
|
*this = seq;
|
|
}
|
|
|
|
~value_t() {
|
|
TRACE_DTOR("value_t");
|
|
destroy();
|
|
}
|
|
|
|
void destroy();
|
|
void simplify();
|
|
|
|
value_t& operator=(const value_t& val);
|
|
value_t& operator=(const bool val) {
|
|
if ((bool *) data != &val) {
|
|
destroy();
|
|
*((bool *) data) = val;
|
|
type = BOOLEAN;
|
|
}
|
|
return *this;
|
|
}
|
|
value_t& operator=(const long val) {
|
|
if ((long *) data != &val) {
|
|
destroy();
|
|
*((long *) data) = val;
|
|
type = INTEGER;
|
|
}
|
|
return *this;
|
|
}
|
|
value_t& operator=(const datetime_t val) {
|
|
if ((datetime_t *) data != &val) {
|
|
destroy();
|
|
*((datetime_t *) data) = val;
|
|
type = DATETIME;
|
|
}
|
|
return *this;
|
|
}
|
|
value_t& operator=(const unsigned long val) {
|
|
return *this = amount_t(val);
|
|
}
|
|
value_t& operator=(const double val) {
|
|
return *this = amount_t(val);
|
|
}
|
|
value_t& operator=(const std::string& val) {
|
|
return *this = amount_t(val);
|
|
}
|
|
value_t& operator=(const char * val) {
|
|
return *this = amount_t(val);
|
|
}
|
|
value_t& operator=(const amount_t& val) {
|
|
if (type == AMOUNT &&
|
|
(amount_t *) data == &val)
|
|
return *this;
|
|
|
|
if (val.realzero()) {
|
|
return *this = 0L;
|
|
} else {
|
|
destroy();
|
|
new((amount_t *)data) amount_t(val);
|
|
type = AMOUNT;
|
|
}
|
|
return *this;
|
|
}
|
|
value_t& operator=(const balance_t& val) {
|
|
if (type == BALANCE &&
|
|
(balance_t *) data == &val)
|
|
return *this;
|
|
|
|
if (val.realzero()) {
|
|
return *this = 0L;
|
|
}
|
|
else if (val.amounts.size() == 1) {
|
|
return *this = (*val.amounts.begin()).second;
|
|
}
|
|
else {
|
|
destroy();
|
|
new((balance_t *)data) balance_t(val);
|
|
type = BALANCE;
|
|
return *this;
|
|
}
|
|
}
|
|
value_t& operator=(const balance_pair_t& val) {
|
|
if (type == BALANCE_PAIR &&
|
|
(balance_pair_t *) data == &val)
|
|
return *this;
|
|
|
|
if (val.realzero()) {
|
|
return *this = 0L;
|
|
}
|
|
else if (! val.cost) {
|
|
return *this = val.quantity;
|
|
}
|
|
else {
|
|
destroy();
|
|
new((balance_pair_t *)data) balance_pair_t(val);
|
|
type = BALANCE_PAIR;
|
|
return *this;
|
|
}
|
|
}
|
|
value_t& operator=(xml::node_t * xml_node) {
|
|
assert(xml_node);
|
|
if (type == XML_NODE && *(xml::node_t **) data == xml_node)
|
|
return *this;
|
|
|
|
if (! xml_node) {
|
|
type = XML_NODE;
|
|
return *this = 0L;
|
|
}
|
|
else {
|
|
destroy();
|
|
*(xml::node_t **)data = xml_node;
|
|
type = XML_NODE;
|
|
return *this;
|
|
}
|
|
}
|
|
value_t& operator=(void * item) {
|
|
assert(item);
|
|
if (type == POINTER && *(void **) data == item)
|
|
return *this;
|
|
|
|
if (! item) {
|
|
type = POINTER;
|
|
return *this = 0L;
|
|
}
|
|
else {
|
|
destroy();
|
|
*(void **)data = item;
|
|
type = POINTER;
|
|
return *this;
|
|
}
|
|
}
|
|
value_t& operator=(sequence_t * seq) {
|
|
assert(seq);
|
|
if (type == SEQUENCE && *(sequence_t **) data == seq)
|
|
return *this;
|
|
|
|
if (! seq) {
|
|
type = SEQUENCE;
|
|
return *this = 0L;
|
|
}
|
|
else {
|
|
destroy();
|
|
*(sequence_t **)data = seq;
|
|
type = SEQUENCE;
|
|
return *this;
|
|
}
|
|
}
|
|
|
|
value_t& set_string(const std::string& str = "") {
|
|
if (type != STRING) {
|
|
destroy();
|
|
*(std::string **) data = new std::string(str);
|
|
type = STRING;
|
|
} else {
|
|
**(std::string **) data = str;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
bool to_boolean() const;
|
|
long to_integer() const;
|
|
datetime_t to_datetime() const;
|
|
amount_t to_amount() const;
|
|
balance_t to_balance() const;
|
|
balance_pair_t to_balance_pair() const;
|
|
std::string to_string() const;
|
|
xml::node_t * to_xml_node() const;
|
|
void * to_pointer() const;
|
|
sequence_t * to_sequence() const;
|
|
|
|
value_t& operator[](const int index) {
|
|
sequence_t * seq = to_sequence();
|
|
assert(seq);
|
|
return (*seq)[index];
|
|
}
|
|
|
|
void push_back(const value_t& val) {
|
|
sequence_t * seq = to_sequence();
|
|
assert(seq);
|
|
return seq->push_back(val);
|
|
}
|
|
|
|
std::size_t size() const {
|
|
sequence_t * seq = to_sequence();
|
|
assert(seq);
|
|
return seq->size();
|
|
}
|
|
|
|
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);
|
|
|
|
template <typename T>
|
|
value_t& operator+=(const T& val) {
|
|
return *this += value_t(val);
|
|
}
|
|
template <typename T>
|
|
value_t& operator-=(const T& val) {
|
|
return *this -= value_t(val);
|
|
}
|
|
template <typename T>
|
|
value_t& operator*=(const T& val) {
|
|
return *this *= value_t(val);
|
|
}
|
|
template <typename T>
|
|
value_t& operator/=(const T& val) {
|
|
return *this /= value_t(val);
|
|
}
|
|
|
|
value_t operator+(const value_t& val) {
|
|
value_t temp(*this);
|
|
temp += val;
|
|
return temp;
|
|
}
|
|
value_t operator-(const value_t& val) {
|
|
value_t temp(*this);
|
|
temp -= val;
|
|
return temp;
|
|
}
|
|
value_t operator*(const value_t& val) {
|
|
value_t temp(*this);
|
|
temp *= val;
|
|
return temp;
|
|
}
|
|
value_t operator/(const value_t& val) {
|
|
value_t temp(*this);
|
|
temp /= val;
|
|
return temp;
|
|
}
|
|
|
|
template <typename T>
|
|
value_t operator+(const T& val) {
|
|
return *this + value_t(val);
|
|
}
|
|
template <typename T>
|
|
value_t operator-(const T& val) {
|
|
return *this - value_t(val);
|
|
}
|
|
template <typename T>
|
|
value_t operator*(const T& val) {
|
|
return *this * value_t(val);
|
|
}
|
|
template <typename T>
|
|
value_t operator/(const T& val) {
|
|
return *this / value_t(val);
|
|
}
|
|
|
|
bool operator<(const value_t& val);
|
|
bool operator<=(const value_t& val);
|
|
bool operator>(const value_t& val);
|
|
bool operator>=(const value_t& val);
|
|
bool operator==(const value_t& val);
|
|
bool operator!=(const value_t& val) {
|
|
return ! (*this == val);
|
|
}
|
|
|
|
template <typename T>
|
|
bool operator<(const T& val) {
|
|
return *this < value_t(val);
|
|
}
|
|
template <typename T>
|
|
bool operator<=(const T& val) {
|
|
return *this <= value_t(val);
|
|
}
|
|
template <typename T>
|
|
bool operator>(const T& val) {
|
|
return *this > value_t(val);
|
|
}
|
|
template <typename T>
|
|
bool operator>=(const T& val) {
|
|
return *this >= value_t(val);
|
|
}
|
|
template <typename T>
|
|
bool operator==(const T& val) {
|
|
return *this == value_t(val);
|
|
}
|
|
template <typename T>
|
|
bool operator!=(const T& val) {
|
|
return ! (*this == val);
|
|
}
|
|
|
|
template <typename T>
|
|
operator T() const;
|
|
|
|
void negate();
|
|
value_t negated() const {
|
|
value_t temp = *this;
|
|
temp.negate();
|
|
return temp;
|
|
}
|
|
value_t operator-() const {
|
|
return negated();
|
|
}
|
|
|
|
bool realzero() const {
|
|
switch (type) {
|
|
case BOOLEAN:
|
|
return ! *((bool *) data);
|
|
case INTEGER:
|
|
return *((long *) data) == 0;
|
|
case DATETIME:
|
|
return ! *((datetime_t *) data);
|
|
case AMOUNT:
|
|
return ((amount_t *) data)->realzero();
|
|
case BALANCE:
|
|
return ((balance_t *) data)->realzero();
|
|
case BALANCE_PAIR:
|
|
return ((balance_pair_t *) data)->realzero();
|
|
case STRING:
|
|
return ((std::string *) data)->empty();
|
|
case XML_NODE:
|
|
case POINTER:
|
|
case SEQUENCE:
|
|
return *(void **) data == NULL;
|
|
|
|
default:
|
|
assert(0);
|
|
break;
|
|
}
|
|
assert(0);
|
|
return 0;
|
|
}
|
|
|
|
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();
|
|
|
|
value_t reduced() const {
|
|
value_t temp(*this);
|
|
temp.reduce();
|
|
return temp;
|
|
}
|
|
|
|
void round();
|
|
value_t unround() const;
|
|
|
|
void write(std::ostream& out, const int first_width,
|
|
const int latter_width = -1) const;
|
|
};
|
|
|
|
#define DEF_VALUE_AUX_OP(OP) \
|
|
inline value_t operator OP(const balance_pair_t& val, \
|
|
const value_t& obj) { \
|
|
return value_t(val) OP obj; \
|
|
} \
|
|
inline value_t operator OP(const balance_t& val, \
|
|
const value_t& obj) { \
|
|
return value_t(val) OP obj; \
|
|
} \
|
|
inline value_t operator OP(const amount_t& val, \
|
|
const value_t& obj) { \
|
|
return value_t(val) OP obj; \
|
|
} \
|
|
template <typename T> \
|
|
inline value_t operator OP(T val, const value_t& obj) { \
|
|
return value_t(val) 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 STRING:
|
|
return **(std::string **) data;
|
|
case XML_NODE:
|
|
return *(xml::node_t **) data;
|
|
case POINTER:
|
|
return *(void **) data;
|
|
case SEQUENCE:
|
|
return *(sequence_t **) data;
|
|
|
|
default:
|
|
assert(0);
|
|
break;
|
|
}
|
|
assert(0);
|
|
return 0;
|
|
}
|
|
|
|
template <> value_t::operator bool() const;
|
|
template <> value_t::operator long() const;
|
|
template <> value_t::operator datetime_t() const;
|
|
template <> value_t::operator double() const;
|
|
template <> value_t::operator std::string() const;
|
|
|
|
inline value_t abs(const value_t& val) {
|
|
value_t temp(val);
|
|
temp.abs();
|
|
return temp;
|
|
}
|
|
|
|
std::ostream& operator<<(std::ostream& out, const value_t& val);
|
|
|
|
class value_context : public error_context
|
|
{
|
|
value_t * bal;
|
|
public:
|
|
value_context(const value_t& _bal,
|
|
const std::string& desc = "") throw();
|
|
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() {}
|
|
};
|
|
|
|
} // namespace ledger
|
|
|
|
#endif // _VALUE_H
|