Class declaration

DBOO uses a declarative reflection API to understand the application’s classes and their relationships. The objective of the reflection API is to understand and support serialization of all standard containers and classes.

In short, DBOO can handle:

  • Any C++ primitive type.

  • Any class or struct type.

  • Any STL container (not the adapters) with elements of any type.

  • C-style fixed length arrays of any type.

  • Complex object graphs (deep copies of pointers).

  • Version control of class declaration.

  • Any type of inheritance structure.

Classes

For each class or struct that is used in the application, and that will be serialized, a class declaration must be added. In the class declaration, a class object is defined and a function that knows how to register member fields is registered with this class object.

The class registration occurs during the static initialization of the program, before the main() function is called.

Class registry

The class registry is implemented in the class cls_reg. As a user of DBOO, the only interaction with cls_reg is with the class declaration, where a call to dboo::init() must be made.

The dboo::init() will call all the registered classes’ class initialization functions. This is necessary for all the member fields to be registered. If you discover that your output files are storing objects without members or base classes, you most likely forgot to call dboo::init() at program start up.

Type id/class name

All the types have an associated type id. For the primitive types and the STL containers, these are defined by the DBOO. The type id for any user defined class types must be set by the user of DBOO. The reason for this, rather than to automatically create a type id internally in DBOO by using the type_info as provided by the C++ standard is that a platform independent externalizable name must be available for the type.

Requirements on classes

There are a few requirements on the classes and structures that must be met in order to use DBOO effectively. Most of them are requirements that normally applies anyway in order to write exception safe classes. The greatest constraint is that the class must have a default constructor. The following list shows the constraints:

  • All non abstract classes must have a default constructor.

  • Any objects created by the constructor, must be destroyed by the destructor.

  • All pointers must be initialized to null in default constructor.

  • The destructor must not throw.

  • Any object that has been constructed in the default constructor and used to initialize pointer members may be destroyed during internalization by the serializer. Thus, constructed objects must be allowed to be destroyed without any side effects.

  • The types of pointed-to objects must have class objects associated with them.

  • A class may contain a reference as long as there is a default constructor that knows how to initialize it. References can not be serialized, and must be omitted from the member registration.

  • All classes must have a constructor, which must be possible to call without any arguments, (i.e. any arguments must have default values).

  • All serialized class members must be initialized.

The cls<> class (C++)

To describe a C++ class for DBOO, you would use the cls<> class (see API reference cls):

template<typename T>
class cls
: public cls_class_any
{
protected:
    cls_class_any(const std::type_info& ti, const cls_id& id);

public:
    cls(const char* id);
    cls(const char* id, void (*dboo_class_init)(cls&));

    void set_post_int_f(void (T::*dboo_post_internalize_mem)());
    void set_post_int_f(void (*dboo_post_internalize)(T&));

    void set_version(cls_version version);
    virtual cls_version get_version() const;
    void set_version_handler(cls_version version,
                             const version_handler* handler);

    template<typename MT, typename C>
    void member(MT C::* m, const char* name);

    template<typename ST>
    void base();

    template<typename ST>
    void base_nonpm();
};

This class is used when a user defined class is registered with DBOO. For each user defined class, a instance of this cls<> is defined in static memory. When the program starts up, before the main() is called, all the defined class objects will be registered with cls_reg as they are initialized.

#include "dboo/rfx.h"

dboo::cls<AddressCard> cAddressCard("AddressCard");

When the dboo::init() is called, the class’ dboo_class_init() function, or another class initialization function if that has been declared, is invoked with the class object as argument. The class initialization function must then register any base classes and member fields.

Class initialization function (C++)

The class initialization function must be defined by the library user for each user defined class that will be serialized. In this function, all the class’ member fields and base classes must be registered. There are 3 different ways to define the class initialization function:

  • Use the default dboo_class_init() function in the user defined class.

  • Use any other function in the user defined class with the signature static void function(cls<T>&);

  • Use a global function with the signature void function(cls<T>&);

The different methods gives the same functionality although they are used in slightly different circumstances.

Default: static void dboo_class_init(cls<T>&) By not specifying a function for the class object’s constructor, you must define the default function in your class. You must use the default function or a user defined class function to be able to serialize protected and private member fields and base classes.

#include "dboo/rfx.h"

class AddressCard
{
    ...
    static void dboo_class_init(cls<AddressCard>& c)
    {
        ...
    }
};

