6#include <rapidjson/prettywriter.h> 
   12Writ::InstanceMap::wrapper_to_json (Wrapper *wrapper, 
const size_t thisid, Jsonipc::JsonAllocator &allocator)
 
   14  warning (
"Ase::Writ: object pointer is not persistent: (%s*) {\"$id\":%d}", wrapper->classname(), thisid);
 
   16  return this->Jsonipc::InstanceMap::wrapper_to_json (wrapper, thisid, allocator);
 
   20Writ::InstanceMap::wrapper_from_json (
const Jsonipc::JsonValue &value)
 
   23    warning (
"Ase::Writ: non persistent object cannot resolve: %s*", Jsonipc::jsonvalue_to_string (value));
 
   25  return this->Jsonipc::InstanceMap::wrapper_from_json (value);
 
   29Writ::Writ (
Flags flags) :
 
   31  skip_zero_ (flags & SKIP_ZERO),
 
   32  skip_emptystring_ (flags & SKIP_EMPTYSTRING),
 
   33  relaxed_ (flags & RELAXED),
 
   40  if (dummy_->index() != Value::NONE)
 
   42      warning (
"invalid Writ::dummy assignment: %s", dummy_->repr());
 
   54  root_.value_ = Value::empty_value;
 
   64  rapidjson::Document document (rapidjson::kNullType);
 
   65  Jsonipc::JsonValue &docroot = document;
 
   66  Jsonipc::JsonAllocator &allocator = document.GetAllocator();
 
   67  docroot = Jsonipc::to_json (root_.value_, allocator); 
 
   68  rapidjson::StringBuffer buffer;
 
   71      constexpr unsigned FLAGS = rapidjson::kWriteNanAndInfFlag;
 
   72      rapidjson::PrettyWriter<rapidjson::StringBuffer, rapidjson::UTF8<>, rapidjson::UTF8<>, rapidjson::CrtAllocator, FLAGS> writer (buffer);
 
   73      writer.SetIndent (
' ', 2);
 
   74      writer.SetFormatOptions (rapidjson::kFormatSingleLineArray);
 
   75      document.Accept (writer);
 
   79      rapidjson::Writer<rapidjson::StringBuffer> writer (buffer);
 
   80      document.Accept (writer);
 
   82  const String output { buffer.GetString(), buffer.GetSize() };
 
   87Writ::from_json (
const String &jsonstring)
 
   91  rapidjson::Document document;
 
   92  Jsonipc::JsonValue &docroot = document;
 
   93  constexpr unsigned PARSE_FLAGS =
 
   94    rapidjson::kParseFullPrecisionFlag |
 
   95    rapidjson::kParseCommentsFlag |
 
   96    rapidjson::kParseTrailingCommasFlag |
 
   97    rapidjson::kParseNanAndInfFlag |
 
   98    rapidjson::kParseEscapedApostropheFlag;
 
   99  document.Parse<PARSE_FLAGS> (jsonstring.
data(), jsonstring.
size());
 
  100  if (document.HasParseError())
 
  105  root_.value_ = Jsonipc::from_json<Value> (docroot);
 
  110Writ::blank_enum (
const String &enumname)
 
  112  warning (
"%s: serialization of enum type without values: %s", 
"Writ::blank_enum", enumname);
 
  116Writ::not_serializable (
const String &classname)
 
  118  warning (
"%s: type not registered as Jsonipc::Serializable<>: %s", 
"Writ::not_serializable", classname);
 
  159  for (LinkEntry &e : links_)
 
  160    if (e.sp == &serializable)
 
  163          warning (
"Ase::Writ: duplicate serialization of: (%s)%p", Jsonipc::rtti_typename (serializable), &serializable);
 
  167  links_.
push_back ({ valuep, &serializable, 0 });
 
  175  for (LinkEntry &e : links_)
 
  176    if (e.sp == &serializable)
 
  179          e.id = ++link_counter_;
 
  182  links_.
push_back ({ 
nullptr, &serializable, ++link_counter_ });
 
  183  return links_.
back().id;
 
  186static const char *
