Changed value_t to use boost::any (more type-safe).

This commit is contained in:
John Wiegley 2009-02-10 04:24:06 -04:00
parent 6d4c3ffde5
commit d726624e60
2 changed files with 104 additions and 240 deletions

View file

@ -41,128 +41,30 @@ value_t::storage_t& value_t::storage_t::operator=(const value_t::storage_t& rhs)
type = rhs.type; type = rhs.type;
switch (type) { switch (type) {
case DATETIME:
new(reinterpret_cast<datetime_t *>(data))
datetime_t(*reinterpret_cast<datetime_t *>
(const_cast<char *>(rhs.data)));
break;
case DATE:
new(reinterpret_cast<date_t *>(data))
date_t(*reinterpret_cast<date_t *>(const_cast<char *>(rhs.data)));
break;
case AMOUNT:
new(reinterpret_cast<amount_t *>(data))
amount_t(*reinterpret_cast<amount_t *>
(const_cast<char *>(rhs.data)));
break;
case BALANCE: case BALANCE:
*reinterpret_cast<balance_t **>(data) = data = new balance_t(*boost::get<balance_t *>(rhs.data));
new balance_t(**reinterpret_cast<balance_t **>
(const_cast<char *>(rhs.data)));
break; break;
case STRING:
new(reinterpret_cast<string *>(data))
string(*reinterpret_cast<string *>(const_cast<char *>(rhs.data)));
break;
case MASK:
new(reinterpret_cast<mask_t *>(data))
mask_t(*reinterpret_cast<mask_t *>(const_cast<char *>(rhs.data)));
break;
case SEQUENCE: case SEQUENCE:
*reinterpret_cast<sequence_t **>(data) = data = new sequence_t(*boost::get<sequence_t *>(rhs.data));
new sequence_t(**reinterpret_cast<sequence_t **>
(const_cast<char *>(rhs.data)));
break; break;
default: default:
// The rest are fundamental types, which can be copied using data = rhs.data;
// std::memcpy
std::memcpy(data, rhs.data, sizeof(data));
break; break;
} }
return *this; return *this;
} }
void value_t::storage_t::destroy()
{
switch (type) {
case AMOUNT:
reinterpret_cast<amount_t *>(data)->~amount_t();
break;
case BALANCE:
checked_delete(*reinterpret_cast<balance_t **>(data));
break;
case STRING:
reinterpret_cast<string *>(data)->~string();
break;
case MASK:
reinterpret_cast<mask_t *>(data)->~mask_t();
break;
case SEQUENCE:
checked_delete(*reinterpret_cast<sequence_t **>(data));
break;
case POINTER:
reinterpret_cast<boost::any *>(data)->~any();
break;
default:
break;
}
type = VOID;
}
void value_t::initialize() void value_t::initialize()
{ {
#if defined(DEBUG_ON)
LOGGER("value.init");
#endif
true_value = new storage_t; true_value = new storage_t;
true_value->type = BOOLEAN; true_value->type = BOOLEAN;
*reinterpret_cast<bool *>(true_value->data) = true; boost::get<bool>(true_value->data) = true;
false_value = new storage_t; false_value = new storage_t;
false_value->type = BOOLEAN; false_value->type = BOOLEAN;
*reinterpret_cast<bool *>(false_value->data) = false; boost::get<bool>(true_value->data) = false;
BOOST_STATIC_ASSERT(sizeof(amount_t) >= sizeof(bool));
BOOST_STATIC_ASSERT(sizeof(amount_t) >= sizeof(datetime_t));
BOOST_STATIC_ASSERT(sizeof(amount_t) >= sizeof(date_t));
BOOST_STATIC_ASSERT(sizeof(amount_t) >= sizeof(long));
BOOST_STATIC_ASSERT(sizeof(amount_t) >= sizeof(amount_t));
BOOST_STATIC_ASSERT(sizeof(amount_t) >= sizeof(balance_t *));
BOOST_STATIC_ASSERT(sizeof(amount_t) >= sizeof(string));
BOOST_STATIC_ASSERT(sizeof(amount_t) >= sizeof(mask_t));
BOOST_STATIC_ASSERT(sizeof(amount_t) >= sizeof(sequence_t *));
BOOST_STATIC_ASSERT(sizeof(amount_t) >= sizeof(boost::any));
DEBUG_(std::setw(3) << std::right << sizeof(bool)
<< " sizeof(bool)");
DEBUG_(std::setw(3) << std::right << sizeof(datetime_t)
<< " sizeof(datetime_t)");
DEBUG_(std::setw(3) << std::right << sizeof(date_t)
<< " sizeof(date_t)");
DEBUG_(std::setw(3) << std::right << sizeof(long)
<< " sizeof(long)");
DEBUG_(std::setw(3) << std::right << sizeof(amount_t)
<< " sizeof(amount_t)");
DEBUG_(std::setw(3) << std::right << sizeof(balance_t *)
<< " sizeof(balance_t *)");
DEBUG_(std::setw(3) << std::right << sizeof(string)
<< " sizeof(string)");
DEBUG_(std::setw(3) << std::right << sizeof(mask_t)
<< " sizeof(mask_t)");
DEBUG_(std::setw(3) << std::right << sizeof(sequence_t *)
<< " sizeof(sequence_t *)");
DEBUG_(std::setw(3) << std::right << sizeof(boost::any)
<< " sizeof(boost::any)");
} }
void value_t::shutdown() void value_t::shutdown()
@ -171,13 +73,6 @@ void value_t::shutdown()
false_value = intrusive_ptr<storage_t>(); false_value = intrusive_ptr<storage_t>();
} }
void value_t::_dup()
{
assert(storage);
if (storage->refc > 1)
storage = new storage_t(*storage.get());
}
value_t::operator bool() const value_t::operator bool() const
{ {
switch (type()) { switch (type()) {
@ -1263,69 +1158,33 @@ value_t value_t::unrounded() const
return NULL_VALUE; return NULL_VALUE;
} }
#if 0 void value_t::annotate(const annotation_t& details)
value_t value_t::annotated_price() const
{ {
switch (type()) { if (is_amount())
case AMOUNT: { as_amount_lval().annotate(details);
optional<amount_t> temp = as_amount().annotation_details().price; else
if (! temp) throw_(value_error, "Cannot annotate " << label());
return false;
return *temp;
}
default:
break;
}
throw_(value_error, "Cannot find the annotated price of " << label());
return NULL_VALUE;
} }
value_t value_t::annotated_date() const bool value_t::is_annotated() const
{ {
switch (type()) { if (is_amount())
case DATETIME: return as_amount().is_annotated();
return *this; else
case DATE: throw_(value_error,
return *this; "Cannot determine whether " << label() << " is annotated");
return false;
case AMOUNT: {
optional<datetime_t> temp = as_amount().annotation_details().date;
if (! temp)
return false;
return *temp;
}
default:
break;
}
throw_(value_error, "Cannot find the annotated date of " << label());
return NULL_VALUE;
} }
value_t value_t::annotated_tag() const annotation_t& value_t::annotation()
{ {
switch (type()) { if (is_amount())
case AMOUNT: { return as_amount_lval().annotation();
optional<string> temp = as_amount().annotation_details().tag; else {
if (! temp) throw_(value_error, "Cannot request annotation of " << label());
return false; return as_amount_lval().annotation(); // quiet g++ warning
return string_value(*temp);
} }
case STRING:
return *this;
default:
break;
}
throw_(value_error, "Cannot find the annotated tag of " << label());
return NULL_VALUE;
} }
#endif
value_t value_t::strip_annotations(const keep_details_t& what_to_keep) const value_t value_t::strip_annotations(const keep_details_t& what_to_keep) const
{ {

View file

@ -125,7 +125,18 @@ private:
* The `type' member holds the value_t::type_t value representing * The `type' member holds the value_t::type_t value representing
* the type of the object stored. * the type of the object stored.
*/ */
char data[sizeof(amount_t)]; variant<bool, // BOOLEAN
datetime_t, // DATETIME
date_t, // DATE
long, // INTEGER
amount_t, // AMOUNT
balance_t *, // BALANCE
string, // STRING
mask_t, // MASK
sequence_t *, // SEQUENCE
boost::any // POINTER
> data;
type_t type; type_t type;
/** /**
@ -157,10 +168,18 @@ private:
TRACE_DTOR(value_t::storage_t); TRACE_DTOR(value_t::storage_t);
DEBUG("value.storage.refcount", "Destroying " << this); DEBUG("value.storage.refcount", "Destroying " << this);
assert(refc == 0); assert(refc == 0);
destroy();
}
void destroy(); switch (type) {
case BALANCE:
checked_delete(boost::get<balance_t *>(data));
break;
case SEQUENCE:
checked_delete(boost::get<sequence_t *>(data));
break;
default:
break;
}
}
private: private:
/** /**
@ -202,7 +221,7 @@ private:
}; };
/** /**
* The actual data for each value_t is kept in the `storage' member. * The actual data for each value_t is kept in reference counted storage.
* Data is modified using a copy-on-write policy. * Data is modified using a copy-on-write policy.
*/ */
intrusive_ptr<storage_t> storage; intrusive_ptr<storage_t> storage;
@ -213,20 +232,19 @@ private:
* *
* _clear() removes our pointer to the current value and initializes * _clear() removes our pointer to the current value and initializes
* a new storage bin for things to be stored in. * a new storage bin for things to be stored in.
*
* _reset() makes the current object appear as if it were
* uninitialized.
*/ */
void _dup(); void _dup() {
void _clear() { assert(storage);
if (! storage || storage->refc > 1) if (storage->refc > 1)
storage = new storage_t; storage = new storage_t(*storage.get());
else
storage->destroy();
} }
void _reset() { void _clear() {
if (storage) if (! storage || storage->refc > 1) {
storage = intrusive_ptr<storage_t>(); storage = new storage_t;
} else {
storage->data = false; // destruct any other type
storage->type = VOID;
}
} }
/** /**
@ -455,7 +473,7 @@ private:
void set_type(type_t new_type) { void set_type(type_t new_type) {
assert(new_type >= VOID && new_type <= POINTER); assert(new_type >= VOID && new_type <= POINTER);
if (new_type == VOID) { if (new_type == VOID) {
_reset(); storage.reset();
assert(is_null()); assert(is_null());
} else { } else {
_clear(); _clear();
@ -500,11 +518,11 @@ public:
bool& as_boolean_lval() { bool& as_boolean_lval() {
assert(is_boolean()); assert(is_boolean());
_dup(); _dup();
return *reinterpret_cast<bool *>(storage->data); return boost::get<bool>(storage->data);
} }
const bool& as_boolean() const { const bool& as_boolean() const {
assert(is_boolean()); assert(is_boolean());
return *reinterpret_cast<bool *>(storage->data); return boost::get<bool>(storage->data);
} }
void set_boolean(const bool val) { void set_boolean(const bool val) {
set_type(BOOLEAN); set_type(BOOLEAN);
@ -517,15 +535,15 @@ public:
datetime_t& as_datetime_lval() { datetime_t& as_datetime_lval() {
assert(is_datetime()); assert(is_datetime());
_dup(); _dup();
return *reinterpret_cast<datetime_t *>(storage->data); return boost::get<datetime_t>(storage->data);
} }
const datetime_t& as_datetime() const { const datetime_t& as_datetime() const {
assert(is_datetime()); assert(is_datetime());
return *reinterpret_cast<datetime_t *>(storage->data); return boost::get<datetime_t>(storage->data);
} }
void set_datetime(const datetime_t& val) { void set_datetime(const datetime_t& val) {
set_type(DATETIME); set_type(DATETIME);
new(reinterpret_cast<datetime_t *>(storage->data)) datetime_t(val); storage->data = val;
} }
bool is_date() const { bool is_date() const {
@ -534,15 +552,15 @@ public:
date_t& as_date_lval() { date_t& as_date_lval() {
assert(is_date()); assert(is_date());
_dup(); _dup();
return *reinterpret_cast<date_t *>(storage->data); return boost::get<date_t>(storage->data);
} }
const date_t& as_date() const { const date_t& as_date() const {
assert(is_date()); assert(is_date());
return *reinterpret_cast<date_t *>(storage->data); return boost::get<date_t>(storage->data);
} }
void set_date(const date_t& val) { void set_date(const date_t& val) {
set_type(DATE); set_type(DATE);
new(reinterpret_cast<date_t *>(storage->data)) date_t(val); storage->data = val;
} }
bool is_long() const { bool is_long() const {
@ -551,15 +569,15 @@ public:
long& as_long_lval() { long& as_long_lval() {
assert(is_long()); assert(is_long());
_dup(); _dup();
return *reinterpret_cast<long *>(storage->data); return boost::get<long>(storage->data);
} }
const long& as_long() const { const long& as_long() const {
assert(is_long()); assert(is_long());
return *reinterpret_cast<long *>(storage->data); return boost::get<long>(storage->data);
} }
void set_long(const long val) { void set_long(const long val) {
set_type(INTEGER); set_type(INTEGER);
*reinterpret_cast<long *>(storage->data) = val; storage->data = val;
} }
bool is_amount() const { bool is_amount() const {
@ -568,20 +586,16 @@ public:
amount_t& as_amount_lval() { amount_t& as_amount_lval() {
assert(is_amount()); assert(is_amount());
_dup(); _dup();
amount_t& amt(*reinterpret_cast<amount_t *>(storage->data)); return boost::get<amount_t>(storage->data);
assert(amt.valid());
return amt;
} }
const amount_t& as_amount() const { const amount_t& as_amount() const {
assert(is_amount()); assert(is_amount());
amount_t& amt(*reinterpret_cast<amount_t *>(storage->data)); return boost::get<amount_t>(storage->data);
assert(amt.valid());
return amt;
} }
void set_amount(const amount_t& val) { void set_amount(const amount_t& val) {
assert(val.valid()); assert(val.valid());
set_type(AMOUNT); set_type(AMOUNT);
new(reinterpret_cast<amount_t *>(storage->data)) amount_t(val); storage->data = val;
} }
bool is_balance() const { bool is_balance() const {
@ -590,20 +604,16 @@ public:
balance_t& as_balance_lval() { balance_t& as_balance_lval() {
assert(is_balance()); assert(is_balance());
_dup(); _dup();
balance_t& bal(**reinterpret_cast<balance_t **>(storage->data)); return *boost::get<balance_t *>(storage->data);
assert(bal.valid());
return bal;
} }
const balance_t& as_balance() const { const balance_t& as_balance() const {
assert(is_balance()); assert(is_balance());
balance_t& bal(**reinterpret_cast<balance_t **>(storage->data)); return *boost::get<balance_t *>(storage->data);
assert(bal.valid());
return bal;
} }
void set_balance(const balance_t& val) { void set_balance(const balance_t& val) {
assert(val.valid()); assert(val.valid());
set_type(BALANCE); set_type(BALANCE);
*reinterpret_cast<balance_t **>(storage->data) = new balance_t(val); storage->data = new balance_t(val);
} }
bool is_string() const { bool is_string() const {
@ -612,19 +622,19 @@ public:
string& as_string_lval() { string& as_string_lval() {
assert(is_string()); assert(is_string());
_dup(); _dup();
return *reinterpret_cast<string *>(storage->data); return boost::get<string>(storage->data);
} }
const string& as_string() const { const string& as_string() const {
assert(is_string()); assert(is_string());
return *reinterpret_cast<string *>(storage->data); return boost::get<string>(storage->data);
} }
void set_string(const string& val = "") { void set_string(const string& val = "") {
set_type(STRING); set_type(STRING);
new(reinterpret_cast<string *>(storage->data)) string(val); storage->data = val;
} }
void set_string(const char * val = "") { void set_string(const char * val = "") {
set_type(STRING); set_type(STRING);
new(reinterpret_cast<string *>(storage->data)) string(val); storage->data = string(val);
} }
bool is_mask() const { bool is_mask() const {
@ -633,19 +643,19 @@ public:
mask_t& as_mask_lval() { mask_t& as_mask_lval() {
assert(is_mask()); assert(is_mask());
_dup(); _dup();
return *reinterpret_cast<mask_t *>(storage->data); return boost::get<mask_t>(storage->data);
} }
const mask_t& as_mask() const { const mask_t& as_mask() const {
assert(is_mask()); assert(is_mask());
return *reinterpret_cast<mask_t *>(storage->data); return boost::get<mask_t>(storage->data);
} }
void set_mask(const string& val) { void set_mask(const string& val) {
set_type(MASK); set_type(MASK);
new(reinterpret_cast<mask_t *>(storage->data)) mask_t(val); storage->data = mask_t(val);
} }
void set_mask(const mask_t& val) { void set_mask(const mask_t& val) {
set_type(MASK); set_type(MASK);
new(reinterpret_cast<mask_t *>(storage->data)) mask_t(val); storage->data = val;
} }
bool is_sequence() const { bool is_sequence() const {
@ -654,15 +664,15 @@ public:
sequence_t& as_sequence_lval() { sequence_t& as_sequence_lval() {
assert(is_sequence()); assert(is_sequence());
_dup(); _dup();
return **reinterpret_cast<sequence_t **>(storage->data); return *boost::get<sequence_t *>(storage->data);
} }
const sequence_t& as_sequence() const { const sequence_t& as_sequence() const {
assert(is_sequence()); assert(is_sequence());
return **reinterpret_cast<sequence_t **>(storage->data); return *boost::get<sequence_t *>(storage->data);
} }
void set_sequence(const sequence_t& val) { void set_sequence(const sequence_t& val) {
set_type(SEQUENCE); set_type(SEQUENCE);
*reinterpret_cast<sequence_t **>(storage->data) = new sequence_t(val); storage->data = new sequence_t(val);
} }
/** /**
@ -679,42 +689,36 @@ public:
boost::any& as_any_pointer_lval() { boost::any& as_any_pointer_lval() {
assert(is_pointer()); assert(is_pointer());
_dup(); _dup();
return *reinterpret_cast<boost::any *>(storage->data); return boost::get<boost::any>(storage->data);
} }
template <typename T> template <typename T>
T * as_pointer_lval() { T * as_pointer_lval() {
assert(is_pointer()); return any_cast<T *>(as_any_pointer_lval());
_dup();
return any_cast<T *>(*reinterpret_cast<boost::any *>(storage->data));
} }
template <typename T> template <typename T>
T& as_ref_lval() { T& as_ref_lval() {
assert(is_pointer()); return *as_pointer_lval<T>();
_dup();
return *any_cast<T *>(*reinterpret_cast<boost::any *>(storage->data));
} }
const boost::any& as_any_pointer() const { const boost::any& as_any_pointer() const {
assert(is_pointer()); assert(is_pointer());
return *reinterpret_cast<boost::any *>(storage->data); return boost::get<boost::any>(storage->data);
} }
template <typename T> template <typename T>
T * as_pointer() const { T * as_pointer() const {
assert(is_pointer()); return any_cast<T *>(as_any_pointer());
return any_cast<T *>(*reinterpret_cast<boost::any *>(storage->data));
} }
template <typename T> template <typename T>
T& as_ref() const { T& as_ref() const {
assert(is_pointer()); return *as_pointer<T>();
return *any_cast<T *>(*reinterpret_cast<boost::any *>(storage->data));
} }
void set_any_pointer(const boost::any& val) { void set_any_pointer(const boost::any& val) {
set_type(POINTER); set_type(POINTER);
new(reinterpret_cast<boost::any *>(storage->data)) boost::any(val); storage->data = val;
} }
template <typename T> template <typename T>
void set_pointer(T * val) { void set_pointer(T * val) {
set_type(POINTER); set_type(POINTER);
new(reinterpret_cast<boost::any *>(storage->data)) boost::any(val); storage->data = boost::any(val);
} }
/** /**
@ -765,12 +769,13 @@ public:
/** /**
* Annotated commodity methods. * Annotated commodity methods.
*/ */
#if 0 void annotate(const annotation_t& details);
// These helper methods only apply to AMOUNT values. bool is_annotated() const;
value_t annotated_price() const;
value_t annotated_date() const; annotation_t& annotation();
value_t annotated_tag() const; const annotation_t& annotation() const {
#endif return const_cast<value_t&>(*this).annotation();
}
value_t strip_annotations(const keep_details_t& what_to_keep) const; value_t strip_annotations(const keep_details_t& what_to_keep) const;
@ -821,14 +826,14 @@ public:
assert(! is_null()); assert(! is_null());
if (! is_sequence()) { if (! is_sequence()) {
_reset(); storage.reset();
} else { } else {
as_sequence_lval().pop_back(); as_sequence_lval().pop_back();
const sequence_t& seq(as_sequence()); const sequence_t& seq(as_sequence());
std::size_t new_size = seq.size(); std::size_t new_size = seq.size();
if (new_size == 0) if (new_size == 0)
_reset(); storage.reset();
else if (new_size == 1) else if (new_size == 1)
*this = seq.front(); *this = seq.front();
} }