dboo::cls<AddressCard> cAddressCard("AddressCard");
* ...

Example of the default :dboo:`dboo_class_init()`. No function pointer is passed in to the constructor of the class object.

User defined class function: static void function(cls<T>&)

This provides exactly the same functionality as the default function with the difference that the user may choose any function name. The function must be passed to the constructor of the class object.

#include "dboo/rfx.h"

class AddressCard
{
    ...
    static void MyInitDBOO(cls<AddressCard>& c)
    {
        ...
    }
};
dboo::cls<AddressCard> cAddressCard("AddressCard", AddressCard::MyInitDBOO);
* ...

Example of user defined class function. The function pointer is passed in to the constructor of the class object.

Global function: void function(cls<T>&)

With a global class initialization function you can add serialization to a class without altering the class itself. This may be useful when the application uses 3rd party libraries or when for any other reason the class declaration cannot be touched. The constraint with this method is that only public member fields can be serialized.

#include "dboo/rfx.h"

void InitAddressCard(cls<AddressCard>& c)
{
    ...
}
dboo::cls<AddressCard> cAddressCard("AddressCard", InitAddressCard);
* ...

Example of user defined global function. The function pointer is passed in to the constructor of the class object.

Member Fields

Member fields are registered in the class initialization function. No matter what type the field has, the syntax is the same:

#include "dboo/rfx.h"

using namespace std;
    ...
    void dboo_class_init(dboo::cls<AddressCard>& c) {
        c.member(&AddressCard::Name, "Name");
        c.member(&AddressCard::Street, "Street");
        ...
    }
    ...
* ...

The function cls<Class>::member() takes the pointer to member of the field as first argument and the name of the field as the second argument. See API reference cls::member().

Base Classes

Just as member fields, base classes are registered in the class initialization function. The function cls<Class>::base<BaseClass>() is used. It only takes the base class type as template argument. See API reference cls::base().

#include "dboo/rfx.h"

class Car
: public Vehicle
, public Engine
, public Seats
{
    ...
    void dboo_class_init(dboo::cls<Car>& c) {
        c.base<Vehicle>();
        c.base<Engine>();
        ...
        c.base_nonpm<Seats>();
        ...
    }
    ...
};
* ...

Any number of base classes may be registered. Virtual base classes (C++) are serializable as well, although in the current release, multiple instances will be output to the external representation. This doesn’t cause any other problem than extra storage space.

Base classes that are not polymorphic must be added with the function base_nonpm(). The syntax is the same, but it will use slightly different code for cast operations internally.

Post Internalization Function

In some cases it is necessary to do initialization of an object after it has been internalized. This can be achieved with a post internalization function. This is set with cls<Class>::set_post_int_f() and may be either a member function or a global function. The member function doesn’t take any arguments but is invoked on the internalized object. The global function takes the internalized object as argument.

#include "dboo/rfx.h"

class AddressCard
{
    ...
    static void dboo_class_init(cls<AddressCard>& c)
    {
        c.set_post_int_f(&AddressCard::my_post_init_f);
        ...
    }

    void my_post_init_f() {
        ...
    }
};

dboo::cls<AddressCard> cAddressCard("AddressCard");
* ...

Example of a post internalization function declared as a member function.

#include "dboo/rfx.h"

void my_post_init_f(AddressCard& object) {
    ...
}

class AddressCard
{
    ...
    static void dboo_class_init(cls<AddressCard>& c)
    {
        c.set_post_int_f(my_post_init_f);
        ...
    }
};
dboo::cls<AddressCard> cAddressCard("AddressCard");
* ...

Example of a post internalization function declared as a global function.

The post internalization function is called after the entire object graph has been internalized. That is, all member fields and all base and sub classes of all objects in the stream have been internalized. Also, the post internalization functions of all member fields and base classes have been called prior to the call to this function. However, the post internalization function of objects pointed to may not have been invoked yet at this point.

Class function (Node.js)

The class function serves the same purpose in Node.js as the cls<> object and the class initialization function in C++. Internally, a cls<> instance is created even for Node.js code (see also dboo.class (Node.js)).

const dboo = require('dboo');

dboo.class(<class/function name>,
           <base class> | [<base class 1>, <base class 2>, ...],
           [{<member 1 name>: <dboo type>},
            {<member 2 name>: <dboo type>}
            ],
           {<extra params>}
          );

