# as a non-submodule
git clone --recursive https://github.com/mgood7123/LibObj
## as a submodule
git submodule add https://github.com/mgood7123/LibObj path/to/LibObj
git submodule update --init --recursive
# building and testing
make test_debug
LibObj is a C++17 compatible library, exposing an Object interface similar to Java's Object
class
a standard implementation of Obj
is provided, along with a Obj_Example
to demonstrate a T*
referencing object
LibObj::Obj_Base
is the base class of LibObj::Obj
,
much like all objects extend from Object
in Java, all subclasses of Obj_Base
and Obj
are ultimately a subclass of Obj_Base
the as
function can be used to static_cast
an Obj_Base
to another Obj_Base
obj.as(Obj2)
returnsstatic_cast<const T &>(*this);
after astatic_assert
to make sureT
derives fromObj_Base
the following macros can be used to correctly setup the object
LIOBJ_BASE
LIBOBJ_BASE_ABSTRACT
LIBOBJ_BASE_WITH_CUSTOM_CLONE
LIBOBJ_BASE_ABSTRACT_WITH_CUSTOM_CLONE
the macro LIBOBJ_POINTER_ASSIGN
can be used to assign a pointer from one object to another object, this implies shared non-ownership
- if the pointer must have shared reference (but not owning) between objects then the macro
LIBOBJ_POINTER_ASSIGN
should be used - if the pointer must have shared ownership between objects then
std::shared_ptr
should be used - if the pointer must have only a single owner then
std::unique_ptr
should be used - otherwise pointer duplication code should be used (such that it is as-if
clone
was used to clone thepointer allocation
and thepointer value
)
the clone
mechanism is implemented via polymorphic inheritence and macros, and will return an allocation
of the bottom-most subclass
of the object it has been invoked upon
all subclasses of Obj_Base
inherit Clone
if set up correctly
the default implementation is
// called from clone() after type checking
obj->from(*this);
// no return needed
the actual implementation is
T* p = baseClone();
if (this->getObjId() != p->getObjId()) {
// log and throw
}
clone_impl(p); // default impl : obj->from(*this);
return std::shared_ptr<T>(static_cast<T *>(p), [](auto p) { delete static_cast<T *>(p); });
clone
is automatically provided via the LIBOBJ_BASE
macro among other important functions
a custom implementation can be given via the LIBOBJ_BASE_WITH_CUSTOM_CLONE
macro
LIBOBJ_BASE_WITH_CUSTOM_CLONE(X) {
// the pointer: X * obj
// is available for use inside this function
//
// type checking gaurentees the actual type is itself X
// if type checking fails then an error is thrown
//
}
an unsafe clone can be done via
auto x = o->baseClone(); // must be deallocated manually
if (x->getObjId() != o->getObjId()) {
// can do something here if types dont match
}
// attempt to clone even if types dont match, UB may happen
o->clone_impl(x);
// ...
delete x;
like Java
, if a class does not implement Clone
then its base class will be tried for Clone
the same is true here
template <typename T>
struct Obj_Example2 : public Obj_Example<T> {
using Obj_Example<T>::Obj_Example;
LIBOBJ_BASE(Obj_Example2<T>)
};
Obj_Example
has a custom clone override, and Obj_Example2
has no custom clone override
invoking clone
on Obj_Example2
will invoke clone_impl
on the base class Obj_Example
or Obj
if Obj_Example
did not have a custom clone override
[LOG] cloning o16 from o15 of type LibObj::Obj_Example2<int>
Obj_Example2<T>CLONE
constructing with assigned value: nullptr
Obj_Example<T>CLONE_IMPL
Obj_Example CLONE
other: LibObj::Obj_Example2<int>@1e3340f, value: 1
assigned value: 1
[LOG] o16 = LibObj::Obj_Example2<int>@1e3365f, value: 1
[LOG] DONE cloned o16 from o15 of type LibObj::Obj_Example2<int>
the object name
can be obtained via getObjId().name()
all copy and move constructors
are explicitly marked as = delete
due to the fact that constructors in C++ do not correctly participate in overload resolution
we provide a from
function to assign objects to other objects, in both copy
and move
form
from can be overriden via LIBOBJ_OVERRIDE__FROM_COPY
and LIBOBJ_OVERRIDE__FROM_MOVE
LIBOBJ_OVERRIDE__FROM_COPY {
std::cout << "other: " << other << "\n";
from_(other);
}
LIBOBJ_OVERRIDE__FROM_MOVE {
std::cout << "other: " << other << "\n";
from_(std::move(other));
}
from
is the recommended way to assign objects
clone
and from
are similar but semantically different
-
from
imples the object should becopied
ormoved
to another object,optionally being converted from one type to another
-
clone
imples the object should beduplicated
,conversion should be skipped if possible
typically from
would be used for conversion(optional)/copying/moving
without allocating
a new object
clone
would be used when the making a copy
of an object with an unknown/abstract type
operator==
can be overrided via LIBOBJ_OVERRIDE__EQUALS
LIBOBJ_OVERRIDE__EQUALS {
if (getObjId() != other.getObjId()) {
return false;
}
return value == static_cast<const Obj_Example<T> &>(other).value;
}
toStream()
is used for printing the object to streams, use LIBOBJ_OVERRIDE__STREAM
to override
LIBOBJ_OVERRIDE__STREAM {
Obj_Example_Base::toStream(os) << ", value: ";
if (value == nullptr) {
return os << "nullptr";
} else {
return os << *value;
}
}
toString()
constructs a string representation of the object via the toStream(os)
function
hashCode()
returns a hash of the object itself, tho implementors are encouraged to use equality instead of identity
a specialization exists for std::hash<LibObj::Obj_Base>
and invokes the hashCode()
function
a specialization exists for std::hash<const LibObj::Obj_Base>
and invokes the hashCode()
function
-
Obj
returns a hash ofthis
by default -
subclasses should implement
hash
by excludingthis
andObj::hashCode
from the hash calculation to allow for multiple copies of the same object to produce the same hash as long as that object compairs equal viaoperator==
HashCodeBuilder
is provided for convinient implementation of hashCode from one or more values
LIBOBJ_OVERRIDE__HASHCODE {
return HashCodeBuilder().add(value).hash;
}
struct HashCodeBuilder {
// add specialization for Obj_Base and all subclasses of Obj_Base
template <typename U, typename std::enable_if<
std::is_base_of<Obj_Base, U>::value,
bool>::type = true>
HashCodeBuilder & add(const U & value) ...
// add specialization for everything else
template <typename U, typename std::enable_if<
!std::is_base_of<Obj_Base, U>::value,
bool>::type = true>
HashCodeBuilder & add(const U & value) ...
template <typename T>
std::string hashAsHex(const T & value) ...
std::string hashAsHex() ...
};