Browse Source

FAILED ATTEMPT - just committing everything 5 months after losing my sanity.

heck-rework
heck 3 years ago
parent
commit
7e823516d4
  1. 1
      .gitignore
  2. 3
      examples/libc99/libc99.h
  3. 897
      src/POTS.hh
  4. 180
      src/libc99.hh
  5. 474
      src/prototype.hh
  6. 73
      test/test_cxx.cc
  7. 42
      test/test_identity.cc
  8. 247
      test/test_nr1.cc
  9. 414
      test/test_prototype.cc
  10. 593
      test/test_sleeve.cc
  11. 237
      test/test_struct_cmp.cc

1
.gitignore

@ -13,3 +13,4 @@ build/
/examples/libc99/libc99.so /examples/libc99/libc99.so
/test/test_identity /test/test_identity
/test/.pEp/ /test/.pEp/
/test/test_sleeve

3
examples/libc99/libc99.h

@ -62,7 +62,8 @@ extern "C" {
// T* // T*
C99_Status c99_supply_basic(int* basic); C99_Status c99_supply_basic(int* basic);
C99_Status c99_supply_struct(C99_A* a); 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 // provide
// in parm, ownership goes to callee // in parm, ownership goes to callee

897
src/POTS.hh

@ -0,0 +1,897 @@
#include <iostream>
#include <cctype>
#include <utility>
#include <pEp/utils.hh>
#include <pEp/pEpLog.hh>
#include <pEp/inspect.hh>
#include <type_traits>
#include <map>
#include <optional>
#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<std::string, BasicSleeve&>(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<std::string, BasicSleeve&> 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<std::string, BasicSleeve&> 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<std::string, BasicSleeve&> member : _members) {
member.second.leave_ownership_recurse();
}
}
virtual void rebind()
{
for (std::pair<std::string, BasicSleeve&> member : _members) {
member.second.rebind();
}
}
virtual std::string to_string(int indent = 0) const
{
std::stringstream ss{};
for (std::pair<std::string, BasicSleeve&> 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<std::string, BasicSleeve&> member : _members) {
ss << member.second.repr(indent);
}
return ss.str();
}
std::optional<std::string> 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<std::string> _name{};
std::optional<std::reference_wrapper<BasicSleeve>> _parent{};
std::map<std::string, BasicSleeve&> _members{};
};
// template<typename T, class Enable = void>
// class Sleeve;
//---------------------------------------------------------------------------------------------
// VALUE
//---------------------------------------------------------------------------------------------
template<typename T>
class Sleeve {
// class Sleeve<T, typename std::enable_if<std::is_fundamental<T>::value || std::is_enum<T>::value || std::is_class<T>::value>::type>
// : public BasicSleeve {
public:
using ResolverFunc = std::function<T*()>;
// 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<T>& rhs) :
_target_resolver{ [this]() { return &_hosted_obj; } },
_hosted_obj{ rhs.obj() }
{
pEpLogClass("copy ctor");
}
// Assignment
// ----------
// takes a copy
Sleeve<T>& operator=(const Sleeve<T>& rhs)
{
pEpLogClass("called");
*target() = const_cast<Sleeve<T>&>(rhs).obj();
return *this;
}
// takes a copy
Sleeve<T>& 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<T>& rhs) const
{
pEpLogClass("called");
return _this()->obj() == const_cast<Sleeve<T>&>(rhs).obj();
}
bool operator==(const T& rhs) const
{
pEpLogClass("called");
return _this()->obj() == rhs;
}
bool operator!=(const Sleeve<T>& rhs) const
{
pEpLogClass("called");
return !operator==(rhs);
}
bool operator!=(const T& rhs) const
{
pEpLogClass("called");
return !operator==(rhs);
}
// object comparison
bool is(const Sleeve<T>& 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<uintptr_t>(_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<T>* _this() const
{
return const_cast<Sleeve<T>*>(this);
}
};
//---------------------------------------------------------------------------------------------
// POINTER
//---------------------------------------------------------------------------------------------
template<typename T>
class Sleeve<T*>
// class Sleeve<T*, typename std::enable_if<std::is_fundamental<T>::value || std::is_enum<T>::value || std::is_class<T>::value>::type>
: public BasicSleeve {
protected:
//TODO simply use T
using SharedPtr = std::shared_ptr<typename std::remove_pointer<T*>::type>;
using AllocFunc = std::function<T*()>;
using FreeFunc = std::function<void(T*)>;
const std::function<void(void*)> _deleter_bypass = [](void*) {};
public:
using ResolverFunc = std::function<T**()>;
// -----------------------------------------------------------------------------------------
// 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<T*>& 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<T*>& operator=(const Sleeve<T*>& rhs)
{
pEpLogClass("called");
if constexpr (std::is_class<T>::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<T*>& operator=(const BasicSleeve& rhs) override
{
pEpLogClass("called");
try {
// try to cast rhs to own type
const Sleeve<T*>& rhs_c = dynamic_cast<const Sleeve<T*>&>(rhs);
operator=(rhs_c);
} catch (...) {
throw std::runtime_error("INVALID TYPES");
}
return *this;
}
// replace the value
// Sleeve<T*>& operator=(T* rhs)
// {
// pEpLogClass("called");
// leave_ownership();
//
// _sptr = SharedPtr{ rhs, _deleter_bypass };
// *target() = rhs;
//
// if constexpr (std::is_class<T>::value) {
// //rebind
// }
//
// return *this;
// }
// replace the value
// deep-copy
// if empty, create a new instance on the target
Sleeve<T*>& operator=(const T& rhs)
{
pEpLogClass("called");
// only members leave ownership
if constexpr (std::is_class<T>::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<T>::value) {
BasicSleeve::rebind();
}
return *this;
}
void rebind() override
{
if constexpr (std::is_class<T>::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<T>::value) {
// try convert it to own type Sleeve<T>
try {
const Sleeve<T*>& rhs_c = dynamic_cast<const Sleeve<T*>&>(rhs);
// both have an object, lets compare values
if (!is_empty() && !rhs_c.is_empty()) {
return *_this()->obj() == *const_cast<Sleeve<T*>&>(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<BasicSleeve&>(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<T>::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<T>::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<T*>& rhs) const
// {
// pEpLogClass("called");
// return !operator==(rhs);
// }
bool operator!=(const T* rhs) const
{
pEpLogClass("called");
if constexpr (!std::is_class<T>::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<T>::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<T*>& 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<T>::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<uintptr_t>(_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<T>::value) {
builder << std::string(indent, '\t') << "value : "
<< (!is_empty() ? CXX::Inspect::all(*_this()->obj()) : "<NULL>");
} 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<T>::value) {
builder << (!is_empty() ? CXX::Inspect::val(*_this()->obj()) : "<NULL>") << "\"";
} 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<T*>* _this() const
{
return const_cast<Sleeve<T*>*>(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<int*> pi{ "pi", *this, [this]() { return &this->obj()->pi; } };
// Sleeve<int*> 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

180
src/libc99.hh

@ -0,0 +1,180 @@
#ifndef LIBPEPDATAYPES_LIBC99_HH
#define LIBPEPDATAYPES_LIBC99_HH
#include "../examples/libc99/libc99.h"
#include <exception>
#include <vector>
#include <string>
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 "<NULL>";
}
}
// 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 "<NULL>";
}
}
// 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) : "<NULL>") << "'"
<< 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 "<NULL>";
}
}
// 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 "<NULL>";
}
}
// 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 "<NULL>";
}
}
} // 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

474
src/prototype.hh

@ -133,31 +133,15 @@ namespace pEp {
// Thats the only difference. // Thats the only difference.
class String { class String {
public: public:
String()
{
pEpLogClass("called");
}
// Best to use this constructor, as the object is in a valid state when the wrapped // Best to use this constructor, as the object is in a valid state when the wrapped
// c_str (char*) is known // c_str (char*) is known
String(bool is_owner, char** c_str_pp) String(char** c_str_pp)
{ {
bind(is_owner, c_str_pp); bind(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 and set ownership // bind and set ownership
void bind(bool is_owner, char** c_str_pp) void bind(char** c_str_pp)
{ {
if (c_str_pp == nullptr) { if (c_str_pp == nullptr) {
throw Exception{ EXSTR("cant bind on a nullptr") }; throw Exception{ EXSTR("cant bind on a nullptr") };
@ -168,13 +152,12 @@ namespace pEp {
} }
// init // init
_c_str_pp = c_str_pp; _target = c_str_pp;
_is_bound = true; _is_bound = true;
pEpLogClass(to_string()); pEpLogClass(to_string());
set_owner(is_owner);
//if the c_str_p is nullptr then init with "" //if the c_str_p is nullptr then init with ""
if (*_c_str_pp == nullptr) { if (*_target == nullptr) {
this->operator=(""); this->operator=("");
} }
} }
@ -182,7 +165,7 @@ namespace pEp {
~String() ~String()
{ {
if (_is_owner) { if (_is_owner) {
_free(*_c_str_pp); _free(*_target);
} }
} }
@ -190,15 +173,15 @@ namespace pEp {
String& operator=(const std::string& str) String& operator=(const std::string& str)
{ {
pEpLogClass("Before: " + to_string() + " - new val: '" + pEp::Utils::clip(str, 30) + "'"); pEpLogClass("Before: " + to_string() + " - new val: '" + pEp::Utils::clip(str, 30) + "'");
if (_c_str_pp == nullptr) { if (_target == nullptr) {
throw Exception{ EXSTR("invalid state") }; throw Exception{ EXSTR("invalid state") };
} }
// DEALLOCATION // DEALLOCATION
_free(*_c_str_pp); _free(*_target);
// ALLOCATION // ALLOCATION
*_c_str_pp = _copy(str); *_target = _copy(str);
pEpLogClass("After: " + to_string()); pEpLogClass("After: " + to_string());
return *this; return *this;
} }
@ -206,29 +189,53 @@ namespace pEp {
// return a copy of whatever c_str currently is (maybe created by us, maybe changed meanwhile) // return a copy of whatever c_str currently is (maybe created by us, maybe changed meanwhile)
operator std::string() const operator std::string() const
{ {
if (_c_str_pp == nullptr) { if (_target == nullptr) {
throw Exception{ EXSTR("invalid state") }; throw Exception{ EXSTR("invalid state") };
} }
if (*_c_str_pp != nullptr) { if (*_target != nullptr) {
return { *_c_str_pp }; return { *_target };
} }
return {}; 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") }; 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 bool operator==(const pEp::String& pstr) const
{ {
return *(pstr.c_data()) == (*c_data()); return *(pstr.c_data()) == (*c_data());
@ -242,12 +249,12 @@ namespace pEp {
std::string to_string() const std::string to_string() const
{ {
if (_c_str_pp == nullptr) { if (_target == nullptr) {
throw Exception{ EXSTR("invalid state") }; throw Exception{ EXSTR("invalid state") };
} }
std::string ret{ "[" + CXX::Inspect::all(_c_str_pp) + " / " + std::string ret{ "[" + CXX::Inspect::all(_target) + " / " +
CXX::Inspect::all(*_c_str_pp) + "]" }; CXX::Inspect::all(*_target) + "]" };
return ret; return ret;
} }
@ -278,9 +285,9 @@ namespace pEp {
::pEp_free(ptr_type); ::pEp_free(ptr_type);
} }
bool _is_owner{ true };
bool _is_bound{ false }; bool _is_bound{ false };
bool _is_owner{ false }; char** _target{ nullptr };
char** _c_str_pp{ nullptr };
Adapter::pEpLog::pEpLogger logger{ "pEp::String", log_enabled }; Adapter::pEpLog::pEpLogger logger{ "pEp::String", log_enabled };
Adapter::pEpLog::pEpLogger& m4gic_logger_n4me = logger; 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 // 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. // 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. // If if owns it, it will free() it when the wrapper dies, otherwise it doesnt.
class BasicSleeve {
public:
virtual ~BasicSleeve() = default;
};
template<typename T> template<typename T>
class PODStruct { class Sleeve : public BasicSleeve {
public: 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<T*()> resolver) : _effective_resolver{ std::move(resolver) }
{
pEpLogClass("dynamic sleeve");
}
// create a new instance and copy the value
explicit Sleeve(const Sleeve<T>& rhs) :
_effective_resolver{ [this]() { return &_obj; } },
_obj{ rhs._obj }
{
pEpLogClass("copy ctor");
}
// Creates a new instance or binds an existing one // Assignment
PODStruct(bool is_owner, T** c_struct_pp = nullptr) // ----------
// takes a copy
Sleeve<T>& operator=(const Sleeve<T>& rhs)
{ {
pEpLogClass("called"); pEpLogClass("called");
_init(is_owner, c_struct_pp); *obj_ptr() = rhs._obj;
return *this;
} }
// Creates a new instance or binds an existing one // assign the binding
// but takes custom alloc/free functions Sleeve<T>& operator=(const T& rhs)
PODStruct(
bool is_owner,
std::function<T*()> alloc,
std::function<void(T*)> free,
T** c_struct_pp = nullptr) :
_effective_alloc(std::move(alloc)),
_effective_free(std::move(free))
{ {
_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<T>& rhs) const
{ {
if (c_struct_pp == nullptr) { pEpLogClass("called");
throw Exception{ EXSTR("cant bind on a nullptr") }; return *const_cast<Sleeve<T>*>(this)->obj_ptr() == *const_cast<Sleeve<T>&>(rhs).obj_ptr();
} }
if (_is_bound) { bool operator==(const T& rhs) const
throw Exception{ EXSTR("can only bind once") }; {
} pEpLogClass("called");
return *const_cast<Sleeve<T>*>(this)->obj_ptr() == rhs;
}
// init // bool operator!=(const Sleeve<T>& rhs) const
_c_struct_pp = c_struct_pp; // {
_c_struct_p = *_c_struct_pp; // pEpLogClass("called");
_is_bound = true; // return !operator==(rhs);
pEpLogClass(to_string()); // }
set_owner(is_owner);
bool operator!=(const T& rhs) const
{
pEpLogClass("called");
return !operator==(rhs);
} }
~PODStruct() // object comparison
bool is(const Sleeve<T>& rhs) const
{ {
if (_is_owner) { pEpLogClass("called");
_effective_free(_c_struct_p); return id() == rhs.id();
}
} }
T** data() bool is(const T& rhs) const
{ {
return _c_struct_pp; pEpLogClass("called");
return const_cast<Sleeve<T>*>(this)->obj_ptr() == &rhs;
} }
const T* c_data() const // Accessors
// ---------
// Access the object data r/w
T data()
{ {
if (_c_struct_pp == nullptr) { pEpLogClass("called");
throw Exception{ EXSTR("invalid state") }; return *obj_ptr();
}
return *_c_struct_pp;
} }
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<long>(const_cast<Sleeve<T>*>(this)->obj_ptr());
}
std::string to_string()
{
std::string ret{ "[" + CXX::Inspect::all(*obj_ptr()) + "]" };
return ret; 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<T*()> _effective_resolver;
T _obj;
};
template<typename T>
class Sleeve<T*> : 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)); pEpLogClass("new");
_is_owner = is_owner;
} }
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<T**()> 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: protected:
Adapter::pEpLog::pEpLogger& m4gic_logger_n4me = logger; Adapter::pEpLog::pEpLogger& m4gic_logger_n4me = logger;
private:
const std::function<T*()> _effective_alloc{ std::bind(&PODStruct::_alloc, this) }; const std::function<T*()> _effective_alloc{ std::bind(&Sleeve::_default_alloc, this) };
const std::function<void(T*)> _effective_free{ const std::function<void(T*)> _effective_free{
std::bind(&PODStruct::_free, this, std::placeholders::_1) std::bind(&Sleeve::_default_free, this, std::placeholders::_1)
}; };
bool _is_bound{ false }; // We have as many indirections as we have levels in the hosting data structure
bool _is_owner{ false }; // When the obj is member of a struct, the obj we actually wrap can change without
T** _c_struct_pp{ nullptr }; // further noctice from the C99 side.
T* _c_struct_p{ nullptr }; // 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
void _init(bool is_owner, T** c_struct_pp) // 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.
// if no pp is given, alloc new, // Using this technique, we can also accommodate for wrapping an object which can
// otherwise bind to it // be REPLACED without further notice, handing out a T**.
if (!c_struct_pp) { const std::function<T**()> _effective_resolver{ std::bind(&Sleeve::_default_resolver, this) };
_c_struct_p = _effective_alloc();
bind(is_owner, &_c_struct_p); bool _is_owning{ true };
} else { // we ALWAYS have a _target that points to either an internal or external obj
bind(is_owner, c_struct_pp); 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<typename std::remove_pointer<T*>::type> _sptr;
// defaults for customizable functions
// -----------------------------------
// default alloc/free T* _default_alloc()
T* _alloc()
{ {
T* ret = (T*)calloc(1, sizeof(T)); T* ret = (T*)calloc(1, sizeof(T));
pEpLogClass(CXX::Inspect::all(ret)); pEpLogClass(CXX::Inspect::all(ret));
return ret; return ret;
} }
void _free(T* ptr) void _default_free(T* ptr)
{ {
pEpLogClass(CXX::Inspect::all(ptr)); pEpLogClass(CXX::Inspect::all(ptr));
free(ptr); free(ptr);
} }
class Exception : public std::runtime_error { T** _default_resolver()
public: {
explicit Exception(const std::string& msg) : std::runtime_error(msg) {} 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: // template<typename T>
// ctor not exception safe // class Sleeve<T*> : public BasicSleeve {
// 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.
// //
// };
// TOOD://
// alloc/free: // alloc/free:
// the alloc() and free() functions HAVE to use calloc/malloc NOT 'new', because we are interfacing // 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 // 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) // 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: public:
TestStruct1(bool is_owner, ::Test_struct1** test_struct1_p = nullptr) : // create a new instance
PODStruct<::Test_struct1>(is_owner, test_struct1_p) explicit C() : Sleeve<::C99_C*>() {}
{ // create an instance wrapper
} // explicit C(::C* obj) : Sleeve<::C>(obj) {}
// create an instance-ptr wrapper
// fields of the struct as 'properties' ;) // explicit C(::C** obj_ptr) : Sleeve<::C>(obj_ptr) {}
pEp::POD<int> c_int{ &(*data())->c_int }; explicit C(std::function<::C99_C**()> resolver) : Sleeve<::C99_C*>(resolver) {}
pEp::POD<::Test_enum> c_enum{ &(*data())->c_enum };
pEp::String c_str{ is_owner(), &(*data())->c_str }; pEp::Sleeve<int> i{ [this]() { return &dynamic_cast<C*>(this)->data()->i; } };
// pEp::Sleeve<int*> pi{ this, [](BasicSleeve* parent) {
// return &dynamic_cast<C*>(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<B*>(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<A*>(parent)->data()->b; } };
// };
} // namespace pEp } // namespace pEp

73
test/test_cxx.cc

@ -0,0 +1,73 @@
#include <map>
#include <pEp/pEpLog.hh>
#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<typename T>
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<A> {
public:
A() : _obj{ std::make_shared<Struct>() } {
a.bind(&_obj->a);
b.bind(&_obj->b);
}
std::shared_ptr<Struct> _obj;
Field<int> a{};
Field<int> 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);
}

42
test/test_identity.cc

@ -8,20 +8,16 @@
//--------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------
namespace pEp { namespace pEp {
class Identity : public PODStruct<::pEp_identity> { class Identity : public Sleeve<::pEp_identity> {
public: public:
Identity(bool is_owner, ::pEp_identity** ident_p = nullptr) : Identity(::pEp_identity** ident_p = nullptr) : PODStruct<::pEp_identity>(ident_p) {}
PODStruct<::pEp_identity>(is_owner, ident_p)
{
}
Identity( Identity(
bool is_owner,
const std::string& address = "", const std::string& address = "",
const std::string& username = "", const std::string& username = "",
const std::string& user_id = "", const std::string& user_id = "",
const std::string& fpr = "") : const std::string& fpr = "") :
PODStruct<::pEp_identity>(is_owner, nullptr) Sleeve<::pEp_identity>(nullptr)
{ {
pEpLogClass("called"); pEpLogClass("called");
this->address = address; this->address = address;
@ -30,21 +26,20 @@ namespace pEp {
this->fpr = fpr; this->fpr = fpr;
} }
pEp::String address{ is_owner(), &(*data())->address }; pEp::String fpr{ &data()->fpr };
pEp::String fpr{ is_owner(), &(*data())->fpr }; pEp::String user_id{ &data()->user_id };
pEp::String user_id{ is_owner(), &(*data())->user_id }; pEp::String username{ &data()->username };
pEp::String username{ is_owner(), &(*data())->username }; pEp::POD<::PEP_comm_type> comm_type{ &data()->comm_type };
pEp::POD<::PEP_comm_type> comm_type{ &(*data())->comm_type };
//char[3] lang //char[3] lang
pEp::POD<bool> me{ &(*data())->me }; pEp::POD<bool> me{ &data()->me };
pEp::POD<unsigned int> major_ver{ &(*data())->major_ver }; pEp::POD<unsigned int> major_ver{ &data()->major_ver };
pEp::POD<unsigned int> minor_ver{ &(*data())->minor_ver }; pEp::POD<unsigned int> minor_ver{ &data()->minor_ver };
pEp::POD<::PEP_enc_format> enc_format{ &(*data())->enc_format }; pEp::POD<::PEP_enc_format> enc_format{ &data()->enc_format };
pEp::POD<::identity_flags_t> flags{ &(*data())->flags }; pEp::POD<::identity_flags_t> flags{ &data()->flags };
}; };
template<> template<>
bool PODStruct<::pEp_identity>::log_enabled{ false }; bool Sleeve<::pEp_identity>::log_enabled{ false };
} // namespace pEp } // namespace pEp
@ -56,20 +51,21 @@ int main()
::init(&session, nullptr, nullptr, nullptr); ::init(&session, nullptr, nullptr, nullptr);
// create identity // 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.username = "alice";
id1.address = "alice@peptest.org"; id1.address = "alice@peptest.org";
id1.
pEpLog(id1.address); pEpLog(id1.address);
pEpLog(id1.username); pEpLog(id1.username);
::myself(session, id1); ::myself(session, id1);
pEpLog(id1); pEpLog(id1.c_data());
pEp::Identity id2{ true, "bob" }; pEp::Identity id2{ "bob" };
::update_identity(session, id2); ::update_identity(session, id2);
pEpLog(id2); pEpLog(id2.c_data());
return 0; return 0;
} }

247
test/test_nr1.cc

@ -1,247 +0,0 @@
#include <iostream>
#include <cctype>
#include <pEp/pEpEngine.h>
#include <pEp/message_api.h>
#include <pEp/keymanagement.h>
#include <pEp/identity_list.h>
#include <utility>
#include <pEp/utils.hh>
#include <pEp/pEpLog.hh>
#include <pEp/inspect.hh>
#include <type_traits>
//#include "../examples/libc99/libc99.h"
#include "../src/prototype.hh"
void test_getters(int* c_int_p, pEp::POD<int>& 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<int>& 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<int>::value && !std::is_pointer<int>::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<int> 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<int> pint1(&c_int1);
pEp::POD<int> 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");
}
}
}

414
test/test_prototype.cc

@ -0,0 +1,414 @@
#include <iostream>
#include <cctype>
#include <pEp/pEpEngine.h>
#include <pEp/message_api.h>
#include <pEp/keymanagement.h>
#include <pEp/identity_list.h>
#include <utility>
#include <pEp/utils.hh>
#include <pEp/pEpLog.hh>
#include <pEp/inspect.hh>
#include <type_traits>
//#include "../examples/libc99/libc99.h"
#include "../src/prototype.hh"
void test_getters(int* c_int_p, pEp::POD<int>& 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<int>& 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<int>::value && !std::is_pointer<int>::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<int> 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<int> pint1(&c_int1);
pEp::POD<int> 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<int> 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<int> 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<int> 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<int> 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<int> 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<int> 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;
}

593
test/test_sleeve.cc

@ -0,0 +1,593 @@
#include <iostream>
#include <utility>
#include <pEp/pEpLog.hh>
#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<typename A, typename B>
//int TEST_IS_COPY(const A& a, const B& b)
//{
// pEpLog("Test is copy");
// assert(b == a);
// assert(!b.is(a));
// return 0;
//}
//
//template<typename A, typename B>
//int TEST_IS_REF(const A& a, const B& b)
//{
// pEpLog("Test is ref");
// assert(b == a);
// assert(b.is(a));
// return 0;
//}
//template<typename T, typename Enabled = void>
//class TestSleeve;
// -------------------------------------------------------------------------------------------------
// FUNDAMENTAL
// -------------------------------------------------------------------------------------------------
template<typename T>
class PODFactory {
public:
using FactoryFunc = const std::function<T*(const T&)>;
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<typename T>
class TestSleeve;
// VALUE -------------------------------------------------------------------------------------------------
template<typename T>
class TestSleeve {
public:
TestSleeve(PODFactory<T> init1, PODFactory<T> 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<T> b{ a };
pEpLog(b.to_string());
TEST_IS_COPY(a, b);
}
{
pEpLogH2("Test assign HS");
T a1 = factory1;
POTS::Sleeve<T> b1{ a1 };
pEpLog(b1.to_string());
T a2 = factory2;
POTS::Sleeve<T> 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<T> b1{ a1 };
pEpLog(b1.to_string());
T a2 = factory2;
;
POTS::Sleeve<T> 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<T> b1{ a };
pEpLog(b1.to_string());
POTS::Sleeve<T> 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<T> b1{ [&a1]() { return &a1; } };
pEpLog(b1.to_string());
TEST_IS_REF(a1, b1);
}
{
pEpLogH2("Test assign HS");
T a1 = factory1;
POTS::Sleeve<T> b1{ [&a1]() { return &a1; } };
pEpLog(b1.to_string());
T a2 = factory2;
;
POTS::Sleeve<T> 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<T> b1{ [&a1]() { return &a1; } };
pEpLog(b1.to_string());
T a2 = factory2;
;
POTS::Sleeve<T> 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<T> b1{ [&a1]() { return &a1; } };
pEpLog(b1);
pEpLog(b1.to_string());
POTS::Sleeve<T> b2{ b1 };
pEpLog(b2.to_string());
TEST_IS_COPY(b1, b2);
}
return 0;
}
private:
PODFactory<T> factory1;
PODFactory<T> factory2;
};
// POINTER -------------------------------------------------------------------------------------------------
template<typename T>
class TestSleeve<T*> {
public:
TestSleeve(PODFactory<T> init1, PODFactory<T> 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<T*> b1{};
pEpLog(b1.to_string());
}
{
pEpLogH2("Test assign RAW-T (value)");
POTS::Sleeve<T*> 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<T*> b1{};
pEpLog(b1.to_string());
POTS::Sleeve<T*> b2{};
pEpLog(b2.to_string());
b1 = b2;
pEpLog(b1.to_string());
TEST_IS_REF(b1, b2);
}
{
pEpLogH2("Test assign BS");
POTS::Sleeve<T*> b1{};
pEpLog(b1.to_string());
T* a2 = factory1;
POTS::Sleeve<T*> 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<T*> b1{};
pEpLog(b1.to_string());
POTS::Sleeve<T*> 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<T*> b1{ [&a1]() { return &a1; } };
pEpLog(b1.to_string());
TEST_IS_REF(a1, b1);
}
{
pEpLogH2("Test assign RAW-T (value)");
T* a1 = factory1;
POTS::Sleeve<T*> 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<T*> b1{ [&a1]() { return &a1; } };
pEpLog(b1.to_string());
POTS::Sleeve<T*> 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<T*> b1{ [&a1]() { return &a1; } };
pEpLog(b1.to_string());
T* a2 = factory2;
POTS::Sleeve<T*> 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<T*> b1{ [&a1]() { return &a1; } };
pEpLog(b1.to_string());
POTS::Sleeve<T*> b2{ b1 };
pEpLog(b2.to_string());
TEST_IS_REF(b1, b2);
}
return 0;
}
private:
PODFactory<T> factory1;
PODFactory<T> factory2;
};
// STRUCT P -------------------------------------------------------------------------------------------------
template<>
//class TestSleeve<T*, typename std::enable_if<std::is_fundamental<T>::value || std::is_enum<T>::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<typename T>
int test_sleeve(PODFactory<T> init1, PODFactory<T> init2)
{
// TestSleeve<T>{ init1, init2 };
TestSleeve<T*>{ init1, init2 };
return 0;
}
int main()
{
// Utils::readKey();
pEp::Adapter::pEpLog::set_enabled(true);
test_sleeve<int>(PODFactory<int>(23), PODFactory<int>(42));
// test_sleeve<char>(PODFactory<char>('a'), PODFactory<char>('b'));
// test_sleeve<float>(PODFactory<float>(2.3f), PODFactory<float>(4.2f));
// test_sleeve<bool>(PODFactory<bool>(true), PODFactory<bool>(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<int>(23);
::C99_C b{};
b.i = 2;
b.pi = PODFactory<int>(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<int>(val.i);
a->pi = PODFactory<int>(*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<int>(val.i);
a->pi = PODFactory<int>(*val.pi);
return a;
},
b));
/* {
::C99_C c{};
c.i = 1;
c.pi = PODFactory<int>(23);
::C99_B b{};
b.c = PODFactory<::C99_C>(c);
::C99_A a{};
a.b = PODFactory<::C99_B>(b);
}
*/
return 0;
}

237
test/test_struct_cmp.cc

@ -0,0 +1,237 @@
#include <map>
#include <pEp/pEpLog.hh>
#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<std::string, ABase&>(name, a));
}
virtual bool cmp(ABase& rhs)
{
for (std::pair<std::string, ABase&> 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<std::string, ABase&> _members{};
};
template<typename T>
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<T>::value) {
try {
A<T>& rhs_c = dynamic_cast<A<T>&>(rhs);
if (*_obj == *rhs_c._obj) {
return true;
} else {
return false;
}
} catch (...) {
try {
A<T*>& rhs_c = dynamic_cast<A<T*>&>(rhs);
if (*_obj == **rhs_c._obj) {
return true;
} else {
return false;
}
} catch (...) {
return false;
}
}
} else {
return ABase::cmp(rhs);
}
}
// T _value;
T* _obj;
};
template<typename T>
class A<T*> : 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<T>::value) {
try {
A<T*>& rhs_c = dynamic_cast<A<T*>&>(rhs);
if (**_obj == **rhs_c._obj) {
return true;
} else {
return false;
}
} catch (...) {
try {
A<T>& rhs_c = dynamic_cast<A<T>&>(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<typename T>
class B : public A<T> {
public:
B() : A<T>()
{
pEpLog("T");
}
int _T_Bool_ext;
};
*/
//template<typename T>
//class A<T*, typename std::enable_if<std::is_same<T, bool>::value>::type> : public A<T*, T*> {
//public:
// A()
// {
// pEpLog("T* bool");
// }
// int _Tp_Bool_ext;
//};
//template<>
//class A<char*> {
//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<int> i{};
// A<int*> ip{};
// B<int> bi{};
// B<int*> bip{};
// A<bool> b{};
// A<bool*> bp{};
{
float f = 3.0f;
int i = 23;
int* pi = &i;
NA::A<int> a{ &i };
NA::A<float> b{ &f };
NA::A<int*> ap{ &pi };
assert(a != b);
assert(a == ap);
}
{
Struct a{};
a.a = 23;
a.b = 42;
NA::A<Struct> b1{ &a };
NA::A<int> b1_a{ "a", b1, &a.a };
NA::A<int> b1_b{ "b", b1, &a.b };
NA::A<Struct> b2{ &a };
NA::A<int> b2_a{ "a", b2, &a.a };
NA::A<int> b2_b{ "b", b2, &a.b };
assert(b1 == b2);
Struct a2{};
a2.a = 23;
a2.b = 42;
NA::A<Struct> b3{ &a2 };
NA::A<int> b3_a{ "a", b3, &a2.a };
NA::A<int> b3_b{ "b", b3, &a2.b };
assert(b2 == b3);
}
}
Loading…
Cancel
Save