xo-umbrella2/xo-object/include/xo/object/object.hpp
Roland Conybeare 5d49dc5a9b git subrepo clone git@github.com:Rconybea/xo-object.git xo-object
subrepo:
  subdir:   "xo-object"
  merged:   "d830632a"
upstream:
  origin:   "git@github.com:Rconybea/xo-object.git"
  branch:   "main"
  commit:   "d830632a"
git-subrepo:
  version:  "0.4.9"
  origin:   "???"
  commit:   "???"
2026-06-06 22:14:14 -04:00

205 lines
7 KiB
C++

/** @file object.hpp
*
* Author: Roland Conybeare
**/
#pragma once
#include "xo/reflect/Object.hpp"
#include <cstdint>
namespace xo {
namespace obj {
enum class otag : std::uint16_t {
ot_invalid = static_cast<uint16_t>(-1),
ot_sentinel = 0x00,
ot_boolean = 0x01,
ot_char = 0x02,
ot_i32 = 0x03,
ot_f32 = 0x04,
/* 0x05, 0x06, 0x07 reserved */
ot_i64 = 0x08,
ot_f64 = 0x09,
ot_zstring = 0x0a,
ot_symbol = 0x0b,
ot_cons = 0x0c,
/* 0x0d, 0x0e reserved */
ot_rc_object = 0x10, /* refcounted pointer to xo::reflect::Object */
};
class cons;
class zstring;
class symbol;
/** @class object
*
* @brief dynamically tyyped object; support for schematica
**/
class object {
public:
using Object = xo::reflect::Object;
/** tags involving pointers use values 0x08 .. 0x10 **/
static constexpr uint16_t c_tag_pointer_mask = 0xfff8;
/** number of pointer bits stolen for type tag **/
static constexpr int c_tag_bits = 16;
/** number of pointer bits remaining after hi tag bits stolen **/
static constexpr int c_ptr_bits = 64 - c_tag_bits;
static constexpr std::uint64_t c_tag_mask = (0UL - 1) << c_ptr_bits;
static constexpr std::uint64_t c_ptr_mask = (0UL - 1) >> c_tag_bits;
public:
object() = default;
object(const object & x);
otag tag() const noexcept { return (static_cast<otag>(value_ >> c_ptr_bits)); }
std::uint64_t masked_value() const noexcept { return value_ & c_ptr_mask; }
std::int32_t as_int32(std::int32_t sentinel = 0) const noexcept {
if (tag() == otag::ot_i32) {
return masked_value();
} else {
return sentinel;
}
}
float as_float32(float sentinel = std::numeric_limits<float>::quiet_NaN()) const noexcept {
if (tag() == otag::ot_f64) {
std::uint32_t bits = masked_value();
return * reinterpret_cast<float *>(&bits);
} else {
return sentinel;
}
}
std::int64_t as_int64(int64_t sentinel = 0) const noexcept {
if (tag() == otag::ot_i64) {
return cast_int64();
} else {
return sentinel;
}
}
double as_float64(double sentinel = std::numeric_limits<double>::quiet_NaN()) const noexcept {
if (tag() == otag::ot_f64) {
return * reinterpret_cast<double *>(masked_value());
} else {
return sentinel;
}
}
zstring * as_zstring() const noexcept {
if (tag() == otag::ot_zstring) {
return reinterpret_cast<zstring *>(masked_value());
} else {
return nullptr;
}
}
Object * as_object() const noexcept {
if (tag() == otag::ot_rc_object) {
return reinterpret_cast<Object *>(masked_value());
} else {
return nullptr;
}
}
private:
/** ctor. only use least-significant c_ptr_bits (48) bits from value **/
explicit object(otag tag, std::uint64_t value) {
this->set_tag_value(tag, value);
}
/** undefined behavior if tag != ot_i64 **/
std::int64_t cast_int64() const noexcept {
return * reinterpret_cast<std::int64_t *>(masked_value());
}
/** undefined behavior if tag != ot_f64 **/
double cast_double() const noexcept {
return * reinterpret_cast<double *>(masked_value());
}
zstring * cast_zstring() const noexcept {
return reinterpret_cast<zstring *>(masked_value());
}
symbol * cast_symbol() const noexcept {
return reinterpret_cast<symbol *>(masked_value());
}
Object * cast_object() const noexcept {
return reinterpret_cast<Object *>(masked_value());
}
/** only use bottom c_ptr_bits (48) from value **/
void set_tag_value(otag tag, std::uint64_t value) {
value_ = (static_cast<std::uint16_t>(tag) | (value & c_ptr_mask));
}
private:
/**
* Rely on being able to steal the 16 most-significant
* bits from a 64-bit pointer
*
* @code
*
* - ot_i64, ot_f64, ot_zstring, ot_symbol, ot_cons, ot_rc_object:
*
* <- 16 -> <---------- 48 ---------->
* +--------+--------------------------+
* | tag | ptr |
* +--------+--------------------------+
*
* - ot_f32:
*
* <- 16 -> <- 16 -> <------ 32 ----->
* +--------+--------+-----------------+
* | ot_f32 | unused | 32-bit float |
* +--------+--------+-----------------+
*
* - ot_boolean
*
* <- 16 -> <-------- 47 ----------> 1
* +--------+------------------------+-+
* | ot_f32 | unused |b|
* +--------+------------------------+-+
*
* etc...
*
* @endcode
*
* tag values given by @ref otag
*
* 0x00 -> sentinel. sentinel value. not accessible from schematica
* 0x01 -> boolean. truth value in least-significant bit
* 0x02 -> char. ascii character in least-significant 8 bits
* 0x03 -> int. integer in least-significant 32 bits
* 0x04 -> float. 32-bit float in least-significant 32 bitsg
*
* 0x05..0x07 reserved
*
* 0x08 -> long. ptr refers to 64-bit integer
* 0x09 -> double. ptr refers to 64-bit floating-point value
* 0x0a -> zstring. ptr refers to length-prefixed null-terminated cstring.
* 0x0b -> symbol. ptr refers to interned (unique'd) symbol
* 0x0c -> cons. ptr refers to 128-bit cons-cell (or is nullptr, representing nil)
*
* 0x0d..0x0f reserved
*
* 0x10 -> Object. ptr to refcounted xo::obj::Object
* see xo-reflect/ Object.hpp
**/
std::uint64_t value_ = 0;
};
} /*namespace obj*/
} /*namespace xo*/
/** end object.hpp **/