An example of how it can be used:

const dboo = require('dboo');


class User {
  userId = null;
  emailAddress = "";
  mobileNumber = "";
  firstName = "";
  lastName = "";
  permissions = [];
  lists = [];
  friends = [];
  preferred_locale = "";

  constructor() {
    this.userId = null;
    this.emailAddress = "";
    this.firstName = "";
    this.lastName = "";
    this.permissions = [];
    this.lists = [];
    this.preferred_locale = "";
  }

};

exports.User = User;

dboo.class(User,
  [{"userId": userid.UserId},
   {"emailAddress": dboo.string},
   {"firstName": dboo.string},
   {"lastName": dboo.string},
   {"permissions": dboo.array(dboo.string)},
   {"lists": dboo.array(lists.ShoppingList)},
   {"friends": dboo.array(userid.UserId)},
   {"preferred_locale": dboo.string},
  ]
);

In this case the userid.UserId and lists.ShoppingList are other user defined classes.

The parameter <extra params> is currently not used (it is possible to set the class version by specifying {version: 1}). Support for version handling or post internalization functions will come.

Datatypes with examples

Implementation status

The following tables and examples show which datatypes and constructions are supported. Supported containers can have any other data type as key or value type. The goal is that all datatype should be possible to map to some language specific datatype and still make sense.

Primitives

In C++ it is fairly straight forward. All primitive types are supported. In Node.js, there are not that many primitive types, but Node.js API still support all the types supported for C++. In Node.js, all of them maps to Number at this point. There are no checks whether the number in a property fits in the designated DBOO type.

Data types

Status

Comment

All primitive types

primitives.h

Try it

 1#pragma once
 2#include <dboo/rfx.h>
 3#include <vector>
 4#include <array>
 5
 6class primitives
 7{
 8public:
 9  int8_t _member_int8;
10  uint64_t _member_uint64;
11  int _member_int;
12  double _member_double;
13  long _member_long;
14  float _member_float;
15  std::array<double,13> _member_array_of_double;
16  
17  inline static dboo::cls<primitives> _class{"primitives"};
18  inline static void dboo_class_init(dboo::cls<primitives>& c) {
19    c.member(&primitives::_member_int8, "_member_int8");
20    c.member(&primitives::_member_uint64, "_member_uint64");
21    c.member(&primitives::_member_int, "_member_int");
22    c.member(&primitives::_member_double, "_member_double");
23    c.member(&primitives::_member_long, "_member_long");
24    c.member(&primitives::_member_float, "_member_float");
25    c.member(&primitives::_member_array_of_double, "_member_array_of_double");
26  }
27  
28};

Data types

Status

DBOO specifier

Comment

Number

dboo.int

.

dboo.uint

.

dboo.long

.

dboo.ulong

.

dboo.int8

.

dboo.int16

.

dboo.int32

.

dboo.int64

Number can represent a 53 bit integer at most.

.

dboo.uint8

.

dboo.uint16

.

dboo.uint32

.

dboo.uint64

Number can represent a 53 bit integer at most.

.

dboo.float

.

dboo.double

.

dboo.ldouble

bool

dboo.bool

BigInt

Planned.

primitives.js

Try it

 1const dboo = require('dboo');
 2
 3class primitives {
 4  _member_int8 = 0;
 5  _member_uint64 = 0;
 6  _member_int = 0;
 7  _member_double = 0;
 8  _member_long = 0;
 9  _member_float = 0;
10  _member_array_of_double = [];
11  
12  constructor() {
13  }
14}
15
16dboo.class(primitives, [
17  {"_member_int8": dboo.int8_t },
18  {"_member_uint64": dboo.uint64_t },
19  {"_member_int": dboo.int },
20  {"_member_double": dboo.double },
21  {"_member_long": dboo.long },
22  {"_member_float": dboo.float },
23  {"_member_array_of_double": dboo.array(dboo.double)},
24]);

Strings

Data types

Status

Comment

std::string

std::wstring

strings.h