const ase_linkid = 
"\u0012.ID"; 
 
  193  for (LinkEntry &e : links_)
 
  196        if (e.value && e.value->index() == Value::RECORD)
 
  202          warning (
"Ase::Writ: missing serialization of: (%s)%p", Jsonipc::rtti_typename (*e.sp), e.sp);
 
  211  links_.
push_back ({ 
nullptr, &serializable, 
id });
 
  216Writ::resolve_link (
int64 id)
 
  219  for (LinkEntry &e : links_)
 
  230  for (LinkPtr &e : linkptrs_)
 
  234        *e.spp = serializable;
 
  236        warning (
"Ase::Writ: failed to resolve serialization link: %d", e.id);
 
  243  writ_ (writ), valuep_ (vp), value_ (*vp)
 
  250WritNode::recfield (
const String &fieldname, 
bool front)
 
  254      assert_return (value_.index() == Value::RECORD, { writ_, dummy() });
 
  256      return WritNode { writ_, rec.valuep (fieldname, front) };
 
  258  if (
in_load() && value_.index() == Value::RECORD)
 
  261      ValueP vp = rec.peek (fieldname);
 
  277      for (
auto &valp : values)
 
 
  289      if (value_.index() == Value::NONE)
 
  290        value_ = ValueS::empty_array;
 
  291      if (value_.index() == Value::ARRAY)
 
  294          array.push_back (
Value());
 
  295          return { writ_, array.
back() };
 
  298  const WritNode fallback { writ_, dummy() };
 
 
  312          const int64 linkid = writ_.use_link (**l.spp_);
 
  324        writ_.linkptrs_.
push_back ({ l.spp_, linkid });
 
 
  336      value_ = ValueR::empty_record;
 
  337      writ_.prepare_link (serializable, valuep_);
 
  342      if (value_.index() == Value::RECORD)
 
  344          const int64 linkid = (*this)[ase_linkid].as_int();
 
  346            writ_.collect_link (linkid, serializable);
 
  360    if (purge_emptystring && field.value->index() == Value::STRING && 
std::get<String> (*field.value) == 
"")
 
  362    if (purge_zero && field.value->index() == Value::INT64 && 
std::get<int64> (*field.value) == 0)
 
  364    if (purge_zero && field.value->index() == Value::DOUBLE && 
std::get<double> (*field.value) == 0)
 
 
  392struct Simple { 
int i = 0; 
float f = 0; 
String s; };
 
  394serialize (Simple &simple, 
WritNode &xs)
 
  420    chars.push_back (
'A');
 
  421    chars.push_back (
'B');
 
  422    chars.push_back (
'C');
 
  423    tf = { .
name = 
"NAME", .type = 
"TYPE", .offset = -1234567, .
length = 987654321 };
 
  440template<
typename T> 
static T via_json (
const T &v) { 
return json_parse<T> (
json_stringify (v)); }
 
  447  TASSERT (
false == via_json (
false));                  
 
  448  TASSERT (
true == via_json (
true));
 
  450  TASSERT (+3.5 == via_json (+3.5));                    
 
  453  TASSERT (Error::PERMS == via_json (Error::PERMS));    
 
  456    TASSERT (floats == via_json (floats2));
 
  459    using namespace MakeIcon;
 
  460    Choice choice = { 
"grump", 
"¿"_uc, 
"Grump", 
"A flashy Grump", 
"Notice", 
"Warn" }, choice2 = via_json (choice);
 
  461    TASSERT (choice.
ident == choice2.ident && choice.
icon == choice2.icon && choice.
label == choice2.label &&
 
  462             choice.
blurb == choice2.blurb && choice.
notice == choice2.notice && choice.
warning == choice2.warning);
 
  466    Tuple tuple = { 
"TUPLE", -618033988, 1.6180339887498948482 };
 
  467    Tuple u = via_json (tuple);
 
  472    Simple simple { 7, -3.14159265358979, 
"SIMPLE" };
 
  473    auto s2 = via_json (simple);
 
  474    TASSERT (s2.i == 7 && 
fabs (s2.f - -3.14159265358979) < 1e-7 && s2.s == 
"SIMPLE");
 
  506      bool operator== (
const Test1 &o)
 const { 
return i == o.i && t == o.t; }
 
  509    test1 .
