Files
mlpack/fastlib2/fastlib/base/otrav.h
T

1938 lines
62 KiB
C++

// Copyright 2007 Georgia Institute of Technology. All rights reserved.
/**
* @file otrav.h
*
* Object-tree traversal for the purpose of deep-copying,
* pretty-printing, and advanced memory management.
*
* @see namespace ot, OBJECT_TRAVERSAL
*/
#ifndef BASE_OTRAV_H
#define BASE_OTRAV_H
#include "common.h"
#include "debug.h"
#include "cc.h"
#include "ccmem.h"
#include <typeinfo>
/**
* Inside OBJECT_TRAVERSAL (or, more likely, OT_CUSTOM_PRINT),
* traverse an object or primitive, but giving it a specified name and
* type to print.
*
* You do not need to use this macro even in OT_CUSTOM_PRINT unless it
* is most convenient for x to be some big nasty expression or of a
* type that is not true to its value. Otherwise, just use OT_OBJ.
*
* Example (printing NULL for pointers):
* @code
* class MyClass {
* index_t len_;
* double *array_;
* ...
* OT_CUSTOM_PRINT() {
* OT_OBJ(len_);
* if (!array_) {
* OT_CUSTOM_PRINT_OBJ("NULL", "array_", typeid(array_).name());
* } else {
* OT_ARRAY(array_, len_);
* }
* ...
* }
* ...
* };
* @endcode
*
* @param x the object or value to traverse; in OT_CUSTOM_PRINT, may
* be any expression; otherwise must be an lvalue expression
* @param name a string giving the object's printed name
* @param type a string giving the object's printed type
*
* @see OT_OBJ, OT_CUSTOM_PRINT, OBJECT_TRAVERSAL
*/
#define OT_CUSTOM_PRINT_OBJ(x, name) \
if (true) { \
ot__visitor->Name(name, x); \
ot__visitor->Obj(x); \
} else NOP // require semicolon
/**
* Inside OBJECT_TRAVERSAL, traverse a non-pointer object or
* primitive. This is usually the declaration you want.
*
* OT_OBJ is designed for use with primitives and other classes that
* have defined OBJECT_TRAVERSAL, but it can work with untraversed
* classes. Nearly all FASTlib core classes are traversable.
*
* You should use OT_OBJ for pointers that your class is not
* responsible for deallocating, such as string constants. These will
* be shallow-copied and initialized to BIG_BAD_POINTER, but not
* freed. If you intend to serialize or freeze your class, you should
* instead make sure to allocate copies for all pointers and arrays
* and use OT_PTR, OT_ARRAY, or their expert versions.
*
* Example (composure with a vector):
* @code
* class MyClass {
* Vector vec_;
* ...
* OBJECT_TRAVERSAL() {
* OT_OBJ(vec_);
* ...
* }
* ...
* };
* @endcode
*
* @param x the object; may be any lvalue expression, e.g. @c array[i]
*
* @see OT_PTR, OT_ARRAY, OBJECT_TRAVERSAL
*/
#define OT_OBJ(x) \
OT_CUSTOM_PRINT_OBJ(x, #x)
/**
* Inside OT_ENUM_EXPERT (itself inside OBJECT_TRAVERSAL), test for
* and handle a specific enum value when printing.
*
* @param val the enum value, as defined in @c enum{val,...};
*
* @see OT_ENUM_EXPERT
*/
#define OT_ENUM_VAL(val) \
if (ot__visitor->IS_PRINTER && ot__enum == val) { \
const char *ot__temp = #val; \
ot__visitor->Obj(ot__temp); \
} else // for series of OT_ENUM_VAL, use in OT_ENUM_EXPERT
/**
* Inside OBJECT_TRAVERSAL, traverse an enum, optionally providing
* a printable list of values that it might occupy.
*
* This macro exists because C++ templates cannot recognize enums as
* primitives. It is roughly equivalent to @c OT_OBJ((int)x) but does
* a better job of printing the member name and type.
*
* Example (cannonical card suits):
* @code
* class MyClass {
* enum Suit {
* CLUBS,
* SPADES,
* DIAMONDS,
* HEARTS
* };
* Suit suit_;
* ...
* OBJECT_TRAVERSAL() {
* OT_ENUM_EXPERT(suit_, int,
* OT_ENUM_VAL(CLUBS)
* OT_ENUM_VAL(SPADES)
* OT_ENUM_VAL(DIAMONDS)
* OT_ENUM_VAL(HEARTS));
* ...
* }
* ...
* };
* @endcode
*
* Note that each OT_ENUM_VAL is white-space separated rather than
* comma or semi-colon separated. It is only necessary to provide the
* list of OT_ENUM_VAL invocations if you want to print value names
* rather than numbers, and only in the traversal function used by
* ot::Print (i.e. if you have OT_CUSTOM_PRINT, your OBJECT_TRAVERSAL
* is free to omit the list). Omit the list by leaving only
* white-space between the last comma and close parenthesis.
*
* Because this list is needed each time the enum appears in some
* object's print traversal, it may be a good idea to define a macro
* that exands to the list at the time that the enum is defined.
*
* @param x the enum; may be any lvalue expreesion, e.g. @c array[i]
* @param T the enum's inherited type; almost certainly int
* @param print_code a list of white-space separated OT_ENUM_VAL's
*
* @see OT_ENUM_VAL, OT_ENUM, OBJECT_TRAVERSAL
*/
#define OT_ENUM_EXPERT(x, T, print_code) \
if (true) { \
ot__visitor->Name(#x, x); \
T ot__enum = x; \
print_code { \
ot__visitor->Obj(reinterpret_cast< T &>(x)); \
} \
} else NOP // require semicolon
/**
* Inside OBJECT_TRAVERSAL, traverse an enum as if it were an int.
*
* This macro exists because C++ templates cannot recognize enums as
* primitives. It is roughly equivalent to @c OT_OBJ((int)x) but does
* a better job of printing the member name and type.
*
* Equivalent to @c OT_ENUM_EXPERT(x, int, )
*
* @param x the enum; may be any lvalue expresion, e.g. @c array[i]
*
* @see OT_ENUM_EXPERT, OT_OBJ, OBJECT_TRAVERSAL
*/
#define OT_ENUM(x) \
OT_ENUM_EXPERT(x, int, )
/**
* The implementation for OT_STATIC_ARRAY_EXPERT, OT_PTR_EXPERT,
* OT_ARRAY_EXPERT, and OT_ALLOC_EXPERT.
*
* There is no need to invoke this macro unless you know what you're
* doing.
*
* @param x the array; may be any lvalue expresion, e.g. @c *ptr
* @param len the length of the array; may be any expression but must
* depend only on previously declared variables
* @param func the visitor call used to handle the array; will be
* prefixed with Pre and Post for before and aftet the loop
* @param iter_var a variable created to iterate the array
* @param loop_code another member declaration, e.g. OT_OBJ
*
* @see OT_STATIC_ARRAY_EXPERT, OT_PTR_EXPERT, OT_ARRAY_EXPERT,
* OT_ALLOC_EXPERT, OBJECT_TRAVERSAL
*/
#define OT_ARRAY_IMPL(x, len, iter_var, loop_code, func, args...) \
if (true) { \
ot__visitor->Name(#x, x); \
index_t ot__len = len; \
if (ot__visitor->Pre##func(x, ot__len, ## args)) { \
for (index_t iter_var = 0; iter_var < ot__len; ++iter_var) { \
ot__visitor->ElemOf(x); \
loop_code; \
} \
ot__visitor->Name(#x, x); \
ot__visitor->Post##func(x, ot__len, ## args); \
} \
} else NOP // require semicolon
/**
* Inside OBJECT_TRAVERSAL, traverse a static array, i.e. with size
* given at compile time, and its contents.
*
* Example (static array of pointers):
* @code
* class MyClass {
* MyClass *children_[NUM_CHILDREN];
* ...
* OBJECT_TRAVERSAL(MyClass} {
* OT_STATIC_ARRAY_EXPERT(children_,
* i, OT_PTR(children_[i]));
* ...
* }
* ...
* };
* @endcode
*
* @param x the array; may be any lvalue expresion, e.g. @c *ptr
* @param iter_var a variable created to iterate the array
* @param elem_code another member declaration, e.g. OT_OBJ
*
* @see OT_STATIC_ARRAY, OBJECT_TRAVERSAL
*/
#define OT_STATIC_ARRAY_EXPERT(x, iter_var, elem_code) \
OT_ARRAY_IMPL(x, sizeof(x) / sizeof(*x), \
iter_var, elem_code, StaticArray)
/**
* Inside OBJECT_TRAVERSAL, traverse a static array, i.e. with size
* given at compile time, assuming its contents to be objects or
* primitives.
*
* Equivalent to @c OT_STATIC_ARRAY_EXPERT(x, i, OT_OBJ(x[i]))
*
* @param x the array; may be any lvalue expression, e.g. @c *ptr
*
* @see OT_STATIC_ARRAY_EXPERT, OT_ARRAY, OBJECT_TRAVERSAL
*/
#define OT_STATIC_ARRAY(x) \
OT_STATIC_ARRAY_EXPERT(x, ot__iter, OT_OBJ((x)[ot__iter]))
/**
* Inside OBJECT_TRAVERSAL, traverse a pointer managed by new and
* delete and its contents.
*
* Example (non-null pointer, for maximal speed):
* @code
* class MyClass {
* MyTree *tree_;
* ...
* OBJECT_TRAVERSAL(MyClass} {
* OT_PTR_EXPERT(tree_, false,
* OT_OBJ(*tree_));
* ...
* }
* ...
* };
* @endcode
*
* You should instead use OT_OBJ for pointers that your class is not
* responisble for deallocating, especially string constants and other
* const pointers.
*
* @param x the pointer; may be any lvalue expresion, e.g. @c array[i]
* @param nullable bool indicating whether the pointer might be NULL
* @param deref_code another member declaration, e.g. OT_OBJ
*
* @see OT_PTR, OBJECT_TRAVERSAL
*/
#define OT_PTR_EXPERT(x, nullable, deref_code) \
OT_ARRAY_IMPL(x, 1, ot__iter, deref_code, \
Array, nullable, false, true)
/**
* Inside OBJECT_TRAVERSAL, traverse a pointer managed by new and
* delete, assuming its contents to be an object or primitive.
*
* Equivalent to @c OT_PTR_EXPERT(x, true, OT_OBJ(*x))
*
* You should instead use OT_OBJ for pointers that your class is not
* responisble for deallocating, especially string constants and other
* const pointers.
*
* @param x the pointer; may be any lvalue expresion, e.g. @c array[i]
*
* @see OT_PTR_EXPERT, OT_ARRAY, OT_OBJ, OBJECT_TRAVERSAL
*/
#define OT_PTR(x) \
OT_PTR_EXPERT(x, true, OT_OBJ(*(x)))
/**
* Inside OBJECT_TRAVERSAL, traverse an array managed by new[] and
* delete[] and its contents.
*
* Example (two-dimensional array):
* @code
* class MyClass {
* index_t rows_;
* index_t cols_;
* double **data_;
* ...
* OBJECT_TRAVERSAL(MyClass} {
* OT_OBJ(rows_);
* OT_OBJ(cols_);
* OT_ARRAY_EXPERT(array_, rows_, true,
* i, OT_ARRAY(array_[i], cols));
* ...
* }
* ...
* };
* @endcode
*
* For character strings (or any array), make sure you explicitly
* allocate buffers when initializing/modifying members declared with
* this macro. These members will be freed, and will thus segfault if
* they are set to string constants. You may use OT_OBJ for arrays
* that should not be freed.
*
* Note that, even when x is not nullable, x == NULL will be managed
* properly if len == 0. After copying, it may be that x != NULL, but
* if so, it is safe to free/delete x. Your code should never expect
* non-nullable arrays to be NULL, but instead should assume they
* always point to freeable/deletable memory or NULL. (Note that it
* is safe to free/delete NULL.)
*
* @param x the array; may be any lvalue expresion, e.g. @c *ptr
* @param len the length of the array; may be any expression but must
* depend only on previously declared variables
* @param nullable bool indicating whether the array might be NULL
* @param iter_var a variable created to iterate the array
* @param elem_code another member declaration, e.g. OT_OBJ
*
* @see OT_ARRAY, OBJECT_TRAVERSAL
*/
#define OT_ARRAY_EXPERT(x, len, nullable, iter_var, elem_code) \
OT_ARRAY_IMPL(x, len, iter_var, elem_code, \
Array, nullable, false, false)
/**
* Inside OBJECT_TRAVERSAL, traverse an array managed by new[] and
* delete[], assuming its contents to be objects or primitives.
*
* Equivalent to @c OT_ARRAY_EXPERT(x, len, true, i, OT_OBJ(x[i]))
*
* For character strings (or any array), make sure you explicitly
* allocate buffers when initializing/modifying members declared with
* this macro. These members will be freed, and will thus segfault if
* they are set to string constants. You may use OT_OBJ for arrays
* that should not be freed.
*
* @param x the array; may be any lvalue expression, e.g. @c *ptr
* @param len the length of the array; may be any expression but must
* depend only on previously declared variables
*
* @see OT_ARRAY_EXPERT, OT_STATIC_ARRAY, OT_ALLOC, OBJECT_TRAVERSAL
*/
#define OT_ARRAY(x, len) \
OT_ARRAY_EXPERT(x, len, true, ot__iter, OT_OBJ((x)[ot__iter]))
/**
* Inside OBJECT_TRAVERSAL, traverse an array managed by mem::Alloc
* and mem::Free and its contents.
*
* Example (array of mem::Alloc'd pointers):
* @code
* class MyClass {
* index_t num_children_;
* MyClass **children_;
* ...
* OBJECT_TRAVERSAL(MyClass} {
* OT_OBJ(num_children_);
* OT_ALLOC_EXPERT(children_, num_children_, true,
* i, OT_ALLOC(children_[i], 1));
* ...
* }
* ...
* };
* @endcode
*
* This function is also suitable for pointers--i.e. singleton
* arrays--managed by mem::Alloc and mem::Free.
*
* @param x the array; may be any lvalue expresion, e.g. @c *ptr
* @param len the length of the array; may be any expression but must
* depend only on previously declared variables
* @param nullable bool indicating whether the array might be NULL
* @param iter_var a variable created to iterate the array
* @param elem_code another member declaration, e.g. OT_OBJ
*
* @see OT_ALLOC, OBJECT_TRAVERSAL
*/
#define OT_ALLOC_EXPERT(x, len, nullable, iter_var, elem_code) \
OT_ARRAY_IMPL(x, len, iter_var, elem_code, \
Array, nullable, true, false)
/**
* Inside OBJECT_TRAVERSAL, traverse an array managed by mem::Alloc
* and mem::Free, assuming its contents to be objects or primitives.
*
* Equivalent to @c OT_ALLOC_EXPERT(x, len, true, i, OT_OBJ(x[i]))
*
* This function is also suitable for pointers--i.e. singleton
* arrays--managed by mem::Alloc and mem::Free.
*
* @param x the array; may be any lvalue expression, e.g. @c *ptr
* @param len the length of the array; may be any expression but must
* depend only on previously declared variables
*
* @see OT_ALLOC_EXPERT, OT_ARRAY, OBJECT_TRAVERSAL
*/
#define OT_ALLOC(x, len) \
OT_ALLOC_EXPERT(x, len, true, ot__iter, OT_OBJ((x)[ot__iter]))
/**
* Inside OBJECT_TRAVERSAL, traverse a null-terminated character
* string.
*
* This macro should only be used for strings with explicitly
* allocated buffers. These strings will be freed, and will thus
* segfault if they are set to string constants. (It is safe to use
* this macro with string constants in OT_CUSTOM_PRINT, though OT_OBJ
* may be used in either case.)
*
* When copying, freezing, and serializing, traversal allocates only
* as much memory as is needed to store the string. Accordingly,
* while it is always safe to shorten the string by setting some
* character to '\0', it is not generally safe to assume memory is
* available beyond the terminating null, even if your Init functions
* leave extra space. Further, because length must be computed by
* scanning the string, some traversal operations may be slower,
* especially for large strings.
*
* For faster management of strings with explicit length, instead use
* OT_ARRAY or OT_ALLOC. This macro is perfered only when it is
* inconvenient or unecessary to store the buffer's size separately.
*
* @param x the string; may be any lvalue expression, e.g. @c *ptr
* @param nullable bool indicating whether the string might be NULL
* @param alloc bool indicating whether to use mem::Alloc (true) or
* new (false)
*
* @see OT_STR, OT_ARRAY_EXPERT, OT_ALLOC_EXPERT, OT_OBJ,
* OBJECT_TRAVERSAL
*/
#define OT_STR_EXPERT(x, nullable, alloc) \
if (true) { \
ot__visitor->Name(#x, x); \
ot__visitor->Str(x, nullable, alloc); \
} else NOP // require semicolon
/**
* Inside OBJECT_TRAVERSAL, traverse a null-terminated character
* string managed by mem::Alloc and mem::Free.
*
* Equivalent to @c OT_STR_EXPERT(x, true, true)
*
* @param x the string; may be any lvalue expression, e.g. @c *ptr
*
* @see OT_STR_EXPERT, OT_ALLOC, OT_OBJ, OBJECT_TRAVERSAL
*/
#define OT_STR(x) \
OT_STR_EXPERT(x, true, true)
/**
* Called by OBJECT_TRAVERSAL to define a default constructor for your
* class.
*
* There is no need to invoke this macro unless you know what you're
* doing.
*
* @param C the name of your class
*
* @see OT_DESTRUCTOR, OBJECT_TRAVERSAL
*/
#define OT_CONSTRUCTOR(C) \
public: \
C() { \
ot__private::Unstructor ot__unstructor(this); \
OT__DefaultConstruct(this); \
} \
private:
/**
* Called by OBJECT_TRAVERSAL to define a destructor for your class.
*
* There is no need to invoke this macro unless you know what you're
* doing.
*
* @param C the name of your class
*
* @see OT_CONSTRUCTOR, OBJECT_TRAVERSAL
*/
#define OT_DESTRUCTOR(C) \
public: \
~C() { \
ot__private::Destructor<false> ot__destructor(this); \
} \
private:
/**
* Called by OBJECT_TRAVERSAL to define a copy constructor and
* assignment for your class. The copy constructor behaves
* identically to ot::InitCopy, and assignment is defined in terms of
* copy construction.
*
* There is no need to invoke this macro unless you know what you're
* doing.
*
* @param C the name of your class
*
* @see ot::InitCopy, OT_COPY_METHOD, OBJECT_TRAVERSAL
*/
#define OT_COPY_CONSTRUCTOR(C) \
public: \
C(const C &src) { \
ot__private::Copier<false> ot__copier(this, &src); \
} \
ASSIGN_VIA_RECURSION_SAFE_COPY_CONSTRUCTION(C) \
private:
/**
* Called by OBJECT_TRAVERSAL_DEPRECATED_COPIES to define a deprecated
* copy constructor and assignment for your class. This permites the
* use of these functions, but generates compiler warnings so as to
* encourage users to move towards OBJECT_TRAVERSAL_NO_COPES.
*
* There is no need to invoke this macro unless you know what you're
* doing.
*
* @param C the name of your class
*
* @see ot::InitCopy, OT_COPY_METHOD, OBJECT_TRAVERSAL
*/
#define OT_DEPRECATED_COPY_CONSTRUCTOR(C) \
public: \
COMPILER_DEPRECATED \
C(const C &src) { \
ot__private::Copier<false> ot__copier(this, &src); \
} \
ASSIGN_VIA_RECURSION_SAFE_COPY_CONSTRUCTION(C) \
private:
/**
* Called by OBJECT_TRAVERSAL to define a Renew method for your class,
* which calls the class's destructor followed by its constructor,
* thereby preparing the object for reuse.
*
* There is no need to invoke this macro unless you know what you're
* doing.
*
* @param C the name of your class
*
* @see OT_DESTRUCTOR, OT_CONSTRUCTOR, OBJECT_TRAVERSAL
*/
#define OT_RENEW_METHOD(C) \
public: \
void Renew() { \
this->~C(); \
new(this) C(); \
} \
private:
/**
* Called by OBJECT_TRAVERSAL to define an InitCopy method for your
* class, which behaves identically to ot::InitCopy and may be used in
* the absense of a copy constructor.
*
* There is no need to invoke this macro unless you know what you're
* doing.
*
* @param C the name of your class
*
* @see ot::InitCopy, OBJECT_TRAVERSAL
*/
#define OT_COPY_METHOD(C) \
public: \
void InitCopy(const C &src) { \
ot::InitCopy(this, src); \
} \
COMPILER_DEPRECATED \
void Copy(const C &src) { \
ot::InitCopy(this, src); \
} \
private:
/**
* Called by OBJECT_TRAVERSAL to define a Print method to your class,
* which behaves identically to ot::Print, but having filled the
* obvious parameter appropriately.
*
* There is no need to invoke this macro unless you know what you're
* doing.
*
* @param C the name of your class
*
* @see ot::Print, OBJECT_TRAVERSAL
*/
#define OT_PRINT_METHOD(C) \
public: \
template<typename TPrintFormat> \
const char *Print(const char *name, FILE *stream = stdout) { \
return ot::Print<TPrintFormat>(*this, name, stream); \
} \
const char *Print(const char *name, FILE *stream = stdout) { \
return Print<ot::StandardFormat>(name, stream); \
} \
private:
/**
* Adds FrozenSize, Freeze, and InitThaw methods to your class, which
* behave identically to ot::FrozenSize, ot::Freeze, and ot::InitThaw,
* but having filled the obvious parameter appropriately.
*
* @param C the name of your class
*
* @see ot::FrozenSize, ot::Freeze, ot::InitThaw, OBJECT_TRAVERSAL
*/
#define OT_FREEZE_METHODS(C) \
public: \
size_t FrozenSize() { \
return ot::FrozenSize(*this); \
} \
size_t Freeze(char *block) { \
return ot::Freeze(block, *this); \
} \
void InitThaw(char *block) { \
ot::InitThaw(this, block); \
} \
private:
/**
* Adds SerialSize, Seralize, and InitDeserialize methods to your
* class, which behave identically to ot::SerialSize, ot::Serialize,
* and ot::InitDeserialize, but having filled the obvious parameter
* appropriately.
*
* @param C the name of your class
*
* @see ot::SerialSize, ot::Serialize, ot::InitDeserialize,
* OBJECT_TRAVERSAL
*/
#define OT_SERIALIZE_METHODS(C) \
public: \
size_t SerialSize(FILE *stream) { \
return ot::SerialSize(*this); \
} \
size_t Serialize(FILE *stream) { \
return ot::Serialize(*this, stream); \
} \
size_t InitDeserialize(FILE *stream) { \
return ot::InitDeserialize(this, stream); \
} \
private:
/**
* Fills a function to be called just after default construction and
* all forms of copying to ensure that fresh and copied objects are
* not considered aliases. Code specified for OT_BECOME_NON_ALIAS
* must cause code for OT_IS_ALIAS to return false.
*
* This macro is unnecessary unless you also invoke OT_ALIAS_METHODS.
* In addition, it is unnecessary if OT_REFILL_TRANSIENTS leaves the
* object in a non-alias state. Note that it is your Init functions'
* responsibility to mark objects as non-aliases (or aliases, if
* appropriate) when they are first initialized. It is possible to
* call @c OT__BecomeNonAlias_() explicitly.
*
* @param C the name of your class
* @param following_code a block containing the code needed to
* put an object of your class into a non-alias state
*
* @see OT_ALIAS_METHODS, OT_BECOME_ALIAS, OT_IS_ALIAS,
* OBJECT_TRAVERSAL
*/
#define OT_BECOME_NON_ALIAS(C) \
public: \
friend void OT__BecomeNonAlias(C *ot__obj) { \
ot__obj->OT__BecomeNonAlias_(); \
DEBUG_ASSERT_MSG(!OT__IsAlias(ot__obj), \
"OT_BECOME_NON_ALIAS left OT_IS_ALIAS true for %s.", \
typeid(C).name()); \
} \
private: \
void OT__BecomeNonAlias_()
/**
* Fills a function to be called during InitAlias and InitSteal to
* ensure that aliased objects are considered aliases. Code specified
* for OT_BECOME_ALIAS must cause code for OT_IS_ALIAS to return true.
*
* This macro is unnecessary unless you also invoke OT_ALIAS_METHODS.
* It is possible to call @c OT__BecomeAlias_() explicitly.
*
* @param C the name of your class
* @param following_code a block containing the code needed to
* put an object of your class into an alias state
*
* @see OT_ALIAS_METHODS, OT_BECOME_NON_ALIAS, OT_IS_ALIAS,
* OBJECT_TRAVERSAL
*/
#define OT_BECOME_ALIAS(C) \
public: \
friend bool OT__Aliasable(const C *ot__obj) { \
return true; \
} \
friend void OT__BecomeAlias(C *ot__obj) { \
ot__obj->OT__BecomeAlias_(); \
DEBUG_ASSERT_MSG(OT__IsAlias(ot__obj), \
"OT_BECOME_ALIAS left OT_IS_ALIAS false for %s.", \
typeid(C).name()); \
} \
private: \
void OT__BecomeAlias_()
/**
* Fills a function to be called during destruction to decide whether
* your object is responsible for its allocated memory. Code
* specified for OT_IS_ALIAS must return true after OT_BECOME_ALIAS an
* false after OT_BECOME_NON_ALIAS.
*
* This macro is unnecessary unless you also invoke OT_ALIAS_METHODS.
*
* @param C the name of your class
* @param following_code a block containing the code needed to
* test whether an object of your class is an alias
* @returns whether the object is an alias
*
* @see OT_ALIAS_METHODS, OT_BECOME_NON_ALIAS, OT_BECOME_ALIAS,
* OBJECT_TRAVERSAL
*/
#define OT_IS_ALIAS(C) \
public: \
friend bool OT__IsAlias(C *ot__obj) { \
return ot__obj->OT__IsAlias_(); \
} \
private: \
bool OT__IsAlias_()
/**
* Adds standard aliasing methods InitAlias, InitSteal, and IsAlias to
* your class. The Init methods behave similarly to InitCopy, but
* create an alias and convert the source into an alias, respectively.
*
* This macro declaration must be used in conjunction with
* OT_BECOME_NON_ALIAS, OT_BECOME_ALIAS, and OT_IS_ALIAS as well as
* OBJECT_TRAVERSAL or one of its bretheren.
*
* Aliases behave exactly the same as their non-alias counterparts,
* but do not free allocated memory. Aliases are invalidated if
* either they or the original copy are modified except below some
* level of pointer indirection. E.g. it is safe to modify the
* contents of an aliased array, but resizing the array invalidates
* all aliases. Unfortunately, traversal cannot detect or enforce
* invalidation; it is recommended that you assert @c !IsAlias()
* before any modifications are made to an aliasable object.
*
* When copied, serialized, or frozen, aliases are converted into true
* copies. Accordingly, cyclical aliasing is forbidden except via
* transients. You can alias an alias, which has the same affect as
* aliasing the original again. To preserve aliasing relationships
* after copying, it may be necessary to instead use transients.
* (TODO: write a guide for advanced aliasing with object traversal.)
*
* Example (using an alias flag):
* @code
* class MyClass {
* bool alias_var_;
* ...
* OT_BECOME_NON_ALIAS(MyClass) {
* alias_var_ = false;
* }
* OT_BECOME_ALIAS(MyClass) {
* alias_var_ = true;
* }
* OT_IS_ALIAS(MyClass) {
* return alias_var_;
* }
* OT_ALIAS_METHODS(MyClass);
* ...
* };
* @endcode
*
* @param C the name of your class
*
* @see OT_BECOME_NON_ALIAS, OT_BECOME_ALIAS, OT_IS_ALIAS,
* OBJECT_TRAVERSAL
*/
#define OT_ALIAS_METHODS(C) \
public: \
void InitAlias(const C &src) { \
DEBUG_INIT_OK(this); \
mem::Copy(this, &src); \
ot__private::Aliaser ot__aliaser(this); \
} \
void InitSteal(C *src) { \
DEBUG_INIT_OK(this); \
DEBUG_WARN_MSG_IF(OT__IsAlias(src), \
"Stealing from an alias."); \
mem::Copy(this, src); \
ot__private::Aliaser ot__aliaser(src); \
} \
bool IsAlias() { \
return OT__IsAlias(this); \
} \
private:
/**
* Fills a function to be called just after default construction.
*
* If aliasing is defined for your class, the default behavior of this
* function is to flag freshly constructed objects as aliases;
* otherwise, it does nothing. This makes sense because aliases are
* destructed trivially, (by default) may be overwritten by standard
* Init functions, and (by default) may not be changed otherwise.
*
* If OT_BECOME_ALIAS cannot operate on freshly constructed objects,
* you must invoke this macro with, for instance, {} to disable this
* behavior. Otherwise, you need only invoke this macro if your class
* needs special construction on top of being poisoned and pointers
* nullified.
*
* In any case, default construction must always leave objects such
* that the code given for OT_DEBUG_INIT_OK returns true.
*
* Example (initializing to a non-alias):
* @code
* class MyClass {
* bool is_alias_;
* ...
* OT_DEFAULT_CONSTRUCT(MyClass) {
* is_alias_ = false;
* }
* ...
* };
* @endcode
*
* @param C the name of your class
* @param following_code a block containing default construction code
*
* @see OT_BECOME_ALIAS, OT_DEBUG_INIT_OK, OT_DEBUG_MODIFY_OK,
* OT_REFILL_TRANSIENTS, OBJECT_TRAVERSAL
*/
#define OT_DEFAULT_CONSTRUCT(C) \
public: \
friend void OT__DefaultConstruct(C *ot__obj) { \
ot__obj->OT__DefaultConstruct_(); \
} \
private: \
void OT__DefaultConstruct_()
/**
* Fills a function to be called by the DEBUG_INIT_OK macro. This
* macro is in turn called by all standard Init functions to test for
* reinitialization.
*
* If aliasing is defined for your class, the default behavior of this
* function is to return whether the object is an alias; otherwise, it
* returns true. In conjunction with the default behavior for
* OT_DEFAULT_CONSTRUCT, this ensures that freshly constructed objects
* may always be initialized. It also permits standard Init functions
* to overwrite aliases, which have no destruction responsibilities.
*
* You may optionally invoke this marco to perform an arbitrary test.
* The provided function should not have any side-effects, must return
* true for freshly constructed objects, and should return false
* otherwise. It may assume that it is called only in debug-mode.
*
* Example (using an initialization flag):
* @code
* class MyClass {
* bool initialized_;
* ...
* OT_DEFAULT_CONSTRUCT(MyClass) {
* DEBUG_ONLY(initialized_ = false);
* }
* OT_DEBUG_INIT_OK(MyClass) {
* return !initialized_;
* }
* ...
* };
* @endcode
*
* @param C the name of your class
* @param following_code a block containing the code needed to
* test whether an object of your class can be initialized
* @returns whether the object can be initialized
*
* @see DEBUG_INIT_OK, OT_DEBUG_MODIFY_OK, OT_IS_ALIAS,
* OT_DEFAULT_CONSTRUCT, OBJECT_TRAVERSAL
*/
#define OT_DEBUG_INIT_OK(C) \
public: \
friend bool OT__DebugInitOK(C *ot__obj) { \
return ot__obj->OT__DebugInitOK_(); \
} \
private: \
bool OT__DebugInitOK_()
/**
* Asserts in debug-mode that an object may be initialized, e.g. that
* no Init function has been called.
*
* If aliasing is defined for you class, this macro (by default)
* asserts that the object is an alias; otherwise, it does nothing.
* Its behavior may be changed by invoking the OT_DEBUG_INIT_OK macro.
*
* @param x the object to assert uninitiailized
*
* @see OT_DEBUG_INIT_OK
*/
#define DEBUG_INIT_OK(x) \
DEBUG_ASSERT_MSG(OT__DebugInitOK(x), \
"Reinitialization of %s detected; missing Renew()?", \
typeid(*x).name());
/**
* Fills a function to be called by the DEBUG_MODIFY_OK macro, which
* is meant to ensure that no change are made by aliases that might
* invalidate the originals.
*
* If aliasing is defined for your class, the default behavior of this
* function is to return true if and only if the object is not an
* alias; otherwise, it returns true.
*
* You may optionally invoke this marco to perform an arbitrary test.
* The provided function should not have any side-effects. It may
* assume that it is called only in debug-mode.
*
* Example (using an lock flag):
* @code
* class MyClass {
* bool locked_;
* ...
* OT_DEBUG_MODIFY_OK(MyClass) {
* return !locked_;
* }
* ...
* };
* @endcode
*
* @param C the name of your class
* @param following_code a block containing the code needed to
* test whether an object of your class can be modified
* @returns whether the object can be modified
*
* @see DEBUG_MODIFY_OK, OT_DEBUG_INIT_OK, OT_IS_ALIAS,
* OBJECT_TRAVERSAL
*/
#define OT_DEBUG_MODIFY_OK(C) \
public: \
friend bool OT__DebugModifyOK(C *ot__obj) { \
return ot__obj->OT__DebugModifyOK_(); \
} \
private: \
bool OT__DebugModifyOK_()
/**
* Asserts that an object may be modified, e.g. that it is not an
* alias.
*
* If aliasing is defined for you class, this macro (by default)
* asserts that the object is not an alias; otherwise, it does
* nothing. Its behavior may be changed by invoking the
* OT_DEBUG_MODIFY_OK macro.
*
* @param x the object to assert uninitiailized
*
* @see OT_DEBUG_MODIFY_OK
*/
#define DEBUG_MODIFY_OK(x) \
DEBUG_ASSERT_MSG(OT__DebugModifyOK(x), \
"Modification of alias/locked %s detected; missing Init?", \
typeid(*x).name());
/**
* Fills a function to be called just after copy construction,
* InitCopy, and other copying functions. This is useful, for
* example, for resetting transient pointers to their new contexts.
*
* Note that your own Init functions should either fill transients
* themselves or call @c OT__RefillTransients_() explicitly.
*
* You do not need to invoke this macro if you have no transients.
*
* Example (setting a parent pointer):
* @code
* class MyClass {
* MyClass *parent_;
* MyClass *child_;
* ...
* OBJECT_TRAVERSAL(MyClass) {
* OT_PTR(child_);
* ...
* }
* OBJECT_TRAVERSAL(MyClass) {
* OT_OBJ(parent_);
* }
* OT_REFILL_TRANSIENTS(MyClass) {
* parent_ = NULL;
* if (child_) {
* child_->parent_ = this;
* }
* }
* ...
* };
* @endcode
*
* @param C the name of your class
* @param following_code a block containing the code needed to
* construct your class's transients after a copy
*
* @see OT_DEFAULT_CONSTRUCT, OT_TRANSIENTS, OBJECT_TRAVERSAL
*/
#define OT_REFILL_TRANSIENTS(C) \
public: \
friend void OT__RefillTransients(C *ot__obj) { \
OT__BecomeNonAlias(ot__obj); \
ot__obj->OT__RefillTransients_(); \
} \
private: \
void OT__RefillTransients_()
/**
* Fills a function used to traverse your transients to debug-poison
* them on default construction and properly deallocate them on
* destruction.
*
* This function is declared similarly to OBJECT_TRAVERSAL. All of
* your class's members should occur in one of OBJECT_TRAVERSAL or
* OT_TRANSIENTS. Destruction of transients occurs before that of
* other members, meaning, e.g., transient arrays may have
* non-transient lengths.
*
* Example (declaring a parent pointer):
* @code
* class MyClass {
* double *transient_array_;
* MyClass *parent_;
* ...
* OT_TRANSIENTS(MyClass) {
* OT_ARRAY(transient_array_, len_);
* OT_OBJ(parent_);
* }
* ...
* };
* @endcode
*
* As you can see, some care is required to discriminate between
* transient pointers that should be freed and those that reference
* other memory. In the latter case, it is appropriate to use OT_OBJ.
*
* While it rarely matters, transients must technically be declared in
* destruction order. For example, if some transient array's length
* is stored in an allocated transient pointer, the array should be
* declared before the pointer. Note that this is the opposite of
* OBJECT_TRAVERSAL.
*
* @param C the name of your class
* @param following_code a block containing macro declarations for
* your class's transient members; e.g. OT_OBJ
*
* @see OT_DEFAULT_CONSTRUCT, OT_REFILL_TRANSIENTS, OBJECT_TRAVERSAL
*/
#define OT_TRANSIENTS(C) \
public: \
template<typename TVisitor> \
friend void OT__TraverseTransients(C *ot__obj, TVisitor *ot__visitor) { \
ot__obj->OT__TraverseTransients_(ot__visitor); \
} \
private: \
template<typename TVisitor> \
void OT__TraverseTransients_(TVisitor *ot__visitor)
/**
* OBJECT_TRAVERSAL_CORE calls this to give your class the bare-minium
* for object traversal functionality.
*
* There is no need to invoke this macro unless you know what you're
* doing.
*
* @param C the name of your class
* @param following_code a block containing macro declarations for
* your class's members; e.g. OT_OBJ
*
* @see OBJECT_TRAVERSAL_CORE, OBJECT_TRAVERSAL
*/
#define OBJECT_TRAVERSAL_ONLY(C) \
public: \
template<typename TVisitor> \
friend bool OT__PreTraverse(C *ot__dest, const C *ot__src, \
index_t ot__len, TVisitor *ot__visitor) { \
return ot__visitor->PreTraverse(ot__dest, ot__src, ot__len); \
} \
template<typename TVisitor> \
friend void OT__TraverseObject(C *ot__obj, TVisitor *ot__visitor) { \
ot__obj->OT__TraverseObject_(ot__visitor); \
} \
private: \
template<typename TVisitor> \
void OT__TraverseObject_(TVisitor *ot__visitor)
/**
* OBJECT_TRAVERSAL calls this to give your class traveral
* functionality and to provide standard Copy and Print methods.
*
* You can invoke this macro instead of OBJECT_TRAVERSAL if you need
* to define your own default constuctor, destructor, or copy
* constructor and assignment, but otherwise you should use
* OBJECT_TRAVERSAL or OBJECT_TRAVERSAL_NO_COPIES.
*
* @param C the name of your class
* @param following_code a block containing macro declarations for
* your class's members; e.g. OT_OBJ
*
* @see OBJECT_TRAVERSAL, OBJECT_TRAVERSAL_NO_COPY
*/
#define OBJECT_TRAVERSAL_CORE(C) \
OT_RENEW_METHOD(C) \
OT_COPY_METHOD(C) \
OT_PRINT_METHOD(C) \
OBJECT_TRAVERSAL_ONLY(C)
/**
* Fills a function to provide all standard FASTlib functionality:
* safe, debug-poisoned construction and destruction; pointer- and
* array-allocating copy construction and assignment; standard Copy
* and Print methods; and the use of other features in the ot
* namespace including serialization and freezing.
*
* This macro establishes your class as a FASTlib-complient data
* storage class and should be used whenever (reasonably) possible.
* In order to use object traversal features, your class's overall
* structure must have the following properties:
*
* @li No cyclical pointers (that cannot be reconstructed as so-called
* transients), directed or otherwise
* @li No inheritance or polymorphism, i.e. virtual functions (TODO:
* test whether this may be OK in some situations)
* @li No need for a specialized (or disabled) default constructor,
* instead using Init functions
* @li Use untraversed member types at your own risk
*
* Example (custom linked list):
* @code
* class MyClass {
* MyInfo info_;
* index_t data_len_;
* double *data_;
* MyClass *next_;
* ...
* OBJECT_TRAVERSAL(MyClass) {
* OT_OBJ(info_);
* OT_OBJ(data_len_);
* OT_ARRAY(data_, data_len_);
* OT_PTR(next_);
* ...
* }
* ...
* };
* @endcode
*
* Traversal occurs in the order that members are declared in
* OBJECT_TRAVERSAL; accordingly, array lengths must be filled before
* they are used. Also, note that the content you provide to
* OBJECT_TRAVERSAL is just a function and you can run arbitrary code
* in it, though this is only appropraite in rare circumstances such
* as computing lengths for interestingly shaped arrays.
*
* Additional or alternate functions can be specified for certain
* traversals. Notably, macros OT_DEFAULT_TRANSIENTS,
* OT_CONSTRUCT_TRANSIENTS, and OT_DESTRUCT_TRANSIENTS permit your
* class to have fields not explicitly copied but instead inferred
* from other parameters. These can reconstruct parent pointers,
* self-referential aliases, and even, for instance, pointers
* representing an arbitrary graph's edge list. Also, OT_CUSTOM_PRINT
* allows for different behavior when pretty-printing, and
* OT_CUSTOM_DESTRUCT is needed in rare occasions to safely deallocate
* your class's memory.
*
* Keep in mind that not all classes need to invoke OBJECT_TRAVERSAL.
* Some things just aren't meant to be copied, such as threads.
*
* @param C the name of your class
* @param following_code a block containing macro declarations for
* your class's members; e.g. OT_OBJ
*
* @see OBJECT_TRAVERSAL_NO_COPIES, OBJECT_TRAVERSAL_AND_ALIAS,
* OBJECT_TRAVERSAL_SHALLOW, OT_OBJ, OT_PTR, OT_ARRAY,
* OT_CUSTOM_PRINT, OT_CUSTOM_DESTRUCT, OT_DEFAULT_TRANSIENTS,
* OT_CONSTRUCT_TRANSIENTS, OT_DESTRUCT_TRANSIENTS
*/
#define OBJECT_TRAVERSAL(C) \
OT_CONSTRUCTOR(C) \
OT_DESTRUCTOR(C) \
OT_COPY_CONSTRUCTOR(C) \
OBJECT_TRAVERSAL_CORE(C)
/**
* Fills a function to provide all standard FASTlib functionality, as
* with OBJECT_TRAVERSAL, but disables copy construnction and
* assignment to prevent accidents.
*
* Your class may still be copied via the InitCopy method,
* ot::InitCopy, and traversal-based copying of containing classes.
*
* @param C the name of your class
*
* @see OBJECT_TRAVERSAL
*/
#define OBJECT_TRAVERSAL_NO_COPIES(C) \
OT_CONSTRUCTOR(C) \
OT_DESTRUCTOR(C) \
FORBID_ACCIDENTAL_COPIES(C) \
OBJECT_TRAVERSAL_CORE(C)
/**
* Fills a function to provide all standard FASTlib functionality, as
* with OBJECT_TRAVERSAL, but flags copy construnction and assignment
* as deprecated. This permites the use of these functions, but
* generates compiler warnings so as to encourage users to move
* towards OBJECT_TRAVERSAL_NO_COPES.
*
* Your class may still be copied via the InitCopy method,
* ot::InitCopy, and traversal-based copying of containing classes.
*
* @param C the name of your class
*
* @see OBJECT_TRAVERSAL
*/
#define OBJECT_TRAVERSAL_DEPRECATED_COPIES(C) \
OT_CONSTRUCTOR(C) \
OT_DESTRUCTOR(C) \
OT_DEPRECATED_COPY_CONSTRUCTOR(C) \
OBJECT_TRAVERSAL_CORE(C)
/**
* Fills a traversal function, as with OBJECT_TRAVERSAL, but
* establishes that your class is shallow, potentially speeding up
* some traversal tasks.
*
* Shallow classes must have the following properties:
* @li No owned pointers, i.e. nothing to deallocate
* @li No transients to be filled on copy (and no aliasing)
* @li All composed objects are also shallow
*
* Naturally, all non-pointer primitives are shallow, as are all
* classes that contain only non-pointer primitives. Traversal is
* accelerated most notably when it can bit-copy entire arrays of
* shallow objects.
*
* Shallow classes use default C++ copy constructors and assignment
* rather than anything defined by object traversal. Further, their
* constructors and destructors simply debug-poison contained memory.
*
* @param C the name of your class
* @param following_code a block containing macro declarations for
* your class's members; e.g. OT_OBJ
*
* @see OBJECT_TRAVERSAL
*/
#define OBJECT_TRAVERSAL_SHALLOW(C) \
public: \
C() { \
mem::DebugPoison(this); \
} \
~C() { \
mem::DebugPoison(this); \
} \
friend bool OT__Shallow(const C *ot__obj) { \
return true; \
} \
private: \
OT_IS_ALIAS(C) { return false; } \
OT_DEFAULT_CONSTRUCT(C) {} \
OT_REFILL_TRANSIENTS(C) {} \
OT_TRANSIENTS(C) {} \
OBJECT_TRAVERSAL_CORE(C)
/**
* Redefines traversal in the special case of ot::Print or the Print
* method. This is useful if normal traversal necessary for
* successful copying, serialization, etc. is not particularly
* human-readable.
*
* You may emit transients via OT_CUSTOM_PRINT by including them as
* normal with OT_OBJ and its bretheren, but be careful not to create
* cycles by traversing pointers to parents. Further, you may emit
* the results of functions or expressions.
*
* Example (printing a managed string):
* @code
* class MyClass {
* char *c_str() { ... }
* ...
* OT_CUSTOM_PRINT(MyClass) {
* OT_STR(c_str());
* }
* ...
* };
* @endcode
*
* @param C the name of your class
* @param following_code a block containing the code needed to
* print your class; may consist of, e.g., OT_OBJ
*
* @see OBJECT_TRAVERSAL, ot::Print
*/
#define OT_CUSTOM_PRINT(C) \
private: \
template<typename TPrintFormat> \
void OT__TraverseObject_(ot__private::Printer<TPrintFormat> *ot__visitor)
/**
* Redefines traversal in the special case of the destructor or
* ot::SemiDestruct. This is necessary if normal traversal would
* deallocate memory needed for the proper destruction of other
* members.
*
* Example (deleting an array of variable length arrays of pointers):
* @code
* class MyClass {
* index_t rows_;
* index_t *cols_;
* MyClass ***children_;
* ...
* OBJECT_TRAVERSAL(MyClass) {
* OT_OBJ(rows_);
* OT_ARRAY(cols_, rows_);
* OT_ARRAY_EXPERT(children_, rows_, true,
* i, OT_ARRAY_EXPERT(children_[i], cols_[i], false,
* j, OT_PTR_EXPERT(children_[i][j], false,
* OT_OBJ(*children_[i][j]))));
* }
* OT_CUSTOM_DESTRUCT(MyClass) {
* OT_ARRAY_EXPERT(children_, rows_, true,
* i, OT_ARRAY_EXPERT(children_[i], cols_[i], false,
* j, OT_PTR_EXPERT(children_[i][j], false,
* OT_OBJ(*children_[i][j]))));
* OT_ARRAY(cols_, rows_);
* OT_OBJ(rows_);
* }
* ...
* };
* @endcode
*
* Note that destruct code is typically "safe" when it is the reverse
* of construction code. Unfortunately, this is beyond the power of
* OBJECT_TRAVERSAL's automation. On the other hand, it takes really
* contrived types to defeat normal traversal behavior. Further, the
* example could be rewritten to use ArrayLists or other types that
* keep length and pointer allocated together.
*
* @param C the name of your class
* @param following_code a block containing the code needed to
* destruct your class; may consist of, e.g., OT_OBJ
*
* @see OBJECT_TRAVERSAL, ot::Print
*/
#define OT_CUSTOM_DESTRUCT(C) \
private: \
template<bool t_semi> \
void OT__TraverseObject_(ot__private::Destructor<t_semi> *ot__visitor)
#define NEED_OTRAV_IMPL
#include "otrav_impl.h"
#undef NEED_OTRAV_IMPL
/**
* Object traversal utilities for printing, copying, freezing, and
* serialization.
*
* These tools operate on all classes that invoke the OBJECT_TRAVERSAL
* macro or an equivalent, which includes all core FASTlib data
* structures. Specifically, they provide:
*
* @li Pretty-printing for debugging or saving to XML
* @li A generic copy routine that works on primitives, untraversed
* classes with copy constructors, and all traversed classes
* @li Object "freezing", which copies the object's contents into a
* flat buffer for easy transport and fast reuse
* @li Serialization and deserialization to and from file streams.
*
* You can incorporate many of these functions as methods of your
* class with the OT_FREEZE_METHODS and OT_SERIALIZE_METHODS macors.
* Default OBJECT_TRAVERSAL already gives your class Print and
* InitCopy methods.
*
* @see OBJECT_TRAVERSAL, OT_FREEZE_METHODS, OT_SERIALIZE_METHODS
*/
namespace ot {
/**
* Pretty-print an object using a given print format.
*
* Print formats are classes used by the traversal printer to emit
* objects in different ways. At current, two print formats are
* offered: ot::StandardFormat and ot::XMLFormat.
*
* You can define your own print format; it's interface must
* include:
*
* @li A constructor that accepts a FILE *stream.
* @li A templated function @c Untraversed(const T &obj)
* @li For each base type, templated/overloaded functions
* @c Primitive(const char *name, index_t index,
* const char *type, T val)
* @li A templated Primitive function that works on pointers
* @li A templated function
* @c Open(const char *name, index_t index,
* const char *type, const T &obj, index_t len = -1)
* @li A templated function
* @c Close(const char *name, const T &obj)
*
* The Untraversed and Primitive functions are responsible for
* printing their respective types. Open and Close are called
* before and after traversing the contents of objects, pointers,
* and arrays. Only allocated arrays will have nonnegative lengths,
* and unallocated (NULL) arrays and pointers are handled by
* Primitive. See FOR_ALL_PRIMITIVES_DO to define many Primitive
* functions with a single macro.
*
* @param obj the object to be printed
* @param name the printed name attributed to the object
* @param stream the output stream
* @return name, permitting reference, for instance, within printf
*
* @see OBJECT_TRAVERSAL, OT_CUSTOM_PRINT
*/
template<typename TPrintFormat, typename T>
const char *Print(const T &obj, const char *name, FILE *stream = stdout) {
ot__private::Printer<TPrintFormat> ot__printer(obj, name, stream);
return name;
}
template<typename TPrintFormat, typename T>
void Print(const T &obj, FILE *stream = stdout) {
ot__private::Printer<TPrintFormat> ot__printer(obj, "_", stream);
}
/**
* Pretty-print an object using the standard print format.
*
* Example (debug message):
* @code
* DEBUG_ASSERT_MSG(node.parent_ != NULL, "NULL parent (see %s).",
* ot::Print(node, "faulty_node", stderr));
* @endcode
*
* @param obj the object to be printed
* @param name the printed name attributed to the object
* @param stream the output stream
* @return name, permitting reference, for instance, within printf
*
* @see OBJECT_TRAVERSAL, OT_CUSTOM_PRINT
*/
template<typename T>
const char *Print(const T &obj, const char *name, FILE *stream = stdout) {
return Print<ot::StandardFormat>(obj, name, stream);
}
template<typename T>
void Print(const T &obj, FILE *stream = stdout) {
Print<ot::StandardFormat>(obj, stream);
}
/**
* Copy an object into a specified destination.
*
* This function works for primitive types and untraversed types
* with copy constructors by calling their copy construstors. For
* traversed types, copying behaves identically to the copy
* constructor if it exists, but no copy constructor is required.
*
* This function does not allocate memory for the top level of the
* copied object, permitting you to copy objects onto the stack or
* into your own allocated buffers.
*
* @param dest an uninitialized object to receive the copy
* @param src the object to be copied
*
* @see OBJECT_TRAVERSAL, OT_CONSTRUCT_TRANSIENTS
*/
template<typename T>
inline void InitCopy(T *dest, const T &src) {
DEBUG_INIT_OK(dest);
ot__private::Copier<false> ot__copier(dest, &src);
}
/**
* Determine the buffer size needed to store a frozen copy of an
* object.
*
* @param obj the object whose frozen size should be computed
* @return the size of the object in bytes after freezing
*
* @see Freeze, OBJECT_TRAVERSAL
*/
template<typename T>
inline size_t FrozenSize(const T &obj) {
ot__private::Freezer<true> ot__freezer(NULL, obj);
return ot__freezer.size();
}
/**
* Copy an object's contents into flat buffer for easy transport,
* normalizing pointers to offsets into the buffer for fast reuse.
*
* Freezing is the prefered method for transmitting objects from
* machine to machine, storing them in temporary files, and other
* situations where speed is favored over efficient use of space.
* The primary advantage over rote serialization is that
* ot::SemiThaw permits (formerly) frozen objects to be used
* in-place. There is no need to allocate memory separately from
* the buffer holding the semi-thawed copy, and ot::SemiFreeze can
* quickly repackage the object for subsequent transmittion.
*
* Example (sending an object):
* @code
* MyClass my_obj;
* ... // Initialize my_obj
* char *buf = mem::Alloc<char>(ot::FrozenSize(my_obj));
* ot::Freeze(buf, my_obj);
* ... // Transmit over the network
* mem::Free(buf);
* @endcode
*
* Example (receiving an object):
* @code
* char *buf;
* ... // Fill buf from network
* MyClass *thawed_obj = ot::SemiThaw(buf);
* ... // Use thawed_obj
* ot::SemiDestruct(thawed_obj);
* mem::Free(buf);
* @endcode
*
* To be more specific, ot::Freeze forms frozen objects by
* bit-copying the data structure into a flat (e.g. contiguous)
* buffer. Traversed pointers and arrays are processed depth-first
* and normalized to the bit-offsets of their represented regions
* from the beginning of the buffer. It is always safe to bit-copy
* frozen objects to new locations and their containing buffers may
* be freed without additional memory maintenance.
*
* Afterward, ot::SemiThaw converts a frozen object into a
* semi-object by adding the buffer's memory location to all
* contained pointers and reconstructing transients. These are
* fully operational up to the ability to allocate or free
* non-transient fields. It is possible to bit-copy semi-objects to
* new locations, but after having done so, one must call either
* ot::Relocate or ot::SemiCopy, the former simply updating pointer
* positions (semantically invalidating the old location) and the
* latter reconstructing transients as well (leaving the old
* location valid). Alternately, you may call ot::SemiFreeze to
* revert the copy to a frozen state. Semi-objects must be
* explicitly destructed via ot::SemiDestruct, or may be frozen
* in-place with ot::SemiFreezeDestruct.
*
* You can form a normal object directly from a frozen object with
* ot::InitThaw. Keep in mind that ot::InitCopy works when copying
* from a semi-object.
*
* @param block the memory location to receive the frozen copy
* @param src the object to be frozen
* @return the size of the object in bytes after freezing
*
* @see FrozenSize, InitThaw, Relocate, SemiFreeze, SemiCopy,
* SemiThaw, SemiDestruct, SemiFreezeDestruct,
* OT_FREEZE_METHODS, Serialize, OBJECT_TRAVERSAL
*/
template<typename T>
inline size_t Freeze(char *block, const T &src) {
ot__private::Freezer<false> ot__freezer(block, src);
return ot__freezer.size();
}
/**
* Copy from a frozen object into a specified destination.
*
* Has the same effect as ot::SemiThaw followed by ot::InitCopy.
*
* @param dest an uninitialized object to receive the copy
* @param block the frozen object to be copied
*
* @see Freeze, SemiThaw, InitCopy, OBJECT_TRAVERSAL
*/
template<typename T>
inline void InitThaw(T *dest, const char *block) {
DEBUG_INIT_OK(dest);
ot__private::Copier<true>
ot__copier(dest, reinterpret_cast<const T *>(block));
}
/**
* Fixes the pointers and reconstructs transients of a semi-object
* assuming it has already been bit-copied into a new location.
*
* It is OK for the original location to have already been freed.
*
* @param block the new location of the semi-object to freeze
* @param orig the original location of the semi-object
* @return block recast to a T pointer
*
* @see Freeze, SemiThaw, Relocate, SemiFreeze
*/
template<typename T>
inline T *SemiCopy(char *block, const T *orig) {
DEBUG_WARN_MSG_IF(block == reinterpret_cast<const char *>(orig),
"In-place SemiCopy may leak memory; probably incorrect.");
ot__private::Relocator<false, true> ot__relocator(block, orig);
return reinterpret_cast<T *>(block);
}
template<typename T>
inline T *SemiCopy(char *block, const char *orig) {
return SemiCopy(block, reinterpret_cast<const T *>(orig));
}
/**
* Re-freezes a semi-object assuming it has already been bit-copied
* into a new location.
*
* Example (freezing a semi-object):
* @code
* MyClass *semi_obj;
* char *freeze_buf;
* ... // Fill semi_obj with a semi-object of size bytes
* freeze_buf = mem::AllocCopyBytes<char>(semi_obj, bytes);
* ot::SemiFreeze(freeze_buf, semi_obj);
* ...
* ot::SemiDestruct(semi_obj);
* @endcode
*
* You should not use this freeze semi-objects in place because this
* precludes properly destructing their transients. Instead,
* bit-copy the semi-object to a new location and freeze it their,
* or use ot::SemiFreezeDestruct. It is OK for the original
* location to have already been freed.
*
* @param block the new location of the semi-object to freeze
* @param orig the original location of the semi-object
*
* @see Freeze, SemiFreezeDestruct, Relocate, SemiCopy
*/
template<typename T>
inline void SemiFreeze(char *block, const T *orig) {
DEBUG_WARN_MSG_IF(block == reinterpret_cast<const char *>(orig),
"In-place SemiFreeze may leak memory; use SemiFreezeDestruct.");
ot__private::Relocator<true, false> ot__relocator(block, orig);
}
template<typename T>
void SemiFreeze(char *block, const char *orig) {
SemiFreeze(block, reinterpret_cast<const T *>(orig));
}
/**
* Thaws a frozen object by fixing pointers and reconstructing
* transients.
*
* Equivalent to ot::SemiCopy with orig equal to NULL.
*
* @param block the frozen object to be thawed
* @return block recast to a T pointer
*
* @see Freeze, SemiFreeze
*/
template<typename T>
inline T *SemiThaw(char *block) {
return SemiCopy(block, reinterpret_cast<const T *>(NULL));
}
/**
* Destructs a semi-object's transients and debug-poisons its
* memory.
*
* @param obj the semi-object to destroy
*
* @see Freeze, SemiFreezeDestruct
*/
template<typename T>
inline void SemiDestruct(T *obj) {
ot__private::Destructor<true> ot__destructor(obj);
}
template<typename T>
inline void SemiDestruct(char *block) {
SemiDestruct(reinterpret_cast<T *>(block));
}
/**
* Re-freezes a semi-object after destructing its transients.
*
* @param obj the semi-object to destroy
* @return obj recast to a char pointer
*
* @see Freeze, SemiDestruct, SemiFreeze
*/
template<typename T>
inline char *SemiFreezeDestruct(T *obj) {
ot__private::Relocator<true, true>
ot__relocator(reinterpret_cast<char *>(obj), obj);
return reinterpret_cast<char *>(obj);
}
template<typename T>
inline char *SemiFreezeDestruct(char *block) {
return SemiFreezeDestruct(reinterpret_cast<T *>(block));
}
/**
* Determine the length in bytes of a serialized version of an
* object.
*
* @param obj the object whose serial size should be computed
* @return the size of the object in bytes after serialization
*
* @see Serialize, Deserialize, OBJECT_TRAVERSAL
*/
template<typename T>
inline size_t SerialSize(const T &obj, FILE *stream) {
ot__private::Serializer<true> ot__serializer(obj, stream);
return ot__serializer.size();
}
/**
* Write a serialized version of an object to a file stream.
*
* Serialization is more compact than freezing because it emits an
* object's members without the padding. Further, it emits bools to
* indicate whether or not a pointer is NULL (or nothing at all if
* the pointer is not nullable), inferring the position of the
* pointer's contents by depth-first traversal. This violates the
* alignment and stride constraints of various platforms, and thus
* serialization is only available in the form of writing to and
* reading from a file stream.
*
* Example (saving an object to file):
* @code
* MyClass my_obj;
* ... // Initialize my_obj
* FILE *f_out = fopen("out.dat", "wb");
* ot::Serialize(my_obj, f_out);
* fclose(f_out);
* @endcode
*
* @param obj the object to serialize
* @param stream a file stream openned for binary output
* @return number of bytes written to the stream
*
* @see Deserialize, SerialSize, Freeze, OBJECT_TRAVERSAL
*/
template<typename T>
inline size_t Serialize(const T &obj, FILE *stream) {
ot__private::Serializer<false> ot__serializer(obj, stream);
return ot__serializer.size();
}
/**
* Read a serialized version of an object from a file stream,
* reconstructing its transients.
*
* Example (reading an object from file):
* @code
* MyClass my_obj;
* FILE *f_in = fopen("in.dat", "rb");
* ot::Deserialize(&my_obj, f_in);
* fclose(f_in);
* @endcode
*
* @param dest an uninitialized object to receive the copy
* @param stream a file stream openned for binary input
* @return number of bytes read from the stream
*
* @see Serialize, SerialSize, Freeze, OBJECT_TRAVERSAL
*/
template<typename T>
inline size_t InitDeserialize(T *dest, FILE *stream) {
DEBUG_INIT_OK(dest);
ot__private::Deserializer ot__deserializer(dest, stream);
return ot__deserializer.size();
}
/**
* Equivalent to mem::Construct, but exploits properties of shallow
* types.
*/
template<typename T>
inline T *Construct(T *array, size_t elems = 1) {
if (OT__ShallowOrPtr(array)) {
return mem::DebugPoison(array, elems);
} else {
return mem::Construct(array, elems);
}
}
/**
* Equivalent to mem::Destruct, but exploits properties of shallow
* types.
*/
template<typename T>
inline T *Destruct(T *array, size_t elems = 1) {
if (OT__ShallowOrPtr(array)) {
return mem::DebugPoison(array, elems);
} else {
return mem::Destruct(array, elems);
}
}
/**
* Equivalent to mem::CopyConstruct, but exploits properties of
* shallow types and does not require a copy constructor to be
* defined for traversed types.
*
* Note the absense of DEBUG_INIT_OK. This ensures that this
* function works on arrays of unconstructed objects.
*/
template<typename T>
inline T *CopyConstruct(T *dest, const T *src, size_t elems = 1) {
ot__private::Copier<false> ot__copier(dest, src, elems);
return dest;
}
/**
* Equivalent to mem::RepeatConstruct, but does not require a copy
* constructor to be defined for traversed types.
*
* Note the absense of DEBUG_INIT_OK. This ensures that this
* function works on arrays of unconstructed objects.
*/
template<typename T>
inline T *RepeatConstruct(T *array, const T &init, size_t elems) {
for (size_t i = 0; i < elems; ++i) {
ot__private::Copier<false> ot__copier(array + i, &init);
}
return array;
}
////////// Deprecated //////////////////////////////////////////////
/** Renamed InitCopy */
template<typename T>
COMPILER_DEPRECATED
void Copy(const T &src, T *dest) {
InitCopy(dest, src);
}
/** Renamed FrozenSize */
template<typename T>
COMPILER_DEPRECATED
size_t PointerFrozenSize(const T &obj) {
return FrozenSize(obj);
}
/** Renamed Freeze */
template<typename T>
COMPILER_DEPRECATED
void PointerFreeze(const T &obj, char *block) {
Freeze(block, obj);
}
/** Renamed SemiFreeze */
template<typename T>
COMPILER_DEPRECATED
void PointerRefreeze(const T *src, char *dest) {
SemiFreeze(dest, src);
}
/** Renamed SemiThaw */
template<typename T>
COMPILER_DEPRECATED
T *PointerThaw(char *block) {
return SemiThaw<T>(block);
}
/** Behavior similar to SemiCopy; beware memory leaks */
template<typename T>
COMPILER_DEPRECATED
void PointerRelocate(const char *old_loc, char *new_loc) {
// leaks memory for allocated transients
SemiCopy<T>(new_loc, old_loc);
}
};
////////// Deprecated ////////////////////////////////////////////////
#define OT_MY_OBJECT OT_OBJ
#define OT_MY_ARRAY OT_STATIC_ARRAY
#define OT_MALLOC_ARRAY OT_ALLOC
#define OT_PTR_NULLABLE OT_PTR
#define OT_FIX OT_REFILL_TRANSIENTS
#define OT_DEF OBJECT_TRAVERSAL
#define OT_DEF_BASIC OBJECT_TRAVERSAL
#endif /* BASE_OTRAV_H */