Try it

 1#pragma once
 2#include <dboo/rfx.h>
 3
 4#include <string>
 5#include <list>
 6
 7class my_strings
 8{
 9public:
10  // A wide string, this has 16 or 32 bits chars...
11  std::wstring _a_wide_string;
12  // A normal string
13  std::string _a_narrow_string;
14
15  // You can put them in any stl container
16  std::list<std::string> _a_list_of_strings;
17  std::map<std::string, std::string> _a_map_of_strings;
18  
19  inline static dboo::cls<my_strings> _class{"my_strings"};
20  inline static void dboo_class_init(dboo::cls<my_strings>& c) {
21    c.member(&my_strings::_a_wide_string, "_a_wide_string");
22    c.member(&my_strings::_a_narrow_string, "_a_narrow_string");
23    c.member(&my_strings::_a_list_of_strings, "_a_list_of_strings");
24    c.member(&my_strings::_a_map_of_strings, "_a_map_of_strings");
25  }
26};

Data types

Status

DBOO specifier

Comment

String

dboo.string

strings.js

Try it

 1const dboo = require('dboo');
 2
 3class my_strings {
 4  // You don't really need to define them here
 5  _a_string;
 6  // You can put them in an array
 7  _a_list_of_strings;
 8  // or a map...
 9  _a_map_of_strings;
10  
11  constructor() {
12    // A constructor is required that should put some default values in all properties
13    this._a_string = '';
14    this._a_list_of_strings = [];
15    this._a_map_of_strings = new Map();
16  }
17}
18
19dboo.class(my_strings, [
20  {"_a_string": dboo.string },
21  {"_a_list_of_strings": dboo.array(dboo.string) },
22  {"_a_map_of_strings": dboo.map(dboo.string, dboo.string) },
23]);

Pointers and references

Data types

Status

Comment

std::unique_ptr

std::shared_ptr

Planned.

dboo::obj_id

dboo::ref

Lazy loading reference

pointer

To other user defined class types.

C++ reference

References cannot be serialized.

value composition

Where a member field is of a class type.

pointers.h

Try it

 1#pragma once
 2#include "primitives.h"
 3#include "strings.h"
 4
 5#include <dboo/rfx.h>
 6#include <memory>
 7
 8class pointers {
 9public:
10  pointers() = default;
11
12private:
13  // A normal C pointer. Must be nullptr initialized in default constructor (or point to a real object!!!)
14  primitives *_a_bare_pointer{nullptr};
15  
16  // A unique ptr.
17  std::unique_ptr<my_strings> _a_smarter_ptr;
18  
19  // A dboo::ref, which is a lazy loading pointer (retrieves object once dereferenced).
20  dboo::ref<primitives> _the_smartest_ptr;
21  friend void test_data(pointers& ptr);
22public:
23  inline static dboo::cls<pointers> _class{"pointers"};
24  inline static void dboo_class_init(dboo::cls<pointers>& c) {
25    c.member(&pointers::_a_bare_pointer, "_a_bare_pointer");
26    c.member(&pointers::_a_smarter_ptr, "_a_smarter_ptr");
27    c.member(&pointers::_the_smartest_ptr, "_the_smartest_ptr");
28  }
29};

Data types

Status

DBOO specifier

Comment

Javascript reference

class name

dboo::ref

dboo.ref(class name)

Lazy loading referece. Retrieves object when accessing it.

value composition

dboo.value(class name)

Lazy loading referece. Retrieves object when accessing it.

pointers.js

Try it

 1const dboo = require('dboo');
 2
 3import {primitives} from './primitives.js'
 4import {my_strings} from './my_strings.js'
 5
 6class pointers {
 7  // So, trying to match this node example with the C++ examplee
 8  // but obviously, js doesn't have bare pointers, or unique pointers etc
 9  _a_bare_pointer; // : primitives (In C++ example, a primitives*)
10
11  _a_smarter_ptr; // : my_strings (In C++ example, a unique_ptr<_a_smarter_ptr>)
12  
13  // A dboo::ref, which is a lazy loading pointer (retrieves object once dereferenced).
14  _the_smartest_ptr; // dboo::ref<primitives>
15  
16  constructor() {
17    this._a_bare_pointer = null;
18    this._a_smarter_ptr = null;
19    this._the_smartest_ptr = null;
20  }
21};
22
23dboo.class(pointers, [
24    {"_a_bare_pointer": primitives },
25    {"_a_smarter_ptr": my_strings },
26    {"_the_smartest_ptr": dboo.ref(primitives) },
27  ]);

Containers

Containers

Status

Comment

fixed size array

vector

list

set

map

multiset

multimap

unordered_set

unordered_map

unordered_multiset

unordered_multimap

array

deque