set (
"i", &Test1::i) .set (
"t", &Test1::t) ;
 
  516      bool operator== (
const Test2 &o)
 const {
 
  517        return i == o.i && t1 == o.t1 && server == o.server && serverp == o.serverp;
 
  524      .
set (
"i", &Test2::i)
 
  525      .set (
"t1", &Test2::t1)
 
  526      .set (
"server", &Test2::server) 
 
  529    Test2 t2, t2orig = Test2 ({ 777, { 9, 
"nine" }, });
 
  533    TASSERT (s == R
"'''({"i":777,"server":null,"t1":{"i":9,"t":"nine"}})'''"); 
  542    TASSERT (sbase1.chars == sbase2.chars && sbase1.d == sbase2.d && sbase1.e == sbase2.e);
 
  556    xs[
"flags"] & flags_;
 
  559struct FrobnicatorSpecial : 
public FrobnicatorBase {
 
  563  explicit FrobnicatorSpecial (
const String &k) : kind_ (k)
 
  565    TASSERT (kind_ == 
"K321" || kind_ == 
"Special");
 
  574    FrobnicatorBase::serialize (xs);
 
  577      xs[
"state"] & this->state_;
 
  579      xs[
"factor"] & factor_;
 
  583struct FrobnicatorImpl : 
public FrobnicatorBase {
 
  587  FrobnicatorSpecialP special_;
 
  592  explicit FrobnicatorImpl (
const String &t = 
"") : kind_ (t) {}
 
  595    for (
auto *c : children_)
 
  599  create_lane  (
const String &kind)
 
  602    if (kind == 
"Special")
 
  603      c = 
new FrobnicatorSpecial (kind);
 
  605      c = 
new FrobnicatorImpl (kind);
 
  606    return *children_.emplace_back (c);
 
  609  populate (
bool extended = 
false)
 
  614        special_->flags_ = 321;
 
  618      matrix_ = { { 9, 8, 7 }, { 6, 5, 4 }, };
 
  619    error_ = Error::NO_MEMORY;
 
  629    FrobnicatorBase::serialize (xs);
 
  633    xs[
"matrix"] & matrix_;
 
  636      xs[
"error"] & error_;
 
  641      xs[
"special"] & *special_;
 
  644      for (
auto &childp : children_)
 
  651      for (
auto &xc : xs[
"tracks"].to_nodes())
 
  653          Serializable &child = create_lane (xc[
"kind"].as_string());
 
  657    xs[
"sibling"] & 
WritLink (&sibling_);
 
  661    if (xs.
in_load() && xs.has (
"Q?"))
 
  662      yesno_ = 
yn == 
"yes";
 
  668test_serializable_hierarchy()
 
  673    FrobnicatorImpl prjct;
 
  675    prjct.populate (
true);
 
  676    TASSERT (prjct.children_.size() == 0);
 
  677    auto &nidi = prjct.create_lane (
"Nidi");
 
  678    dynamic_cast<FrobnicatorBase*
> (&nidi)->flags_ = 0x02020202;
 
  679    prjct.sibling_ = &prjct.create_lane (
"Odio");
 
  680    FrobnicatorImpl *fimpl = 
dynamic_cast<FrobnicatorImpl*
> (prjct.sibling_);
 
  681    fimpl->populate (
false);
 
  682    fimpl->yesno_ = 
true;
 
  683    TASSERT (prjct.children_.size() == 2);
 
  684    FrobnicatorSpecial *special = 
dynamic_cast<FrobnicatorSpecial*
> (&prjct.create_lane (
"Special"));
 
  685    special->flags_ = 0xeaeaeaea;
 
  686    special->factor_ = 2.5;
 
  687    special->state_ = -17;
 
  688    TASSERT (prjct.children_.size() == 3);
 
  693    printerr (
"%s\n", streamtext1);
 
  696    FrobnicatorImpl prjct;
 
  697    const bool success = 
json_parse (streamtext1, prjct);
 
  699    TASSERT (prjct.children_.size() > 0 && 
dynamic_cast<FrobnicatorImpl*
> (prjct.children_[0])->kind_ == 
"Nidi");
 
  700    TASSERT (prjct.children_.size() > 1 && 
dynamic_cast<FrobnicatorImpl*
> (prjct.children_[1])->kind_ == 
"Odio");
 
  701    TASSERT (prjct.children_.size() > 2 && 
dynamic_cast<FrobnicatorSpecial*
> (prjct.children_[2])->kind_ == 
"Special");
 
  703    FrobnicatorImpl *fimpl = 
dynamic_cast<FrobnicatorImpl*
> (prjct.sibling_);
 
  707    printerr (
"%s\n", streamtext2);
 
  708  TASSERT (streamtext1 == streamtext2);
 
Interface for serializable objects with Reflink support.
 
virtual void serialize(WritNode &xs)=0
Serialize members and childern.
 
Central singleton, serves as API entry point.
 
static ServerP instancep()
Retrieve global Server instance as std::shared_ptr.
 
One entry in a Writ serialization document.
 
bool skip_emptystring() const
Omit empty strings during in_save()
 
WritNode push()
Append new WritNode for serializing arrays during in_save().
 
bool in_load() const
Return true during deserialization.
 
bool skip_zero() const
Omit zero integers or floats during in_save()
 
bool in_save() const
Return true during serialization.
 
WritNodeS to_nodes()
Create std::vector<WritNode> for serialized arrays during in_load().
 
void purge_value()
Clean up defaults in Value.
 
bool loadable(const String &key) const
True if in_load() && has (key).
 
bool operator&(T &v)
Serialization operator.
 
Document containing all information needed to serialize and deserialize a Value.
 
bool in_save() const
Return true during serialization.
 
static bool typedata_find_minimum(const StringS &typedata, const std::string &fieldname, long double *limit)
Find the minimum value for field of typedata.
 
static bool typedata_find_maximum(const StringS &typedata, const std::string &fieldname, long double *limit)
Find the maximum value for field of typedata.
 
static bool typedata_is_loadable(const StringS &typedata, const String &fieldname)
Check for the writable and storage flags in the hints field of typedata.
 
bool in_load() const
Return true during deserialization.
 
static bool typedata_is_storable(const StringS &typedata, const String &fieldname)
Check for the readable and storage flags in the hints field of typedata.
 
Keep track of temporary instances during IpcDispatcher::dispatch_message().
 
#define assert_return(expr,...)
Return from the current function if expr is unmet and issue an assertion warning.
 
#define return_unless(cond,...)
Return silently if cond does not evaluate to true with return value ...
 
#define TEST_INTEGRITY(FUNC)
Register func as an integrity test.
 
Flags
Flags for allocator behavior.
 
The Anklang C++ API namespace.
 
int32_t int32
A 32-bit signed integer.
 
String name
Names like "bpm", etc.
 
bool json_parse(const String &jsonstring, T &target)
Parse a well formed JSON string and assign contents to target.
 
int64_t int64
A 64-bit unsigned integer, use PRI*64 in format strings.
 
Error
Enum representing Error states.
 
String json_stringify(const T &source, Writ::Flags flags=Writ::Flags(0))
Create JSON string from source.
 
Telemetry segment location.
 
Representation of one possible choice for selection properties.
 
String warning
Potential problem indicator.
 
String ident
Identifier used for serialization (may be derived from untranslated label).
 
String icon
Icon (64x64 pixels) or unicode symbol (possibly wide).
 
String notice
Additional information of interest.
 
String blurb
Short description for overviews.
 
String label
Preferred user interface name.
 
Value type used to interface with various property types.
 
int64 as_int() const
Convert Value to int64 or return 0.
 
void filter(const std::function< bool(const ValueField &)> &pred)
Recursively purge/remove RECORD elements iff to pred (recordfield) == true.
 
const ValueS & as_array() const
Retrive a non-empty array if Value contains a non-empty array.
 
Jsonipc wrapper type for objects that support field-wise serialization to/from JSON.
 
Serializable & set(const char *name, A attribute)
Add a member object pointer.
 
#define TASSERT(cond)
Unconditional test assertion, enters breakpoint if not fullfilled.