diff --git a/.gitignore b/.gitignore index 0f74202..6b65d59 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ build/ /examples/libc99/libc99.so /test/test_identity /test/.pEp/ +/test/test_sleeve diff --git a/examples/libc99/libc99.h b/examples/libc99/libc99.h index 74f9d41..3c4abdd 100644 --- a/examples/libc99/libc99.h +++ b/examples/libc99/libc99.h @@ -62,7 +62,8 @@ extern "C" { // T* C99_Status c99_supply_basic(int* basic); C99_Status c99_supply_struct(C99_A* a); - C99_Status c99_supply_string(char** c_str); + // Not valid + // C99_Status c99_supply_string(char** c_str); // provide // in parm, ownership goes to callee diff --git a/src/POTS.hh b/src/POTS.hh new file mode 100644 index 0000000..9086930 --- /dev/null +++ b/src/POTS.hh @@ -0,0 +1,897 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../examples/libc99/libc99.h" + +#ifndef LIBPEPADAPTER_POTS_HH + #define LIBPEPADAPTER_POTS_HH + +// TODO: this needs to go +using namespace pEp; + + +// Python Overlay Type System + + +namespace POTS { + class BasicSleeve; + bool operator==(const BasicSleeve& a, const BasicSleeve& b); + bool operator!=(const BasicSleeve& a, const BasicSleeve& b); + + //--------------------------------------------------------------------------------------------- + class BasicSleeve { + public: + BasicSleeve() = default; + + // Create as a member + BasicSleeve(const std::string& name, BasicSleeve& parent) : _name{ name }, _parent{ parent } + { + _parent.value().get().add_member(*this); + } + + BasicSleeve(const BasicSleeve& rhs, BasicSleeve& parent) : + _name{ rhs._name }, + _parent(parent) + { + _parent.value().get().add_member(*this); + } + + BasicSleeve(const BasicSleeve& rhs) : _name{ rhs._name } {} + + // Add a member + void add_member(BasicSleeve& member) + { + try { + _members.insert( + std::pair(member.get_name().value(), member)); + } catch (const std::bad_optional_access& e) { + throw std::runtime_error("cant add member without a name"); + } + } + + // leave parent + void remove_member(BasicSleeve& member) + { + try { + _members.erase(_members.find(member.get_name().value())); + } catch (const std::bad_optional_access& e) { + throw std::runtime_error("remove_member - cant remove member without a name"); + } catch (...) { + throw std::runtime_error("remove_member - unknown error"); + } + } + + virtual BasicSleeve& operator=(const BasicSleeve& rhs) + { + pEpLog("called"); + if (_members.empty()) { + throw std::runtime_error("struct has no members"); + } + + for (std::pair member : _members) { + try { + BasicSleeve& other = rhs._members.at(member.first); + member.second = other; + } catch (const std::out_of_range& e) { + throw std::runtime_error("ASSFSD"); + } + } + return *this; + } + + + virtual bool cmp(const BasicSleeve& rhs) const + { + if (_members.empty()) { + throw std::runtime_error("struct has no members"); + } + + for (std::pair member : _members) { + try { + BasicSleeve& other = rhs._members.at(member.first); + if (other != member.second) { + return false; + } + } catch (const std::out_of_range& e) { + return false; + } + } + return true; + } + + virtual void leave_ownership_recurse() + { + for (std::pair member : _members) { + member.second.leave_ownership_recurse(); + } + } + + virtual void rebind() + { + for (std::pair member : _members) { + member.second.rebind(); + } + } + + virtual std::string to_string(int indent = 0) const + { + std::stringstream ss{}; + for (std::pair member : _members) { + ss << member.second.to_string(indent); + } + return ss.str(); + } + + virtual std::string repr(int indent = 0) const + { + std::stringstream ss{}; + for (std::pair member : _members) { + ss << member.second.repr(indent); + } + return ss.str(); + } + + std::optional get_name() + { + return _name; + } + + virtual ~BasicSleeve() + { + try { + if (_parent) { + _parent.value().get().remove_member(*this); + } + } catch (...) { + //TODO: + // haha sth went wrong and you have no idea.... + } + } + + protected: + std::optional _name{}; + std::optional> _parent{}; + std::map _members{}; + }; + + + // template + // class Sleeve; + + //--------------------------------------------------------------------------------------------- + // VALUE + //--------------------------------------------------------------------------------------------- + template + class Sleeve { + // class Sleeve::value || std::is_enum::value || std::is_class::value>::type> + // : public BasicSleeve { + + public: + using ResolverFunc = std::function; + // Creation + // -------- + Sleeve() = delete; + + // Create a hosting sleeve + explicit Sleeve(const T& val) : + _target_resolver{ [this]() { return &_hosted_obj; } }, + _hosted_obj(val) + { + pEpLogClass("hosting sleeve"); + } + + // // create a static sleeve + // // using static binding to an existing object + // explicit Sleeve(T* target) : _effective_resolver{ [target]() { return target; } } + // { + // pEpLogClass("static sleeve"); + // } + + // create a dynamic sleeve + // using dynamic binding (to a per-access determined object using the custom resolver) + explicit Sleeve(ResolverFunc target_resolver) : + _target_resolver{ std::move(target_resolver) } + { + pEpLogClass("dynamic sleeve"); + } + + // create a dynamic sleeve + // using dynamic binding (to a per-access determined object using the custom target_resolver) + // explicit Sleeve(BasicSleeve& parent, ResolverFunc target_resolver) : + // BasicSleeve(parent), + // _effective_resolver{ std::move(target_resolver) } + // { + // pEpLogClass("dynamic sleeve"); + // } + + // copy + // copy constructor will always create a hosting sleeve + explicit Sleeve(const Sleeve& rhs) : + _target_resolver{ [this]() { return &_hosted_obj; } }, + _hosted_obj{ rhs.obj() } + { + pEpLogClass("copy ctor"); + } + + // Assignment + // ---------- + // takes a copy + Sleeve& operator=(const Sleeve& rhs) + { + pEpLogClass("called"); + *target() = const_cast&>(rhs).obj(); + return *this; + } + + // takes a copy + Sleeve& operator=(const T& rhs) + { + pEpLogClass("called"); + *target() = rhs; + return *this; + } + + // Comparison + // ---------- + // value comparison + + // virtual bool operator==(const BasicSleeve& rhs) const override + bool operator==(const Sleeve& rhs) const + { + pEpLogClass("called"); + return _this()->obj() == const_cast&>(rhs).obj(); + } + + bool operator==(const T& rhs) const + { + pEpLogClass("called"); + return _this()->obj() == rhs; + } + + bool operator!=(const Sleeve& rhs) const + { + pEpLogClass("called"); + return !operator==(rhs); + } + + bool operator!=(const T& rhs) const + { + pEpLogClass("called"); + return !operator==(rhs); + } + + // object comparison + bool is(const Sleeve& rhs) const + { + pEpLogClass("called"); + return id() == rhs.id(); + } + + bool is(const T& rhs) const + { + pEpLogClass("called"); + return _this()->target() == &rhs; + } + + // Accessors + // --------- + + // Access the object data r/w + operator T() const + { + pEpLogClass("called"); + return obj(); + } + + // Access the object data r/w + T obj() const + { + pEpLogClass("called"); + return *(_this()->target()); + } + + // Access the object pointer r/w + T* target() + { + // pEpLogClass("called"); + return _target_resolver(); + } + + // Misc + // ---- + // obj_pointer as id + uintptr_t id() const + { + return reinterpret_cast(_this()->target()); + } + + std::string to_string() const //override + { + int indent = 0; + std::stringstream builder; + builder << std::endl; + builder << std::string(indent, '\t') << "{" << std::endl; + indent++; + builder << std::string(indent, '\t') << "this : " << CXX::Inspect::all(_this()) + << std::endl; + builder << std::string(indent, '\t') + << "target : " << CXX::Inspect::all(_this()->target()) << std::endl; + builder << std::string(indent, '\t') + << "_hosted_obj: " << CXX::Inspect::all(_this()->obj()) << std::endl; + + indent--; + builder << std::string(indent, '\t') << "}"; + return builder.str(); + } + + // static bool log_enabled; + Adapter::pEpLog::pEpLogger logger{ typeid(T).name(), true }; + + protected: + Adapter::pEpLog::pEpLogger& m4gic_logger_n4me = logger; + + ResolverFunc _target_resolver; + T _hosted_obj; + + private: + Sleeve* _this() const + { + return const_cast*>(this); + } + }; + + + //--------------------------------------------------------------------------------------------- + // POINTER + //--------------------------------------------------------------------------------------------- + + template + class Sleeve + // class Sleeve::value || std::is_enum::value || std::is_class::value>::type> + : public BasicSleeve { + protected: + //TODO simply use T + using SharedPtr = std::shared_ptr::type>; + + using AllocFunc = std::function; + using FreeFunc = std::function; + + const std::function _deleter_bypass = [](void*) {}; + + public: + using ResolverFunc = std::function; + + + // ----------------------------------------------------------------------------------------- + // Creation + + + // Hosting Sleeve Constructor - Create a hosting sleeve + // unitialized + explicit Sleeve() : + _target_resolver{ [this]() { return &_hosted_obj; } }, + _hosted_obj{ _effective_alloc() }, + _sptr{ obj(), _deleter_bypass } + { + pEpLogClass("hosting sleeve"); + } + + // Hosting Sleeve Constructor - Create a hosting sleeve + // initialized + // explicit Sleeve(const T& val) : Sleeve() + // { + // pEpLogClass("hosting sleeve (initialized)"); + // this->operator=(val); + // } + + // create a static sleeve + // using static binding to object pointer + // explicit Sleeve(T** target) : + // _target_resolver{ [target]() { return target; } }, + // _hosted_obj{ nullptr }, + // _sptr{ obj(), _deleter_bypass } + // { + // pEpLogClass("static sleeve"); + // } + + // Binding Sleeve Constructor - create a dynamic sleeve + // using dynamic binding (to a per-access determined object pointer using the custom target resolver) + explicit Sleeve(ResolverFunc target_resolver) : + _target_resolver{ std::move(target_resolver) }, + _hosted_obj{ nullptr }, + _sptr{ obj(), _deleter_bypass } + { + pEpLogClass("binding sleeve"); + } + + // Member Binding Sleeve Constructor - create a dynamic sleeve + // using dynamic binding (to a per-access determined object pointer using the custom resolver) + explicit Sleeve(const std::string& name, BasicSleeve& parent, ResolverFunc target_resolver) : + BasicSleeve(name, parent), + _target_resolver{ std::move(target_resolver) }, + _hosted_obj{ nullptr }, + _sptr{ obj(), _deleter_bypass } + { + pEpLogClass("member binding sleeve"); + } + + // const AllocFunc _effective_alloc{ std::bind(&Sleeve::_default_alloc, this) }; + // const FreeFunc _effective_free{ + // std::bind(&Sleeve::_default_free, this, std::placeholders::_1) + // }; + // const ResolverFunc _target_resolver{ nullptr }; + // + // bool _is_owning{ true }; + // T* _hosted_obj{ nullptr }; + // SharedPtr _sptr{ nullptr }; + + // copy + // copy constructor will always create a hosting sleeve +// explicit Sleeve(const Sleeve& rhs) : +// BasicSleeve(rhs), +// _effective_alloc{ std::bind(&Sleeve::_default_alloc, this) }, +// _effective_free{ +// std::bind(&Sleeve::_default_free, this, std::placeholders::_1) +// }, _target_resolver{ [this]() { return &_hosted_obj; } }, +// _hosted_obj{ _effective_alloc() }, +// _sptr{ obj(), _deleter_bypass } +// { +// pEpLogClass("copy ctor"); +// } + + + // ----------------------------------------------------------------------------------------- + // Assignment + + Sleeve& operator=(const Sleeve& rhs) + { + pEpLogClass("called"); + if constexpr (std::is_class::value) { + BasicSleeve::operator=(rhs); + } + + // replace own object + leave_ownership(); + _sptr = rhs._sptr; + *target() = _sptr.get(); + return *this; + } + + // DYN TYPED ASSIGN ;p) + // replace the object (managed) + // take copy of the shared_ptr + Sleeve& operator=(const BasicSleeve& rhs) override + { + pEpLogClass("called"); + try { + // try to cast rhs to own type + const Sleeve& rhs_c = dynamic_cast&>(rhs); + operator=(rhs_c); + } catch (...) { + throw std::runtime_error("INVALID TYPES"); + } + return *this; + } + + + // replace the value + // Sleeve& operator=(T* rhs) + // { + // pEpLogClass("called"); + // leave_ownership(); + // + // _sptr = SharedPtr{ rhs, _deleter_bypass }; + // *target() = rhs; + // + // if constexpr (std::is_class::value) { + // //rebind + // } + // + // return *this; + // } + + // replace the value + // deep-copy + // if empty, create a new instance on the target + Sleeve& operator=(const T& rhs) + { + pEpLogClass("called"); + // only members leave ownership + if constexpr (std::is_class::value) { + BasicSleeve::leave_ownership_recurse(); + } + + // if empty, create a new obj + if (is_empty()) { + pEpLogClass("creating new instance"); + *target() = _effective_alloc(); + _sptr = SharedPtr{ obj(), _deleter_bypass }; + } + + // copy in the value + *obj() = rhs; + + // only members rebind + if constexpr (std::is_class::value) { + BasicSleeve::rebind(); + } + + return *this; + } + + void rebind() override + { + if constexpr (std::is_class::value) { + BasicSleeve::rebind(); + } + _sptr = SharedPtr{ obj(), _deleter_bypass }; + } + + // ----------------------------------------------------------------------------------------- + // Comparison + + // value comparison + // must be specialized for struct types + bool cmp(const BasicSleeve& rhs) const override + { + // pEpLogClass("called"); + if constexpr (!std::is_class::value) { + // try convert it to own type Sleeve + try { + const Sleeve& rhs_c = dynamic_cast&>(rhs); + + // both have an object, lets compare values + if (!is_empty() && !rhs_c.is_empty()) { + return *_this()->obj() == *const_cast&>(rhs_c).obj(); + } + // both have no object, they are equal + if (is_empty() && rhs_c.is_empty()) { + return true; + } + // otherwise they are not the same + return false; + } catch (...) { + return false; + } + } else { + return BasicSleeve::cmp(const_cast(rhs)); + } + } + + // TODO: we dont offer comparison to raw T /T* + // Solution: create binding sleeve of T and compare Sleeves + // OR: create hosting sleeve with copy of T and compare sleeves + + // bool operator==(const T* rhs) const + // { + // if constexpr (!std::is_class::value) { + // if (rhs != nullptr && !is_empty()) { + // return *_this()->obj() == *rhs; + // } + // if (rhs == nullptr && is_empty()) { + // return true; + // } + // return false; + // } else { + // throw std::runtime_error("cant compare raw struct types by value"); + // } + // } + + // bool operator==(const T& rhs) const + // { + // pEpLogClass("called"); + // if constexpr (!std::is_class::value) { + // return *_this()->obj() == rhs; + // } else { + // throw std::runtime_error( + // "cant compare raw struct types by value, must be implemented in structure aware specialization"); + // } + // } + // + // bool operator!=(const Sleeve& rhs) const + // { + // pEpLogClass("called"); + // return !operator==(rhs); + // } + + bool operator!=(const T* rhs) const + { + pEpLogClass("called"); + if constexpr (!std::is_class::value) { + !operator==(rhs); + } else { + throw std::runtime_error( + "cant compare raw struct types by value, must be implemented in structure aware specialization"); + } + } + + bool operator!=(const T& rhs) const + { + pEpLogClass("called"); + if constexpr (!std::is_class::value) { + !operator==(rhs); + } else { + throw std::runtime_error( + "cant compare raw struct types by value, must be implemented in structure aware specialization"); + } + } + + // identity comparison + bool is(const Sleeve& rhs) const + { + pEpLogClass("called"); + return id() == rhs.id(); + } + + bool is(const T* rhs) const + { + return _this()->obj() == rhs; + } + + bool is(const T& rhs) const + { + bool ret = _this()->obj() == &rhs; + //TODO: is this right? + assert(ret == false); + return ret; + } + + + // ----------------------------------------------------------------------------------------- + // Accessors + + // // Access value + operator T() const + { + pEpLogClass("called"); + return value(); + } + // + // Access value + T value() const + { + return *_this()->obj(); + } + + // Access the object data r/w + operator T*() + { + pEpLogClass("called"); + return obj(); + } + + // Access the object data r/w + T* obj() + { + return *target(); + } + + // Access the object pointer r/w + T** target() + { + T** ret = _target_resolver(); + return ret; + } + + // ----------------------------------------------------------------------------------------- + // Destruction + + // This is the only way to decide at 'destruction-time' if the last shared_ptr has to be freed or not + ~Sleeve() override + { + pEpLogClass("called"); + leave_ownership(); + } + + void leave_ownership_recurse() override + { + pEpLogClass("called"); + if constexpr (std::is_class::value) { + BasicSleeve::leave_ownership_recurse(); + } + leave_ownership(); + } + + void leave_ownership() + { + pEpLogClass("called"); + if (_sptr.unique()) { + if (_is_owning) { + _effective_free(obj()); + } + } + *target() = nullptr; + _sptr = nullptr; + } + + // Misc + // ---- + // object as id + uintptr_t id() const + { + return reinterpret_cast(_this()->obj()); + } + + void set_owner(bool is_owner) + { + _is_owning = is_owner; + } + + bool is_bound() const + { + return _this()->target() != nullptr; + } + + bool is_empty() const + { + return _this()->obj() == nullptr; + } + + bool is_hosting() const + { + return &_hosted_obj == _this()->target(); + } + + std::string to_string(int indent = 0) const override // override + { + std::stringstream builder; + builder << std::endl; + builder << std::string(indent, '\t') << "{" << std::endl; + indent++; + builder << std::string(indent, '\t') << "this : " << CXX::Inspect::all(_this()) + << std::endl; + builder << std::string(indent, '\t') + << "obj : " << CXX::Inspect::all(*_this()->target()) << std::endl; + builder << std::string(indent, '\t') + << "target : " << CXX::Inspect::all(_this()->target()) << std::endl; + builder << std::string(indent, '\t') + << "hosted_obj : " << CXX::Inspect::all(_this()->_hosted_obj) << std::endl; + builder << std::string(indent, '\t') + << "sptr obj : " << CXX::Inspect::all(_sptr.get()) << std::endl; + builder << std::string(indent, '\t') + << "sptr count : " << CXX::Inspect::all(_sptr.use_count()) << std::endl; + if constexpr (!std::is_class::value) { + builder << std::string(indent, '\t') << "value : " + << (!is_empty() ? CXX::Inspect::all(*_this()->obj()) : ""); + } else { + builder << std::string(indent, '\t') << "value : "; + indent++; + builder << BasicSleeve::to_string(indent); + indent--; + } + indent--; + builder << std::endl; + builder << std::string(indent, '\t') << "}" << std::endl; + return builder.str(); + } + + std::string repr(int indent = 0) const override + { + std::stringstream builder; + builder << std::endl; + builder << std::string(indent, '\t'); + // builder << //PARENT THIS + builder << CXX::Inspect::val(_this()) << " / "; + builder << CXX::Inspect::val(_this()->target()) << " / "; + builder << CXX::Inspect::val(_this()->obj()) << " / "; + builder << _sptr.use_count() << " / "; + builder << CXX::Inspect::type(_hosted_obj) << " - \""; + if constexpr (!std::is_class::value) { + builder << (!is_empty() ? CXX::Inspect::val(*_this()->obj()) : "") << "\""; + } else { + builder << " {" << std::endl; + indent++; + builder << BasicSleeve::repr(indent); + indent--; + builder << std::string(indent, '\t') << "}"; + } + return builder.str(); + } + // static bool log_enabled; + Adapter::pEpLog::pEpLogger logger{ "Sleeve<" + std::string(typeid(T*).name()) + ">", true }; + + protected: + Adapter::pEpLog::pEpLogger& m4gic_logger_n4me = logger; + + const AllocFunc _effective_alloc{ std::bind(&Sleeve::_default_alloc, this) }; + const FreeFunc _effective_free{ + std::bind(&Sleeve::_default_free, this, std::placeholders::_1) + }; + const ResolverFunc _target_resolver{ nullptr }; + + bool _is_owning{ true }; + T* _hosted_obj{ nullptr }; + SharedPtr _sptr{ nullptr }; + + + // defaults for customizable functions + // ----------------------------------- + T* _default_alloc() + { + T* ret = (T*)::calloc(1, sizeof(T)); + pEpLogClass(CXX::Inspect::all(ret)); + return ret; + } + + void _default_free(T* ptr) + { + pEpLogClass(CXX::Inspect::all(ptr)); + ::free(ptr); + } + + + private: + Sleeve* _this() const + { + return const_cast*>(this); + } + }; + + bool operator==(const BasicSleeve& a, const BasicSleeve& b) + { + return a.cmp(b) && b.cmp(a); + } + + bool operator!=(const BasicSleeve& a, const BasicSleeve& b) + { + return !(a == b); + } +}; // namespace POTS + +using POTS::operator==; +using POTS::operator!=; + + +//-------------------------------------------------------------------------------------------------- +// Example Datatypes libC99 +//-------------------------------------------------------------------------------------------------- +// Struct type specialization +// operator << +// operator == +// operator = + +namespace LIBC99 { + using POTS::Sleeve; + + class C99_C : public Sleeve<::C99_C*> { + public: + // HS + explicit C99_C() : Sleeve<::C99_C*>() {} + // explicit C99_C(const ::C99_C& val) : Sleeve<::C99_C*>(val) {} + explicit C99_C(ResolverFunc target_resolver) : Sleeve<::C99_C*>(target_resolver) {} + + // MDS + // explicit C99_C(const std::string& name, Sleeve& parent, ResolverFunc target_resolver) : + // Sleeve<::C99_C*>(name, parent, std::move(target_resolver)) + // { + // } + + // copy + // explicit C99_C(const C99_C& rhs) : Sleeve<::C99_C*>(rhs) {} + // explicit Sleeve(const std::string& name, Sleeve& parent, ResolverFunc target_resolver) : + Sleeve pi{ "pi", *this, [this]() { return &this->obj()->pi; } }; + // Sleeve pi{ "pi", *this }; + + C99_C& operator=(const ::C99_C& rhs) + { + Sleeve<::C99_C*>::operator=(rhs); + return *this; + } + + // bool operator==(const ::C99_C& rhs) const + // { + // pEpLogClass("called"); + // C99_C tmp{ rhs }; + // // return *_this()->obj() == rhs; + // } + }; +} // namespace LIBC99 + + +#endif diff --git a/src/libc99.hh b/src/libc99.hh new file mode 100644 index 0000000..7e1f788 --- /dev/null +++ b/src/libc99.hh @@ -0,0 +1,180 @@ +#ifndef LIBPEPDATAYPES_LIBC99_HH +#define LIBPEPDATAYPES_LIBC99_HH + +#include "../examples/libc99/libc99.h" +#include +#include +#include + + +namespace C99 { + namespace Utils { + // C99_Status + std::string to_string(const C99_Status& data) + { + switch (data) { + case OK: + return "OK"; + case ERROR: + return "ERROR"; + } + } + + std::string to_string(const C99_Status*& data) + { + if (data) { + return to_string(*data); + } else { + return ""; + } + } + + // C99_Enum + std::string to_string(const C99_Enum& data) + { + switch (data) { + case ONE: + return "ONE"; + case TWO: + return "TWO"; + case THREE: + return "THREE"; + } + } + + std::string to_string(const C99_Enum* data) + { + if (data) { + return to_string(*data); + } else { + return ""; + } + } + + // C99_C + std::string to_string(const ::C99_C& data, int indent = 0) + { + std::stringstream builder; + builder << std::endl; + builder << std::string(indent, '\t') << "{" << std::endl; + indent++; + builder << std::string(indent, '\t') << "i: '" << data.i << "'" << std::endl; + builder << std::string(indent, '\t') << "pi: '" + << (data.pi != nullptr ? std::to_string(*data.pi) : "") << "'" + << std::endl; + indent--; + builder << std::string(indent, '\t') << "}"; + return builder.str(); + } + + std::string to_string(const ::C99_C* data, int indent = 0) + { + if (data) { + return to_string(*data, indent); + } else { + return ""; + } + } + + // C99_B + std::string to_string(const ::C99_B& data, int indent = 0) + { + std::stringstream builder; + builder << std::endl; + builder << std::string(indent, '\t') << "{" << std::endl; + indent++; + builder << std::string(indent, '\t') << "c: '" + to_string(data.c, indent) << "'" + << std::endl; + indent--; + builder << std::string(indent, '\t') << "}"; + return builder.str(); + } + + std::string to_string(const ::C99_B* data, int indent = 0) + { + if (data) { + return to_string(*data, indent); + } else { + return ""; + } + } + + // C99_A + std::string to_string(const ::C99_A& data, int indent = 0) + { + std::stringstream builder; + builder << std::endl; + builder << std::string(indent, '\t') << "{" << std::endl; + indent++; + builder << std::string(indent, '\t') << "b: '" + to_string(data.b, indent) << "'" + << std::endl; + indent--; + builder << std::string(indent, '\t') << "}"; + return builder.str(); + } + + std::string to_string(const ::C99_A* data, int indent = 0) + { + if (data) { + return to_string(*data, indent); + } else { + return ""; + } + } + + + } // namespace Utils +} // namespace C99 + +// ostream inserters +//C99_Status +std::ostream& operator<<(std::ostream& o, const ::C99_Status& a) +{ + return o << C99::Utils::to_string(a); +} +std::ostream& operator<<(std::ostream& o, const ::C99_Status* a) +{ + return o << C99::Utils::to_string(a); +} + +//C99_Enum +std::ostream& operator<<(std::ostream& o, const ::C99_Enum& a) +{ + return o << C99::Utils::to_string(a); +} +std::ostream& operator<<(std::ostream& o, const ::C99_Enum* a) +{ + return o << C99::Utils::to_string(a); +} + +//C99_C +std::ostream& operator<<(std::ostream& o, const ::C99_C& a) +{ + return o << C99::Utils::to_string(a); +} +std::ostream& operator<<(std::ostream& o, const ::C99_C* a) +{ + return o << C99::Utils::to_string(a); +} + +//C99_B +std::ostream& operator<<(std::ostream& o, const ::C99_B& a) +{ + return o << C99::Utils::to_string(a); +} +std::ostream& operator<<(std::ostream& o, const ::C99_B* a) +{ + return o << C99::Utils::to_string(a); +} + +//C99_A +std::ostream& operator<<(std::ostream& o, const ::C99_A& a) +{ + return o << C99::Utils::to_string(a); +} +std::ostream& operator<<(std::ostream& o, const ::C99_A* a) +{ + return o << C99::Utils::to_string(a); +} + +#endif // LIBPEPDATAYPES_LIBC99_HH \ No newline at end of file diff --git a/src/prototype.hh b/src/prototype.hh index 8479e20..ca42f92 100644 --- a/src/prototype.hh +++ b/src/prototype.hh @@ -133,31 +133,15 @@ namespace pEp { // Thats the only difference. class String { public: - String() - { - pEpLogClass("called"); - } - // Best to use this constructor, as the object is in a valid state when the wrapped // c_str (char*) is known - String(bool is_owner, char** c_str_pp) + String(char** c_str_pp) { - bind(is_owner, c_str_pp); - } - - String(bool is_owner, char** c_str_pp, const std::string& init_val) - { - bind(is_owner, c_str_pp, init_val); - } - - void bind(bool is_owner, char** c_str_pp, const std::string& init_val) - { - bind(is_owner, c_str_pp); - this->operator=(init_val); + bind(c_str_pp); } // bind and set ownership - void bind(bool is_owner, char** c_str_pp) + void bind(char** c_str_pp) { if (c_str_pp == nullptr) { throw Exception{ EXSTR("cant bind on a nullptr") }; @@ -168,13 +152,12 @@ namespace pEp { } // init - _c_str_pp = c_str_pp; + _target = c_str_pp; _is_bound = true; pEpLogClass(to_string()); - set_owner(is_owner); //if the c_str_p is nullptr then init with "" - if (*_c_str_pp == nullptr) { + if (*_target == nullptr) { this->operator=(""); } } @@ -182,7 +165,7 @@ namespace pEp { ~String() { if (_is_owner) { - _free(*_c_str_pp); + _free(*_target); } } @@ -190,15 +173,15 @@ namespace pEp { String& operator=(const std::string& str) { pEpLogClass("Before: " + to_string() + " - new val: '" + pEp::Utils::clip(str, 30) + "'"); - if (_c_str_pp == nullptr) { + if (_target == nullptr) { throw Exception{ EXSTR("invalid state") }; } // DEALLOCATION - _free(*_c_str_pp); + _free(*_target); // ALLOCATION - *_c_str_pp = _copy(str); + *_target = _copy(str); pEpLogClass("After: " + to_string()); return *this; } @@ -206,29 +189,53 @@ namespace pEp { // return a copy of whatever c_str currently is (maybe created by us, maybe changed meanwhile) operator std::string() const { - if (_c_str_pp == nullptr) { + if (_target == nullptr) { throw Exception{ EXSTR("invalid state") }; } - if (*_c_str_pp != nullptr) { - return { *_c_str_pp }; + if (*_target != nullptr) { + return { *_target }; } return {}; } - char** data() + const char* c_data() const { - return _c_str_pp; + if (_target == nullptr) { + throw Exception{ EXSTR("invalid state") }; + } + return *_target; } - const char* c_data() const + char* data() { - if (_c_str_pp == nullptr) { + if (_target == nullptr) { throw Exception{ EXSTR("invalid state") }; } - return *_c_str_pp; + return *_target; + } + + char** obj() + { + return _target; + } + + operator const char*() const + { + return c_data(); } + operator char*() + { + return data(); + } + + operator char**() + { + return obj(); + } + + bool operator==(const pEp::String& pstr) const { return *(pstr.c_data()) == (*c_data()); @@ -242,12 +249,12 @@ namespace pEp { std::string to_string() const { - if (_c_str_pp == nullptr) { + if (_target == nullptr) { throw Exception{ EXSTR("invalid state") }; } - std::string ret{ "[" + CXX::Inspect::all(_c_str_pp) + " / " + - CXX::Inspect::all(*_c_str_pp) + "]" }; + std::string ret{ "[" + CXX::Inspect::all(_target) + " / " + + CXX::Inspect::all(*_target) + "]" }; return ret; } @@ -278,9 +285,9 @@ namespace pEp { ::pEp_free(ptr_type); } + bool _is_owner{ true }; bool _is_bound{ false }; - bool _is_owner{ false }; - char** _c_str_pp{ nullptr }; + char** _target{ nullptr }; Adapter::pEpLog::pEpLogger logger{ "pEp::String", log_enabled }; Adapter::pEpLog::pEpLogger& m4gic_logger_n4me = logger; @@ -304,165 +311,366 @@ namespace pEp { // You can create an instance of a c-struct or you can wrap an already existing c-struct // In both cases, you can define if the wrapper owns the instance, or not. // If if owns it, it will free() it when the wrapper dies, otherwise it doesnt. + class BasicSleeve { + public: + virtual ~BasicSleeve() = default; + }; + + template - class PODStruct { + class Sleeve : public BasicSleeve { public: - PODStruct() = delete; + // Creation + // -------- + Sleeve() = delete; + + // Create a sleeve and an instance + explicit Sleeve(const T& val) : _effective_resolver{ [this]() { return &_obj; } }, _obj(val) + { + pEpLogClass("hosting sleeve"); + } + + // create a sleeve on an instance + // using static binding to an existing object + explicit Sleeve(T* target) : _effective_resolver{ [target]() { return target; } } + { + pEpLogClass("static sleeve"); + } + + // create a sleeve on an instance + // using dynamic binding (to a per-access determined object using the custom resolver) + explicit Sleeve(std::function resolver) : _effective_resolver{ std::move(resolver) } + { + pEpLogClass("dynamic sleeve"); + } + + // create a new instance and copy the value + explicit Sleeve(const Sleeve& rhs) : + _effective_resolver{ [this]() { return &_obj; } }, + _obj{ rhs._obj } + { + pEpLogClass("copy ctor"); + } - // Creates a new instance or binds an existing one - PODStruct(bool is_owner, T** c_struct_pp = nullptr) + // Assignment + // ---------- + // takes a copy + Sleeve& operator=(const Sleeve& rhs) { pEpLogClass("called"); - _init(is_owner, c_struct_pp); + *obj_ptr() = rhs._obj; + return *this; } - // Creates a new instance or binds an existing one - // but takes custom alloc/free functions - PODStruct( - bool is_owner, - std::function alloc, - std::function free, - T** c_struct_pp = nullptr) : - _effective_alloc(std::move(alloc)), - _effective_free(std::move(free)) + // assign the binding + Sleeve& operator=(const T& rhs) { - _init(is_owner, c_struct_pp); + pEpLogClass("called"); + *obj_ptr() = rhs; + return *this; } - void bind(bool is_owner, T** c_struct_pp) + // Comparison + // ---------- + // value comparison + bool operator==(const Sleeve& rhs) const { - if (c_struct_pp == nullptr) { - throw Exception{ EXSTR("cant bind on a nullptr") }; - } + pEpLogClass("called"); + return *const_cast*>(this)->obj_ptr() == *const_cast&>(rhs).obj_ptr(); + } - if (_is_bound) { - throw Exception{ EXSTR("can only bind once") }; - } + bool operator==(const T& rhs) const + { + pEpLogClass("called"); + return *const_cast*>(this)->obj_ptr() == rhs; + } - // init - _c_struct_pp = c_struct_pp; - _c_struct_p = *_c_struct_pp; - _is_bound = true; - pEpLogClass(to_string()); - set_owner(is_owner); + // bool operator!=(const Sleeve& rhs) const + // { + // pEpLogClass("called"); + // return !operator==(rhs); + // } + + bool operator!=(const T& rhs) const + { + pEpLogClass("called"); + return !operator==(rhs); } - ~PODStruct() + // object comparison + bool is(const Sleeve& rhs) const { - if (_is_owner) { - _effective_free(_c_struct_p); - } + pEpLogClass("called"); + return id() == rhs.id(); } - T** data() + bool is(const T& rhs) const { - return _c_struct_pp; + pEpLogClass("called"); + return const_cast*>(this)->obj_ptr() == &rhs; } - const T* c_data() const + // Accessors + // --------- + // Access the object data r/w + T data() { - if (_c_struct_pp == nullptr) { - throw Exception{ EXSTR("invalid state") }; - } - return *_c_struct_pp; + pEpLogClass("called"); + return *obj_ptr(); } - operator T*() + // Access the object data r/w + operator T() { - return _c_struct_p; + pEpLogClass("called"); + return *obj_ptr(); } - std::string to_string() const + // Access the object pointer r/w + T* obj_ptr() { - std::string ret{ "[" + CXX::Inspect::all(_c_struct_p) + "]" }; + // pEpLogClass("called"); + return _effective_resolver(); + } + + // Misc + // ---- + // obj_pointer as id + long id() const + { + // pEpLogClass("called"); + return reinterpret_cast(const_cast*>(this)->obj_ptr()); + } + + std::string to_string() + { + std::string ret{ "[" + CXX::Inspect::all(*obj_ptr()) + "]" }; return ret; } - void set_owner(bool is_owner) + // static bool log_enabled; + Adapter::pEpLog::pEpLogger logger{ typeid(T).name(), true }; + + protected: + Adapter::pEpLog::pEpLogger& m4gic_logger_n4me = logger; + + std::function _effective_resolver; + T _obj; + }; + + + template + class Sleeve : public BasicSleeve { + public: + // Creates a new sleeve managing and holding a newly created object + explicit Sleeve() : + _target{ &_obj }, + _obj{ _alloc_wrapper() }, + _sptr{ *_target, std::bind(&Sleeve::_free_wrapper, this) } { - pEpLogClass(std::to_string(is_owner)); - _is_owner = is_owner; + pEpLogClass("new"); } - bool is_owner() const + //Create a sleeve managing a C - sleeve explicit + Sleeve(T** target) : + _target(target), + _obj{ nullptr }, + _sptr{ *_target, std::bind(&Sleeve::_free_wrapper, this) } { - return _is_owner; + pEpLogClass("called"); } - static bool log_enabled; + explicit Sleeve(std::function resolver) : _effective_resolver{ std::move(resolver) } + { + pEpLogClass("wrap"); + } + + const T* c_data() const + { + pEpLogClass("called"); + return *obj_ptr(); + } + + // Access the object data r/w + T* data() + { + pEpLogClass("called"); + return *obj_ptr(); + } - Adapter::pEpLog::pEpLogger logger{ typeid(T).name(), log_enabled }; + // Access the object data read-only + operator const T*() + { + return c_data(); + } + + // Access the object data r/w + operator T*() + { + return data(); + } + + // T* abandon() + // { + // T* ret = *_target; + // _is_owning = false; + // _obj = nullptr; + // _sptr.reset(*_target, std::bind(&Sleeve::_free_wrapper, this)); + // return ret; + // } + + // Access the object pointer r/w + T** obj_ptr() + { + return _effective_resolver(); + } + + std::string to_string() + { + std::string ret{ "[" + CXX::Inspect::all(*obj_ptr()) + "]" }; + return ret; + } + + // static bool log_enabled; + Adapter::pEpLog::pEpLogger logger{ typeid(T*).name(), true }; protected: Adapter::pEpLog::pEpLogger& m4gic_logger_n4me = logger; - private: - const std::function _effective_alloc{ std::bind(&PODStruct::_alloc, this) }; + + const std::function _effective_alloc{ std::bind(&Sleeve::_default_alloc, this) }; const std::function _effective_free{ - std::bind(&PODStruct::_free, this, std::placeholders::_1) + std::bind(&Sleeve::_default_free, this, std::placeholders::_1) }; - bool _is_bound{ false }; - bool _is_owner{ false }; - T** _c_struct_pp{ nullptr }; - T* _c_struct_p{ nullptr }; - - void _init(bool is_owner, T** c_struct_pp) - { - // if no pp is given, alloc new, - // otherwise bind to it - if (!c_struct_pp) { - _c_struct_p = _effective_alloc(); - bind(is_owner, &_c_struct_p); - } else { - bind(is_owner, c_struct_pp); - } - } + // We have as many indirections as we have levels in the hosting data structure + // When the obj is member of a struct, the obj we actually wrap can change without + // further noctice from the C99 side. + // Therefore for EVERY access to to the obj we need to ask the hosting struct for the + // address of our obj. If the hosting struct is a member of a struct itself, it will have to + // to ask its hosting struct and so forth... recursively. + // For every access, you have as many indirections as levels deep your object is in the struct. + // Using this technique, we can also accommodate for wrapping an object which can + // be REPLACED without further notice, handing out a T**. + const std::function _effective_resolver{ std::bind(&Sleeve::_default_resolver, this) }; + + bool _is_owning{ true }; + // we ALWAYS have a _target that points to either an internal or external obj + T** _target{ nullptr }; + // we MAYBE also have an obj + T* _obj{ nullptr }; + // we NEVER operate on the obj, but only on the _target + + // All the shared ptr does, is freeing *_target when last copy dies + // the shared pointer CAN NOT have the correct obj pointer at all times, + // because the object can change without notice from the C99 side. + // Thats why this class basically is an (infinitely) indirect shared pointer. + std::shared_ptr::type> _sptr; + + + // defaults for customizable functions + // ----------------------------------- - // default alloc/free - T* _alloc() + T* _default_alloc() { T* ret = (T*)calloc(1, sizeof(T)); pEpLogClass(CXX::Inspect::all(ret)); return ret; } - void _free(T* ptr) + void _default_free(T* ptr) { pEpLogClass(CXX::Inspect::all(ptr)); free(ptr); } - class Exception : public std::runtime_error { - public: - explicit Exception(const std::string& msg) : std::runtime_error(msg) {} - }; + T** _default_resolver() + { + pEpLogClass(CXX::Inspect::all(_target)); + return _target; + } + + // wrappers + // -------- + + T* _alloc_wrapper() + { + pEpLogClass("called"); + return _effective_alloc(); + } + + void _free_wrapper() + { + pEpLogClass("called"); + if (_is_owning) { + _effective_free(*obj_ptr()); + } + } }; - //--------------------------------------------------------------------------------------------- - // TOOD: - // ctor not exception safe - // You can create an instance of a c-struct or you can wrap an already existing c-struct - // In both cases, you can define if the wrapper owns the instance, or not. - // If if owns it, it will free() it when the wrapper dies, otherwise it doesnt. + + // template + // class Sleeve : public BasicSleeve { // + // }; + + + // TOOD:// // alloc/free: // the alloc() and free() functions HAVE to use calloc/malloc NOT 'new', because we are interfacing // c99 code that could possibly get ownership of the struct and can only free memory that has // been allocated using malloc/calloc. (malloc/free - new/delete are NOT compatible) - class TestStruct1 : public PODStruct<::Test_struct1> { + //--------------------------------------------------------------------------------------------- + class C : public Sleeve<::C99_C*> { public: - TestStruct1(bool is_owner, ::Test_struct1** test_struct1_p = nullptr) : - PODStruct<::Test_struct1>(is_owner, test_struct1_p) - { - } - - // fields of the struct as 'properties' ;) - pEp::POD c_int{ &(*data())->c_int }; - pEp::POD<::Test_enum> c_enum{ &(*data())->c_enum }; - pEp::String c_str{ is_owner(), &(*data())->c_str }; + // create a new instance + explicit C() : Sleeve<::C99_C*>() {} + // create an instance wrapper + // explicit C(::C* obj) : Sleeve<::C>(obj) {} + // create an instance-ptr wrapper + // explicit C(::C** obj_ptr) : Sleeve<::C>(obj_ptr) {} + explicit C(std::function<::C99_C**()> resolver) : Sleeve<::C99_C*>(resolver) {} + + pEp::Sleeve i{ [this]() { return &dynamic_cast(this)->data()->i; } }; + // pEp::Sleeve pi{ this, [](BasicSleeve* parent) { + // return &dynamic_cast(parent)->data()->pi; + // } }; }; - template<> - bool PODStruct<::Test_struct1>::log_enabled{ false }; + + // // ------------------------------------------------------------------------------------------ + // class B : public Sleeve<::C99_B*> { + // public: + // // create a new instance + // explicit B() : Sleeve<::C99_B*>() {} + // // create an instance wrapper + // // explicit B(::B* obj) : Sleeve<::B>(obj) {} + // // create an instance-ptr wrapper + // // explicit B(::B** obj_ptr) : Sleeve<::B>(obj_ptr) {} + // explicit B(BasicSleeve* parent, std::function<::C99_B**(BasicSleeve*)> resolver) : + // Sleeve<::C99_B*>(parent, resolver) + // { + // } + // + // pEp::C c{ this, [](BasicSleeve* parent) { return &dynamic_cast(parent)->data()->c; } }; + // }; + // + // + // // ------------------------------------------------------------------------------------------ + // class A : public Sleeve<::C99_A*> { + // public: + // // create a new instance + // explicit A() : Sleeve<::C99_A*>() {} + // // create an instance wrapper + // // explicit A(::A* obj) : Sleeve<::A>(obj) {} + // // create an instance-ptr wrapper + // // explicit A(::A** obj_ptr) : Sleeve<::A>(obj_ptr) {} + // explicit A(BasicSleeve* parent, std::function<::C99_A**(BasicSleeve*)> resolver) : + // Sleeve<::C99_A*>(parent, resolver) + // { + // } + // + // pEp::B b{ this, [](BasicSleeve* parent) { return &dynamic_cast(parent)->data()->b; } }; + // }; + } // namespace pEp diff --git a/test/test_cxx.cc b/test/test_cxx.cc new file mode 100644 index 0000000..55b4d75 --- /dev/null +++ b/test/test_cxx.cc @@ -0,0 +1,73 @@ +#include +#include +#include "pEp/inspect.hh" +#include "../examples/libc99/libc99.h" +#include "../src/libc99.hh" +//#include "../src/POTS.hh" + +using namespace pEp; + +struct Struct { + int a; + int b; +}; + +template +class Field { +public: + Field() = default; + Field(T* target) { + bind(target); + } + + void bind(T* target) { + _target = target; + } + operator T() + { + return *_target; + } + + Field& operator=(T val) + { + *_target = val; + return *this; + } + +private: + T* _target{ nullptr}; +}; + +class A : public Field { +public: + A() : _obj{ std::make_shared() } { + a.bind(&_obj->a); + b.bind(&_obj->b); + } + + + std::shared_ptr _obj; + Field a{}; + Field b{}; +}; + +int main() +{ + Adapter::pEpLog::set_enabled(true); + pEpLog("start"); + A b{}; + pEpLog(b.a); + // pEpLog(b.b); + + { + A a{}; + a.a = 23; + a.b = 42; + pEpLog(a.a); + // pEpLog(a.b); + + b = a; + } + pEpLog(b.a); + // pEpLog(b.b); +} diff --git a/test/test_identity.cc b/test/test_identity.cc index 713e67b..76a7b4c 100644 --- a/test/test_identity.cc +++ b/test/test_identity.cc @@ -8,20 +8,16 @@ //--------------------------------------------------------------------------------------------- namespace pEp { - class Identity : public PODStruct<::pEp_identity> { + class Identity : public Sleeve<::pEp_identity> { public: - Identity(bool is_owner, ::pEp_identity** ident_p = nullptr) : - PODStruct<::pEp_identity>(is_owner, ident_p) - { - } + Identity(::pEp_identity** ident_p = nullptr) : PODStruct<::pEp_identity>(ident_p) {} Identity( - bool is_owner, const std::string& address = "", const std::string& username = "", const std::string& user_id = "", const std::string& fpr = "") : - PODStruct<::pEp_identity>(is_owner, nullptr) + Sleeve<::pEp_identity>(nullptr) { pEpLogClass("called"); this->address = address; @@ -30,21 +26,20 @@ namespace pEp { this->fpr = fpr; } - pEp::String address{ is_owner(), &(*data())->address }; - pEp::String fpr{ is_owner(), &(*data())->fpr }; - pEp::String user_id{ is_owner(), &(*data())->user_id }; - pEp::String username{ is_owner(), &(*data())->username }; - pEp::POD<::PEP_comm_type> comm_type{ &(*data())->comm_type }; + pEp::String fpr{ &data()->fpr }; + pEp::String user_id{ &data()->user_id }; + pEp::String username{ &data()->username }; + pEp::POD<::PEP_comm_type> comm_type{ &data()->comm_type }; //char[3] lang - pEp::POD me{ &(*data())->me }; - pEp::POD major_ver{ &(*data())->major_ver }; - pEp::POD minor_ver{ &(*data())->minor_ver }; - pEp::POD<::PEP_enc_format> enc_format{ &(*data())->enc_format }; - pEp::POD<::identity_flags_t> flags{ &(*data())->flags }; + pEp::POD me{ &data()->me }; + pEp::POD major_ver{ &data()->major_ver }; + pEp::POD minor_ver{ &data()->minor_ver }; + pEp::POD<::PEP_enc_format> enc_format{ &data()->enc_format }; + pEp::POD<::identity_flags_t> flags{ &data()->flags }; }; template<> - bool PODStruct<::pEp_identity>::log_enabled{ false }; + bool Sleeve<::pEp_identity>::log_enabled{ false }; } // namespace pEp @@ -56,20 +51,21 @@ int main() ::init(&session, nullptr, nullptr, nullptr); // create identity - pEp::Identity id1{ true, "wrong@entry.lol", "wrong", "23", "INVA_FPR" }; + pEp::Identity id1{ "wrong@entry.lol", "wrong", "23", "INVA_FPR" }; - pEpLog(id1); + pEpLog(id1.c_data()); id1.username = "alice"; id1.address = "alice@peptest.org"; + id1. pEpLog(id1.address); pEpLog(id1.username); ::myself(session, id1); - pEpLog(id1); + pEpLog(id1.c_data()); - pEp::Identity id2{ true, "bob" }; + pEp::Identity id2{ "bob" }; ::update_identity(session, id2); - pEpLog(id2); + pEpLog(id2.c_data()); return 0; } \ No newline at end of file diff --git a/test/test_nr1.cc b/test/test_nr1.cc deleted file mode 100644 index 9fb631c..0000000 --- a/test/test_nr1.cc +++ /dev/null @@ -1,247 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -//#include "../examples/libc99/libc99.h" -#include "../src/prototype.hh" - -void test_getters(int* c_int_p, pEp::POD& pint, int expected) -{ - pEpLog("to_string(): " + pint.to_string()); - // compare addresses - pEpLog("addresses == c_int_p"); - assert(pint.c_data() == c_int_p); - assert(pint.data() == c_int_p); - - // compare values - if (c_int_p != nullptr) { - pEpLog("operator int(): " + std::to_string(pint)); - assert(pint == *c_int_p); - assert(pint == expected); - - // will segfault with nullptr, and this is correct - pEpLog("data(): " + std::to_string(*pint.data())); - assert(*pint.data() == *c_int_p); - assert(*pint.data() == expected); - - pEpLog("c_data(): " + std::to_string(*pint.c_data())); - assert(*pint.c_data() == *c_int_p); - assert(*pint.c_data() == expected); - } -} - -void test_assign(int* c_int_p, pEp::POD& pint) -{ - { - pEpLogH2("assign operator"); - int new_val = 23; - pint = new_val; - test_getters(c_int_p, pint, new_val); - } - { - pEpLogH2("raw c_str assign"); - int new_val = 23; - *c_int_p = new_val; - test_getters(c_int_p, pint, new_val); - } -} - - -void test_getters(char** c_str_p, pEp::String& pstr, const std::string& expected) -{ - pEpLog("to_string(): " + pstr.to_string()); - // compare addresses - pEpLog("addresses == c_str_p"); - assert(pstr.c_data() == *c_str_p); - assert(pstr.data() == c_str_p); - - // compare values - if (*c_str_p != nullptr) { - pEpLog("operator std::string(): " + std::string(pstr)); - assert(std::string(pstr) == std::string(*c_str_p)); - assert(std::string(pstr) == expected); - - // will segfault with nullptr, and this is correct - pEpLog("data(): " + std::string(*pstr.data())); - assert(std::string(*pstr.data()) == std::string(*c_str_p)); - assert(std::string(*pstr.data()) == expected); - - pEpLog("c_data(): " + std::string(pstr.c_data())); - assert(std::string(pstr.c_data()) == std::string(*c_str_p)); - assert(std::string(pstr.c_data()) == expected); - } else { - std::string tmp{ pstr }; - pEpLog("operator std::string(): " + tmp); - assert(tmp.empty()); - } -} - -void test_assign(char** c_str_p, pEp::String& pstr) -{ - { - pEpLogH2("assign operator"); - std::string new_val{ "assign operator" }; - pstr = new_val; - test_getters(c_str_p, pstr, new_val); - } - { - pEpLogH2("raw c_str assign"); - std::string new_val{ "raw c_str assign" }; - free(*c_str_p); - *c_str_p = strdup(new_val.c_str()); - test_getters(c_str_p, pstr, new_val); - } -} - - -int main() -{ - // c-types are always POD - // we need to handle pointer types and value types differently - // pointer types need to be memory-managed (heap) - // value types are being copied around (stack) - static_assert(std::is_pod::value && !std::is_pointer::value, "not an integral"); - static_assert( - std::is_pod<::Test_enum>::value && !std::is_pointer<::Test_enum>::value, - "not an integral"); - static_assert( - std::is_pod<::Test_struct1>::value && !std::is_pointer<::Test_struct1>::value, - "not an integral"); - - // pEp::Utils::readKey(); - pEp::Adapter::pEpLog::set_enabled(true); - - // POD - if (1) { - // VALID USAGE - int init_val = 0; - // new pEp::POD on int - { - pEpLogH1("new pEp::POD on int"); - int c_int = init_val; - pEp::POD pint(&c_int); - test_getters(&c_int, pint, init_val); - test_assign(&c_int, pint); - } - // equality operator - { - pEpLogH1("equality operator"); - int c_int1 = init_val; - int c_int2 = init_val; - pEp::POD pint1(&c_int1); - pEp::POD pint2(&c_int2); - assert(pint1 == pint2); - - c_int2 = 23; - assert(pint1 != pint2); - } - } - - // String - // pEp::String::log_enabled = false; - if (1) { - //TODO: Test non-owning mode - - // INVALID USAGE - // char* c_str_u; // uninitialized == INVALID - // undefined behaviour, most likely "pointer being freed was not allocated" - // { - // char* c_str; - // pEp::String pstr(c_str); - // } - // char* c_str_s = "fdsfs"; // statically initialized == INVALID - // { - // char ca[4] = { 'p', 'E', 'p'}; - // char* c_str = ca; - // pEp::String pstr(c_str); - // } - - // VALID USAGE - // new pEp::String on char* pointing to NULL - { - pEpLogH1("new pEp::String on char* pointing to NULL"); - char* c_str = NULL; // nullptr - - pEp::String pstr(true, &c_str); - test_getters(&c_str, pstr, ""); - test_assign(&c_str, pstr); - } - - std::string init_val{ "initialized c string" }; - // new pEp::String on already initalized char* - { - pEpLogH1("new pEp::String on already initalized char*"); - char* c_str = strdup(init_val.c_str()); - - pEp::String pstr(true, &c_str); - test_getters(&c_str, pstr, init_val); - test_assign(&c_str, pstr); - } - - // bind() - { - pEpLogH1("bind()"); - pEp::String pstr{}; - - char* c_str = strdup(init_val.c_str()); - //TODO: PITYASSERT_THROWS - pstr.bind(true, &c_str); - test_getters(&c_str, pstr, init_val); - test_assign(&c_str, pstr); - } - - // equality operator - { - pEpLogH1("equality operator"); - pEp::String pstr1{}; - - char* c_str1 = strdup(init_val.c_str()); - char* c_str2 = strdup(init_val.c_str()); - pstr1.bind(true, &c_str1); - pEp::String pstr2{ true, &c_str2 }; - assert(pstr1 == pstr2); - pstr2 = "huhu"; - assert(pstr1 != pstr2); - } - } - - // Struct - if (1) { - // create an instance - if (1) { - pEp::TestStruct1 pstruct1{ true }; - // pEp::TestStruct1 pstruct1(true, 23, TWO, "pEp"); - pEpLog(pstruct1.c_int); - pEpLog(pstruct1.c_enum); - pEpLog(pstruct1.c_str); - - ::Test_struct1* c_struct1 = pstruct1; - pEpLog(c_struct1->c_int); - pEpLog(c_struct1->c_enum); - pEpLog(c_struct1->c_str); - pEpLog("DONE"); - } - - // wrap already existing instance - { - ::Test_struct1* c_struct1 = ::new_test_struct(0, ONE, "pEp"); - pEpLog(c_struct1->c_int); - pEpLog(c_struct1->c_enum); - pEpLog(c_struct1->c_str); - pEpLog("DONE"); - - pEp::TestStruct1 pstruct1(true, &c_struct1); - pEpLog(pstruct1.c_int); - pEpLog(pstruct1.c_enum); - pEpLog(pstruct1.c_str); - pEpLog("DONE"); - } - } -} diff --git a/test/test_prototype.cc b/test/test_prototype.cc new file mode 100644 index 0000000..017f5a8 --- /dev/null +++ b/test/test_prototype.cc @@ -0,0 +1,414 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include "../examples/libc99/libc99.h" +#include "../src/prototype.hh" + +void test_getters(int* c_int_p, pEp::POD& pint, int expected) +{ +// pEpLog("to_string(): " + pint.to_string()); + // compare addresses + pEpLog("addresses == c_int_p"); + assert(pint.c_data() == c_int_p); + assert(pint.data() == c_int_p); + + // compare values + if (c_int_p != nullptr) { + pEpLog("operator int(): " + std::to_string(pint)); + assert(pint == *c_int_p); + assert(pint == expected); + + // will segfault with nullptr, and this is correct + pEpLog("data(): " + std::to_string(*pint.data())); + assert(*pint.data() == *c_int_p); + assert(*pint.data() == expected); + + pEpLog("c_data(): " + std::to_string(*pint.c_data())); + assert(*pint.c_data() == *c_int_p); + assert(*pint.c_data() == expected); + } +} + +void test_assign(int* c_int_p, pEp::POD& pint) +{ + { + pEpLogH2("assign operator"); + int new_val = 23; + pint = new_val; + test_getters(c_int_p, pint, new_val); + } + { + pEpLogH2("raw c_str assign"); + int new_val = 23; + *c_int_p = new_val; + test_getters(c_int_p, pint, new_val); + } +} + + +void test_getters(char** c_str_p, pEp::String& pstr, const std::string& expected) +{ + pEpLog("to_string(): " + pstr.to_string()); + // compare addresses + pEpLog("addresses == c_str_p"); + assert(pstr.c_data() == *c_str_p); + assert(pstr.obj() == c_str_p); + + // compare values + if (*c_str_p != nullptr) { + pEpLog("operator std::string(): " + std::string(pstr)); + assert(std::string(pstr) == std::string(*c_str_p)); + assert(std::string(pstr) == expected); + + // will segfault with nullptr, and this is correct + pEpLog("data(): " + std::string(*pstr.obj())); + assert(std::string(*pstr.obj()) == std::string(*c_str_p)); + assert(std::string(*pstr.obj()) == expected); + + pEpLog("c_data(): " + std::string(pstr.c_data())); + assert(std::string(pstr.c_data()) == std::string(*c_str_p)); + assert(std::string(pstr.c_data()) == expected); + } else { + std::string tmp{ pstr }; + pEpLog("operator std::string(): " + tmp); + assert(tmp.empty()); + } +} + +void test_assign(char** c_str_p, pEp::String& pstr) +{ + { + pEpLogH2("assign operator"); + std::string new_val{ "assign operator" }; + pstr = new_val; + test_getters(c_str_p, pstr, new_val); + } + { + pEpLogH2("raw c_str assign"); + std::string new_val{ "raw c_str assign" }; + free(*c_str_p); + *c_str_p = strdup(new_val.c_str()); + test_getters(c_str_p, pstr, new_val); + } +} + + +//void test_func1(char** out) { +// *out = strdup("fds"); +//} + + +int main() +{ + // c-types are always POD + // we need to handle pointer types and value types differently + // pointer types need to be memory-managed (heap) + // value types are being copied around (stack) + static_assert(std::is_pod::value && !std::is_pointer::value, "not an integral"); + static_assert( + std::is_pod<::C99_Enum>::value && !std::is_pointer<::C99_Enum>::value, + "not an integral"); + static_assert(std::is_pod<::C99_B>::value && !std::is_pointer<::C99_B>::value, "not an integral"); + + // pEp::Utils::readKey(); + pEp::Adapter::pEpLog::set_enabled(true); + + // char* tmp = strdup("fd"); + // std::cout << pEp::CXX::Inspect::all(tmp) << std::endl; + // change_ptr(tmp); + // std::cout << pEp::CXX::Inspect::all(tmp) << std::endl; + + // char *ht; + // pEp::String tmp1{true,&ht}; + // + // test_func1(tmp1); + // std::cout << tmp1; + + // POD + if (0) { + // VALID USAGE + int init_val = 0; + // new pEp::POD on int + { + pEpLogH1("new pEp::POD on int"); + int c_int = init_val; + pEp::POD pint(&c_int); + test_getters(&c_int, pint, init_val); + test_assign(&c_int, pint); + } + // equality operator + { + pEpLogH1("equality operator"); + int c_int1 = init_val; + int c_int2 = init_val; + pEp::POD pint1(&c_int1); + pEp::POD pint2(&c_int2); + assert(pint1 == pint2); + + c_int2 = 23; + assert(pint1 != pint2); + } + } + + // String + // pEp::String::log_enabled = false; + if (0) { + //TODO: Test non-owning mode + // + // INVALID USAGE + // char* c_str_u; // uninitialized == INVALID + // undefined behaviour, most likely "pointer being freed was not allocated" + // { + // char* c_str; + // pEp::String pstr(c_str); + // } + // char* c_str_s = "fdsfs"; // statically initialized == INVALID + // { + // char ca[4] = { 'p', 'E', 'p'}; + // char* c_str = ca; + // pEp::String pstr(c_str); + // } + + // VALID USAGE + { + pEpLogH1("new pEp::String on char* pointing to NULL"); + char* c_str = NULL; // nullptr + + pEp::String pstr(&c_str); + test_getters(&c_str, pstr, ""); + test_assign(&c_str, pstr); + } + + std::string init_val{ "initialized c string" }; + { + pEpLogH1("new pEp::String on already initalized char*"); + char* c_str = strdup(init_val.c_str()); + + pEp::String pstr(&c_str); + test_getters(&c_str, pstr, init_val); + test_assign(&c_str, pstr); + } + + // equality operator + { + pEpLogH1("equality operator"); + + char* c_str1 = strdup(init_val.c_str()); + char* c_str2 = strdup(init_val.c_str()); + pEp::String pstr1{ &c_str1 }; + pEp::String pstr2{ &c_str2 }; + assert(pstr1 == pstr2); + pstr2 = "huhu"; + assert(pstr1 != pstr2); + } + } + + + if (1) { + pEpLogH2("Create a sleeve and an instance"); + int i = 23; + pEp::Sleeve ot_i{ i }; + + + pEpLog(pEp::CXX::Inspect::all(i)); + pEpLog(ot_i.to_string()); + assert(ot_i == i); + assert(!ot_i.is(i)); + + pEpLogH3("change from C++ / POTS"); + pEp::Sleeve i_pots{42}; + ot_i = i_pots; + assert(ot_i == i_pots); + assert(!ot_i.is(i_pots)); + + pEpLogH3("change from C++ / native"); + int i_native = 23; + ot_i = i_native; + assert(ot_i == i_native); + assert(!ot_i.is(i_native)); + + pEpLogH3("change from C99"); + i = 42; + int* pi = ot_i.obj_ptr(); + *pi = i; + assert(ot_i == i); + assert(!ot_i.is(i)); + } + + if (1) { + pEpLogH2("create a sleeve on an instance, static"); + int i{ 23 }; + pEp::Sleeve ot_i{ &i }; + pEpLog(pEp::CXX::Inspect::all(i)); + pEpLog(ot_i.to_string()); + assert(ot_i == i); + assert(ot_i.is(i)); + + pEpLogH3("change from C++ / POTS"); + pEp::Sleeve i_pots{42}; + ot_i = i_pots; + assert(ot_i == i_pots); + assert(!ot_i.is(i_pots)); + + pEpLogH3("change from C++ / native"); + int i_native = 23; + ot_i = i_native; + assert(ot_i == i_native); + assert(!ot_i.is(i_native)); + + pEpLogH3("change from C99"); + i = 42; + int* pi = ot_i.obj_ptr(); + *pi = i; + assert(ot_i == i); + assert(ot_i.is(i)); + } + + if (1) { + pEpLogH2("create a sleeve on an instance, dynamic"); + int i{ 23 }; + pEp::Sleeve ot_i{ [&i](){ return &i;} }; + pEpLog(pEp::CXX::Inspect::all(i)); + pEpLog(ot_i.to_string()); + assert(ot_i == i); + assert(ot_i.is(i)); + + pEpLogH3("change from C++ / POTS"); + pEp::Sleeve i_pots{42}; + ot_i = i_pots; + assert(ot_i == i_pots); + assert(!ot_i.is(i_pots)); + + pEpLogH3("change from C++ / native"); + int i_native = 23; + ot_i = i_native; + assert(ot_i == i_native); + assert(!ot_i.is(i_native)); + + pEpLogH3("change from C99"); + i = 42; + int* pi = ot_i.obj_ptr(); + *pi = i; + assert(ot_i == i); + assert(ot_i.is(i)); + } + + // Struct + /* if (0) { + // sleeve empty + { + pEp::Sleeve<::B> pstruct1{ nullptr }; + const B* cc_struct = pstruct1; + B* c_struct = pstruct1; + assert(pstruct1.data() == nullptr); + assert(pstruct1.c_data() == nullptr); + assert(c_struct == nullptr); + assert(cc_struct == nullptr); + pEpLog("DONE"); + + // return_struct(pstruct1); + } + + // Creates a sleeve managing and holding a newly created object + { + pEp::Sleeve<::B> pstruct1{}; + B* c_struct = pstruct1; + c_struct->c_int = 3; + c_struct->c_str = std::string("huhu").data(); + c_struct->c_enum = TWO; + pEpLog("DONE"); + } + + // Create a sleeve managing and holding an already existing object + { + // create an object + ::B* c_struct1 = ::new_B(0, ONE, "pEp"); + pEpLog(c_struct1->c_int); + pEpLog(c_struct1->c_enum); + pEpLog(c_struct1->c_str); + pEpLog("DONE"); + + // reference the object + pEp::Sleeve<::B> pstruct1{ c_struct1 }; + pEpLog("DONE"); + } + + // Create a sleeve managing a C-sleeve + { + + B* c_struct = nullptr; + pEp::Sleeve<::B> pstruct1{ &c_struct }; + // c_struct->c_str = strdup("pEp"); + // c_struct->c_int = 5; + // c_struct->c_enum = TWO; + } + } + */ + // Calling conventions + // pEp::Utils::readKey(); + // { + // pEpLogH1("use_struct()"); + // { + // pEp::A a{}; + // use_struct(a); + // } + // pEpLogH1("c99_supply_struct()"); + // { + // int val = 23; + // // C99 + // { + // // prepare data structure + // ::C99_A* c_a = ::c99_A_new(); + // c_a->b = c99_B_new(); + // c_a->b->c = c99_C_new(); + // c_a->b->c->pi = (int*)calloc(1, sizeof(int)); + // *c_a->b->c->pi = val; + // + // // call supply + // ::C99_Status status = c99_supply_struct(c_a); + // + // // test + // assert(!status); + // assert(*c_a->b->c->pi == val * 2); + // } + + // C++ + { + + // a = b; + // b = 5 + // a.b = pEp::B{}; + // a.b.c = pEp::C{}; + // a.b.c.pi = (int*)calloc(1, sizeof(int)); + // a.b.c.pi = 23; + // ::Status status = supply_struct(a); + // test + // assert(!status); + // assert(*a.b.c.pi == val * 2); + } + // pstruct1.c_int + // } + // pEpLogH1("provide_struct()"); + // { + // pEp::A a{}; + // //release ownership + // provide_struct(a); + // } + // pEpLogH1("return_struct()"); + // { + // A* a = NULL; + // return_struct(&a); + // pEp::A pa{ a }; + // pEpLog(*(pa.b.c.pi.data())); + // } + // } + return 0; +} diff --git a/test/test_sleeve.cc b/test/test_sleeve.cc new file mode 100644 index 0000000..8cf4361 --- /dev/null +++ b/test/test_sleeve.cc @@ -0,0 +1,593 @@ +#include +#include +#include +#include "../src/POTS.hh" +#include "../src/libc99.hh" +//using namespace pEp; +using POTS::operator==; + +#define TEST_IS_COPY(a, b) \ + do { \ + pEpLog("Test is copy"); \ + assert(b == a); \ + assert(!b.is(a)); \ + } while (0); + +#define TEST_IS_REF(a, b) \ + do { \ + pEpLog("Test is ref"); \ + assert(b == a); \ + assert(b.is(a)); \ + } while (0); + +//template +//int TEST_IS_COPY(const A& a, const B& b) +//{ +// pEpLog("Test is copy"); +// assert(b == a); +// assert(!b.is(a)); +// return 0; +//} +// +//template +//int TEST_IS_REF(const A& a, const B& b) +//{ +// pEpLog("Test is ref"); +// assert(b == a); +// assert(b.is(a)); +// return 0; +//} + +//template +//class TestSleeve; + +// ------------------------------------------------------------------------------------------------- +// FUNDAMENTAL +// ------------------------------------------------------------------------------------------------- + +template +class PODFactory { +public: + using FactoryFunc = const std::function; + +public: + PODFactory() = delete; + PODFactory(const T& value) : _factory_func{ new_ }, _value{ value } {} + PODFactory(FactoryFunc& init1, const T& value) : _factory_func{ init1 }, _value{ value } {} + + operator T() + { + return new_value(); + } + + operator T*() + { + return new_obj(); + } + + T* new_obj() + { + return _factory_func(_value); + } + + T& new_value() + { + return *new_obj(); + } + +private: + static T* new_(const T& val) + { + T* ret = (T*)calloc(1, sizeof(T)); + *ret = val; + return ret; + } + + FactoryFunc _factory_func; + const T& _value; +}; + +template +class TestSleeve; + +// VALUE ------------------------------------------------------------------------------------------------- +template +class TestSleeve { +public: + TestSleeve(PODFactory init1, PODFactory init2) : factory1{ init1 }, factory2{ init2 } + { + test_sleeve(); + } + + int test_sleeve() + { + pEpLogH1("Test sleeve<" + std::string(typeid(T).name()) + ">"); + test_HS(); + test_BS(); + return 0; + } + + int test_HS() + { + pEpLogH1("Test HS<" + std::string(typeid(T).name()) + ">"); + { + pEpLogH2("Test HS ctor"); + T a = factory1; + POTS::Sleeve b{ a }; + pEpLog(b.to_string()); + TEST_IS_COPY(a, b); + } + { + pEpLogH2("Test assign HS"); + T a1 = factory1; + POTS::Sleeve b1{ a1 }; + pEpLog(b1.to_string()); + + T a2 = factory2; + POTS::Sleeve b2{ a2 }; + pEpLog(b2.to_string()); + + b1 = b2; + + pEpLog(b1.to_string()); + TEST_IS_COPY(b1, b2); + } + { + pEpLogH2("Test assign BS"); + T a1 = factory1; + POTS::Sleeve b1{ a1 }; + pEpLog(b1.to_string()); + + T a2 = factory2; + ; + POTS::Sleeve b2{ [&a2]() { return &a2; } }; + pEpLog(b2.to_string()); + + b1 = b2; + + pEpLog(b1.to_string()); + TEST_IS_COPY(b1, b2); + } + { + pEpLogH2("Test copy ctor"); + T a = factory1; + POTS::Sleeve b1{ a }; + pEpLog(b1.to_string()); + + POTS::Sleeve b2{ b1 }; + pEpLog(b2.to_string()); + TEST_IS_COPY(b1, b2); + } + return 0; + } + + int test_BS() + { + pEpLogH1("Test BS<" + std::string(typeid(T).name()) + ">"); + { + pEpLogH2("Test BS ctor"); + T a1 = factory1; + POTS::Sleeve b1{ [&a1]() { return &a1; } }; + pEpLog(b1.to_string()); + TEST_IS_REF(a1, b1); + } + { + pEpLogH2("Test assign HS"); + T a1 = factory1; + POTS::Sleeve b1{ [&a1]() { return &a1; } }; + pEpLog(b1.to_string()); + + T a2 = factory2; + ; + POTS::Sleeve b2{ a2 }; + pEpLog(b2.to_string()); + + b1 = b2; + + pEpLog(b1.to_string()); + TEST_IS_COPY(b1, b2); + } + { + pEpLogH2("Test assign BS"); + T a1 = factory1; + POTS::Sleeve b1{ [&a1]() { return &a1; } }; + pEpLog(b1.to_string()); + + T a2 = factory2; + ; + POTS::Sleeve b2{ [&a2]() { return &a2; } }; + pEpLog(b2.to_string()); + + b1 = b2; + + pEpLog(b1.to_string()); + TEST_IS_COPY(b1, b2); + } + { + pEpLogH2("Test copy ctor"); + T a1 = factory1; + pEpLog(a1); + POTS::Sleeve b1{ [&a1]() { return &a1; } }; + pEpLog(b1); + pEpLog(b1.to_string()); + + POTS::Sleeve b2{ b1 }; + pEpLog(b2.to_string()); + + TEST_IS_COPY(b1, b2); + } + return 0; + } + +private: + PODFactory factory1; + PODFactory factory2; +}; + + +// POINTER ------------------------------------------------------------------------------------------------- + +template +class TestSleeve { +public: + TestSleeve(PODFactory init1, PODFactory init2) : factory1{ init1 }, factory2{ init2 } + { + test_sleeve(); + } + + int test_sleeve() + { + pEpLogH1("Test sleeve<" + std::string(typeid(T*).name()) + ">"); + test_HS(); + test_BS(); + return 0; + } + + int test_HS() + { + pEpLogH1("Test HS<" + std::string(typeid(T*).name()) + ">"); + { + pEpLogH2("Test HS ctor"); + POTS::Sleeve b1{}; + pEpLog(b1.to_string()); + } + { + pEpLogH2("Test assign RAW-T (value)"); + POTS::Sleeve b1{}; + pEpLog(b1.to_string()); + + T b2 = factory2; + pEpLog(b2); + + b1 = b2; + + pEpLog(b1.to_string()); + TEST_IS_COPY(b2, b1); + } + { + pEpLogH2("Test assign HS"); + POTS::Sleeve b1{}; + pEpLog(b1.to_string()); + + POTS::Sleeve b2{}; + pEpLog(b2.to_string()); + + b1 = b2; + pEpLog(b1.to_string()); + TEST_IS_REF(b1, b2); + } + { + pEpLogH2("Test assign BS"); + POTS::Sleeve b1{}; + pEpLog(b1.to_string()); + + T* a2 = factory1; + POTS::Sleeve b2{ [&a2]() { return &a2; } }; + pEpLog(b2.to_string()); + + b1 = b2; + pEpLog(b1.to_string()); + TEST_IS_REF(b1, b2); + } + { + pEpLogH2("Test copy ctor"); + POTS::Sleeve b1{}; + pEpLog(b1.to_string()); + + POTS::Sleeve b2{ b1 }; + pEpLog(b2.to_string()); + TEST_IS_REF(b1, b2); + } + return 0; + } + + int test_BS() + { + pEpLogH1("Test BS"); + { + pEpLogH2("Test BS ctor"); + T* a1 = factory1; + POTS::Sleeve b1{ [&a1]() { return &a1; } }; + pEpLog(b1.to_string()); + TEST_IS_REF(a1, b1); + } + { + pEpLogH2("Test assign RAW-T (value)"); + T* a1 = factory1; + POTS::Sleeve b1{ [&a1]() { return &a1; } }; + pEpLog(b1.to_string()); + + T b2 = factory2; + pEpLog(b2); + b1 = b2; + + pEpLog(b1.to_string()); + TEST_IS_COPY(b2, b1); + } + { + pEpLogH2("Test assign HS"); + T* a1 = factory1; + POTS::Sleeve b1{ [&a1]() { return &a1; } }; + pEpLog(b1.to_string()); + + POTS::Sleeve b2{}; + pEpLog(b2.to_string()); + + b1 = b2; + pEpLog(b1.to_string()); + TEST_IS_REF(b1, b2); + } + { + pEpLogH2("Test assign BS"); + T* a1 = factory1; + POTS::Sleeve b1{ [&a1]() { return &a1; } }; + pEpLog(b1.to_string()); + + T* a2 = factory2; + POTS::Sleeve b2{ [&a2]() { return &a2; } }; + pEpLog(b2.to_string()); + + b1 = b2; + pEpLog(b1.to_string()); + TEST_IS_REF(b1, b2); + } + { + pEpLogH2("Test copy ctor"); + + T* a1 = factory1; + POTS::Sleeve b1{ [&a1]() { return &a1; } }; + pEpLog(b1.to_string()); + + POTS::Sleeve b2{ b1 }; + pEpLog(b2.to_string()); + TEST_IS_REF(b1, b2); + } + return 0; + } + +private: + PODFactory factory1; + PODFactory factory2; +}; + + +// STRUCT P ------------------------------------------------------------------------------------------------- +template<> +//class TestSleeve::value || std::is_enum::value>::type> { +class TestSleeve<::C99_C*> { +public: + TestSleeve(PODFactory<::C99_C> init1, PODFactory<::C99_C> init2) : + factory1{ init1 }, + factory2{ init2 } + { + test_sleeve(); + } + + int test_sleeve() + { + pEpLogH1("Test sleeve<" + std::string(typeid(::C99_C*).name()) + ">"); + test_HS(); + test_BS(); + return 0; + } + + int test_HS() + { + pEpLogH1("Test HS<" + std::string(typeid(::C99_C*).name()) + ">"); + { + pEpLogH2("Test HS ctor"); + LIBC99::C99_C b1{}; + pEpLog(b1.to_string()); + pEpLog(b1.to_string()); + } + { + pEpLogH2("Test assign RAW-T (value)"); + LIBC99::C99_C b1{}; + pEpLog(b1.to_string()); + + ::C99_C b2 = factory2; + + pEpLog(b2); + pEpLog(CXX::Inspect::all(b2.pi)); + + b1 = b2; + + pEpLog(b1.to_string()); + // TODO: cant test yet + // TEST_IS_COPY(b2, b1); + } + { + pEpLogH2("Test assign HS"); + LIBC99::C99_C b1{}; + pEpLog(b1.to_string()); + + LIBC99::C99_C b2{}; + pEpLog(b2.to_string()); + + b1 = b2; + + pEpLog(b1.to_string()); + TEST_IS_REF(b1, b2); + } + { + pEpLogH2("Test assign BS"); + LIBC99::C99_C b1{}; + pEpLog(b1.to_string()); + + ::C99_C* a2 = factory1; + LIBC99::C99_C b2{ [&a2]() { return &a2; } }; + pEpLog(b2.to_string()); + + b1 = b2; + + pEpLog(b2.to_string()); + TEST_IS_REF(b1, b2); + } + if (1 ) { + pEpLogH2("Test copy ctor"); + LIBC99::C99_C b1{}; + b1.pi = 23; + pEpLog(b1.to_string()); + + LIBC99::C99_C b2{ b1 }; + pEpLog(b2.to_string()); + TEST_IS_REF(b1, b2); + } + return 0; + } + + int test_BS() + { + pEpLogH1("Test BS"); + { + pEpLogH2("Test BS ctor"); + ::C99_C* a1 = factory1; + LIBC99::C99_C b1{ [&a1]() { return &a1; } }; + pEpLog(b1.to_string()); + TEST_IS_REF(a1, b1); + } + { + pEpLogH2("Test assign RAW-T (value)"); + ::C99_C* a1 = factory1; + LIBC99::C99_C b1{ [&a1]() { return &a1; } }; + pEpLog(b1.to_string()); + + ::C99_C b2 = factory2; + + pEpLog(b2); + pEpLog(CXX::Inspect::all(b2.pi)); + + b1 = b2; + + pEpLog(b1.to_string()); + // TODO: cant test yet + // TEST_IS_COPY(b2, b1); + } + { + pEpLogH2("Test assign HS"); + ::C99_C* a1 = factory1; + LIBC99::C99_C b1{ [&a1]() { return &a1; } }; + pEpLog(b1.to_string()); + + LIBC99::C99_C b2{}; + pEpLog(b2.to_string()); + + b1 = b2; + pEpLog(b1.to_string()); + TEST_IS_REF(b1, b2); + } + { + pEpLogH2("Test assign BS"); + ::C99_C* a1 = factory1; + LIBC99::C99_C b1{ [&a1]() { return &a1; } }; + pEpLog(b1.to_string()); + + ::C99_C* a2 = factory2; + LIBC99::C99_C b2{ [&a2]() { return &a2; } }; + pEpLog(b2.to_string()); + + b1 = b2; + pEpLog(b1.to_string()); + TEST_IS_REF(b1, b2); + } + if (0) { + pEpLogH2("Test copy ctor"); + ::C99_C* a1 = factory1; + LIBC99::C99_C b1{ [&a1]() { return &a1; } }; + pEpLog(b1.to_string()); + + LIBC99::C99_C b2{ b1 }; + pEpLog(b2.to_string()); + TEST_IS_REF(b1, b2); + } + + return 0; + } + +private: + PODFactory<::C99_C> factory1; + PODFactory<::C99_C> factory2; +}; + +template +int test_sleeve(PODFactory init1, PODFactory init2) +{ + // TestSleeve{ init1, init2 }; + TestSleeve{ init1, init2 }; + return 0; +} + + +int main() +{ + // Utils::readKey(); + pEp::Adapter::pEpLog::set_enabled(true); + test_sleeve(PODFactory(23), PODFactory(42)); + // test_sleeve(PODFactory('a'), PODFactory('b')); + // test_sleeve(PODFactory(2.3f), PODFactory(4.2f)); + // test_sleeve(PODFactory(true), PODFactory(false)); + // test_sleeve<::C99_Status>(PODFactory<::C99_Status>(OK), PODFactory<::C99_Status>(ERROR)); + + + // pEpLog(CXX::Inspect::all(*a.pi)); + + ::C99_C a{}; + a.i = 1; + a.pi = PODFactory(23); + + ::C99_C b{}; + b.i = 2; + b.pi = PODFactory(42); + + test_sleeve<::C99_C>( + PODFactory<::C99_C>( + [](const ::C99_C& val) { + ::C99_C* a = (::C99_C*)calloc(1, sizeof(::C99_C)); + a->i = PODFactory(val.i); + a->pi = PODFactory(*val.pi); + return a; + }, + a), + PODFactory<::C99_C>( + [](const ::C99_C& val) { + ::C99_C* a = (::C99_C*)calloc(1, sizeof(::C99_C)); + a->i = PODFactory(val.i); + a->pi = PODFactory(*val.pi); + return a; + }, + b)); + + /* { + ::C99_C c{}; + c.i = 1; + c.pi = PODFactory(23); + + ::C99_B b{}; + b.c = PODFactory<::C99_C>(c); + + ::C99_A a{}; + a.b = PODFactory<::C99_B>(b); + } + */ + return 0; +} diff --git a/test/test_struct_cmp.cc b/test/test_struct_cmp.cc new file mode 100644 index 0000000..cdd3537 --- /dev/null +++ b/test/test_struct_cmp.cc @@ -0,0 +1,237 @@ +#include +#include +#include "pEp/inspect.hh" +#include "../examples/libc99/libc99.h" +#include "../src/libc99.hh" +//#include "../src/POTS.hh" + +using namespace pEp; + + +// This is a protoype pattern trying to make it possible to compare objects of +// (possibly) different types. + +// + +namespace NA { + class ABase; + bool operator==(ABase& a, ABase& b); + bool operator!=(ABase& a, ABase& b); + + class ABase { + public: + ABase() = default; + ABase(const std::string& name, ABase& a) + { + a.add(name, *this); + } + + void add(const std::string& name, ABase& a) + { + _members.insert(std::pair(name, a)); + } + + virtual bool cmp(ABase& rhs) + { + for (std::pair member : _members) { + try { + ABase& other = rhs._members.at(member.first); + if (other != member.second) { + return false; + } + } catch (const std::out_of_range& e) { + return false; + } + } + return true; + } + + protected: + std::map _members{}; + }; + + + template + class A : public ABase { + public: + A(T* obj) : _obj{ obj } + { + pEpLog("T"); + } + + A(const std::string& name, ABase& a, T* obj) : ABase(name, a), _obj{ obj } {} + + + bool cmp(ABase& rhs) override + { + if constexpr (!std::is_class::value) { + try { + A& rhs_c = dynamic_cast&>(rhs); + if (*_obj == *rhs_c._obj) { + return true; + } else { + return false; + } + } catch (...) { + try { + A& rhs_c = dynamic_cast&>(rhs); + if (*_obj == **rhs_c._obj) { + return true; + } else { + return false; + } + } catch (...) { + return false; + } + } + } else { + return ABase::cmp(rhs); + } + } + + // T _value; + T* _obj; + }; + + template + class A : public ABase { + public: + A(T** obj) : _obj{ obj } + { + pEpLog("T*"); + } + + A(const std::string& name, ABase& a, T** obj) : ABase(name, a), _obj{ obj } {} + + bool cmp(ABase& rhs) override + { + if constexpr (!std::is_class::value) { + try { + A& rhs_c = dynamic_cast&>(rhs); + if (**_obj == **rhs_c._obj) { + return true; + } else { + return false; + } + } catch (...) { + try { + A& rhs_c = dynamic_cast&>(rhs); + if (**_obj == *rhs_c._obj) { + return true; + } else { + return false; + } + } catch (...) { + return false; + } + } + } else { + return ABase::cmp(rhs); + } + } + + // T* _value_ptr; + T** _obj; + }; + + bool operator==(ABase& a, ABase& b) + { + return a.cmp(b) && b.cmp(a); + } + + bool operator!=(ABase& a, ABase& b) + { + return !(a == b); + } + +} + + +/* +template +class B : public A { +public: + B() : A() + { + pEpLog("T"); + } + int _T_Bool_ext; +}; +*/ + +//template +//class A::value>::type> : public A { +//public: +// A() +// { +// pEpLog("T* bool"); +// } +// int _Tp_Bool_ext; +//}; + + +//template<> +//class A { +//public: +// A() +// { +// pEpLog("char*"); +// } +// +// char* a; +//}; + +struct Struct { + int a; + int b; +}; + +using NA::operator==; +using NA::operator!=; + +int main() +{ + Adapter::pEpLog::set_enabled(true); + pEpLog("start"); + // A i{}; + // A ip{}; + // B bi{}; + // B bip{}; + + // A b{}; + // A bp{}; + { + float f = 3.0f; + int i = 23; + int* pi = &i; + NA::A a{ &i }; + NA::A b{ &f }; + NA::A ap{ &pi }; + assert(a != b); + assert(a == ap); + } + { + Struct a{}; + a.a = 23; + a.b = 42; + NA::A b1{ &a }; + NA::A b1_a{ "a", b1, &a.a }; + NA::A b1_b{ "b", b1, &a.b }; + + NA::A b2{ &a }; + NA::A b2_a{ "a", b2, &a.a }; + NA::A b2_b{ "b", b2, &a.b }; + + assert(b1 == b2); + + Struct a2{}; + a2.a = 23; + a2.b = 42; + + NA::A b3{ &a2 }; + NA::A b3_a{ "a", b3, &a2.a }; + NA::A b3_b{ "b", b3, &a2.b }; + + assert(b2 == b3); + } +}