bitset

forward_list

variant

Planned.

tuple

Planned.

pair

Planned.

containers.h

Try it

 1#pragma once
 2
 3#include "primitives.h"
 4#include "strings.h"
 5
 6#include <dboo/rfx.h>
 7
 8class key {
 9public:
10  double value_a;
11  int value_b;
12  bool operator<(const key& b) const {
13    return b.value_a < b.value_b;
14  }
15    inline static dboo::cls<key> _class{"key"};
16    inline static void dboo_class_init(dboo::cls<key>& c) {
17      c.member(&key::value_a, "value_a");
18      c.member(&key::value_b, "value_b");
19    }
20};
21
22class containers {
23public:
24  containers() = default;
25  
26  std::unordered_map<std::string, my_strings> _loads_of_strings;
27  std::unordered_map<std::string, my_strings*> _loads_of_string_ptrs;
28  std::unordered_map<std::string, std::unique_ptr<my_strings>> _loads_of_string_uptrs;
29  
30  std::vector<double> _numbers;
31  std::vector<std::vector<std::vector<double>>> _3d_numbers;
32  double _fixed_3d_numbers[12][12][12];
33  
34  std::unordered_set<double> _a_set_of_unique_numbers;
35  
36  std::list<primitives> _a_list_of_primitives;
37  
38  std::map<key, double> _complex_key;
39  std::unordered_set<std::unique_ptr<key>> _a_set_of_unique_keys;
40
41public:
42  inline static dboo::cls<containers> _class{"containers"};
43  inline static void dboo_class_init(dboo::cls<containers>& c) {
44    c.member(&containers::_loads_of_strings, "_loads_of_strings");
45    c.member(&containers::_loads_of_string_ptrs, "_loads_of_string_ptrs");
46    c.member(&containers::_loads_of_string_uptrs, "_loads_of_string_uptrs");
47    c.member(&containers::_numbers, "_numbers");
48    c.member(&containers::_3d_numbers, "_3d_numbers");
49    c.member(&containers::_fixed_3d_numbers, "_fixed_3d_numbers");
50    c.member(&containers::_a_set_of_unique_numbers, "_a_set_of_unique_numbers");
51    c.member(&containers::_a_list_of_primitives, "_a_list_of_primitives");
52    c.member(&containers::_complex_key, "_complex_key");
53    c.member(&containers::_a_set_of_unique_keys, "_a_set_of_unique_keys");
54  }
55};

Containers

Status

DBOO specifier

Comment

array

dboo.array(<type>)

Map

dboo.map(<key type>, <value type>)

Set

dboo.set(<type>)

array buffer

dboo.array_buffer(<type>)

typed array

dboo.typed_array(<type>)

containers.js

Try it

 1const dboo = require('dboo');
 2
 3import {primitives} from './primitives.js'
 4import {my_strings} from './my_strings.js'
 5
 6class key {
 7  value_a;
 8  value_b;
 9};
10
11class containers {
12  constructor() {
13  }
14  
15  _loads_of_strings;
16  _loads_of_string_ptrs;
17  _loads_of_string_uptrs;
18  _numbers;
19  _3d_numbers;
20  _a_set_of_unique_numbers;
21  _a_list_of_primitives;
22  _complex_key;
23  _a_set_of_unique_keys;
24}
25
26dboo.class(containers, [
27  {"_loads_of_strings": dboo.map(dboo.string, dboo.composed(my_string)) },
28  {"_loads_of_string_ptrs": dboo.map(dboo.string, my_string) },
29  {"_loads_of_string_uptrs": dboo.map(dboo.string, my_string) },
30  {"_numbers": dboo.array(dboo.double) },
31  {"_3d_numbers": dboo.array(dboo.array(dboo.array(dboo.double))) },
32  {"_fixed_3d_numbers": dboo.array(dboo.array(dboo.array(dboo.double))) },
33  {"_a_set_of_unique_numbers": dboo.set(dboo.double) },
34  {"_a_list_of_primitives": dboo.array(primitives) },
35  {"_complex_key": dboo.map(dboo.composed(key), double) },
36  {"_a_set_of_unique_keys": dboo.set(key) }
37]);

Value composition

composition.h

Try it

 1#pragma once
 2
 3#include "primitives.h"
 4#include "strings.h"
 5
 6#include <dboo/rfx.h>
 7
 8class by_value {
 9public:
10  by_value() = default;
11  
12  primitives _my_primitives;
13  my_strings _my_strings;
14  
15public:
16  inline static dboo::cls<by_value> _class{"by_value"};
17  inline static void dboo_class_init(dboo::cls<by_value>& c) {
18    c.member(&by_value::_my_primitives, "_my_primitives");
19    c.member(&by_value::_my_strings, "_my_strings");
20  }
21};

composition.js

Try it

 1const dboo = require('dboo');
 2
 3import {primitives} from './primitives.js'
 4import {my_strings} from './my_strings.js'
 5
 6class by_value {
 7  _my_primitives; // primitives
 8  _my_strings; // my_strings
 9  
10  constructor() {
11    this._my_primitives = {};
12    this._my_strings = {};
13  }
14}
15
16dboo.class(by_value, [
17  {"_my_primitives": dboo.value(primitives) },
18  {"_my_strings": dboo.value(my_strings) }
19]);

Inheritance

inheritance.h

Try it

 1#pragma once
 2
 3#include <dboo/rfx.h>
 4
 5class baseclass {
 6public:
 7    virtual ~baseclass() = default;
 8    std::string id;
 9public:
10    inline static dboo::cls<baseclass> _class{"baseclass"};
11    inline static void dboo_class_init(dboo::cls<baseclass>& c) {
12      c.member(&baseclass::id, "id");
13    }
14};
15
16class subclass : public baseclass {
17public:
18    subclass() = default;
19
20    std::string name;
21
22public:
23    inline static dboo::cls<subclass> _class{"subclass"};
24    inline static void dboo_class_init(dboo::cls<subclass>& c) {
25      c.base<baseclass>();
26      c.member(&subclass::name, "name");
27    }
28};
29
30class an_abstract_baseclass {
31public:
32    virtual ~an_abstract_baseclass() = default;
33    std::string some_other_property;
34
35    virtual void a_virtual_function() = 0;
36
37public:
38    inline static dboo::cls<an_abstract_baseclass> _class{dboo::abstract<an_abstract_baseclass>(), "an_abstract_baseclass"};
39    inline static void dboo_class_init(dboo::cls<an_abstract_baseclass>& c) {
40      c.member(&an_abstract_baseclass::some_other_property, "some_other_property");
41    }
42};
43
44
45class subclass2 : public baseclass, public an_abstract_baseclass {
46public:
47    subclass2() = default;
48
49    virtual void a_virtual_function() {}
50
51public:
52    inline static dboo::cls<subclass2> _class{"subclass2"};
53    inline static void dboo_class_init(dboo::cls<subclass2>& c) {
54      c.base<baseclass>();
55      c.base<an_abstract_baseclass>();
56    }
57};

inheritance.js

Try it

 1import {primitives} from "./primitives";
 2
 3const dboo = require('dboo');
 4
 5class baseclass {
 6  id;
 7  constructor(id = '') {
 8    this.id = id;
 9  }
10};
11
12dboo.class(baseclass, [
13  {"id": dboo.string },
14]);
15
16
17class subclass extends baseclass {
18  constructor(id = '', name = '') {
19    super(id);
20    this.name = name;
21  }
22  
23  name;
24};
25
26
27dboo.class(subclass, [baseclass], [
28  {"name": dboo.string },
29]);
30
31
32// You probably wouldn't implement abstract classes in javascript, but this show how the classes in the C++
33// example would be defined in JS. When designing classes for use in both languages, make sure to only
34// use common elements.
35class an_abstract_baseclass {
36  constructor(value = '') {
37    this.some_other_property = value;
38  }
39
40  some_other_property;
41  
42  a_virtual_function() {}
43}
44
45dboo.class(an_abstract_baseclass, [
46  {"some_other_property": dboo.string },
47]);
48
49
50
51// multiple inheritence doesn't work in javascript, so just extend one...
52class subclass2 extends baseclass {
53  constructor() {
54    super();
55  }
56  
57  a_virtual_function() {
58  }
59};
60
61// DBOO still understands multiple inheritence and will populate the object with properties from
62// both base classes.
63dboo.class(subclass2, [baseclass, an_abstract_baseclass], [
64]);

Datetime

Date & time

Status

Comment

chrono::time_point

Some language/platform

chrono::duration

independent support is planned.

Date & time

Status

DBOO specifier

Comment

Datetime

Planned.