diff --git a/test/test_nr1.cc b/test/test_nr1.cc index 03f323f..972f211 100644 --- a/test/test_nr1.cc +++ b/test/test_nr1.cc @@ -46,18 +46,47 @@ namespace pEp { template class POD { - static_assert(std::is_pod::value && !std::is_pointer::value, "not an integral"); + static_assert( + std::is_pod::value && !std::is_pointer::value, + "only POD value types supported"); public: - POD() = delete; + POD() + { + pEpLogClass("called"); + }; + explicit POD(T* pod_p, const T& init_val) + { + bind(pod_p, init_val); + } + + // Best to use this constructor, as the object is in a valid state when the wrapped + // c_str (char*) is known explicit POD(T* pod_p) + { + bind(pod_p); + } + + void bind(T* pod_p, const T& init_val) + { + bind(pod_p); + this->operator=(init_val); + } + + void bind(T* pod_p) { if (pod_p == nullptr) { - throw Exception{ EXSTR("cant init on a nullptr") }; + throw Exception{ EXSTR("cant bind on a nullptr") }; + } + + if (_is_initialized) { + throw Exception{ EXSTR("double initialization") }; } + // init _pod_p = pod_p; - pEpLogClass(to_string() + " - taking ownership"); + _is_initialized = true; + pEpLogClass(to_string()); } // make a copy @@ -65,7 +94,7 @@ namespace pEp { { pEpLogClass("Before: " + to_string() + " - new val: '" + CXX::Inspect::val(value) + "'"); *_pod_p = value; - pEpLogClass("After: " + to_string()); + // pEpLogClass("After: " + to_string()); return *this; } @@ -96,6 +125,7 @@ namespace pEp { static bool log_enabled; private: + bool _is_initialized{ false }; T* _pod_p{ nullptr }; Adapter::pEpLog::pEpLogger logger{ "pEp::POD", log_enabled }; @@ -111,9 +141,9 @@ namespace pEp { bool pEp::POD::log_enabled{ true }; template - std::ostream& operator<<(std::ostream& o, pEp::POD pEpEnum) + std::ostream& operator<<(std::ostream& o, pEp::POD pEpPOD) { - return o << (int)pEpEnum; + return o << (T)pEpPOD; } //--------------------------------------------------------------------------------------------- @@ -127,55 +157,59 @@ namespace pEp { // * dont take ownership, only provide a C++ interface // There must be the option to construct the object before the wrapped c_str (char*) // is known. This leads to a pEp::String object in invalid state, and it must be initialized using - // init(). Otherwise all(most) functions will throw. - // init() can only be called once, will throw otherwise. + // bind(). Otherwise all(most) functions will throw. + // bind() can only be called once, will throw otherwise. // NON-owning mode, just does not free the string when it dies, owning mode does. // Thats the only difference. class String { public: - String() = default; - - // Best to use this constructor, as the object is in a valid state when the wrapped - // c_str (char*) is known - String(bool take_ownership, char** c_str_pp) + String() { - init(take_ownership, c_str_pp); + pEpLogClass("called"); } - explicit String(bool take_ownership, char** c_str_pp, const std::string& init_val) + // 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) { - init(take_ownership, c_str_pp, init_val); + bind(is_owner, c_str_pp); } - ~String() + String(bool is_owner, char** c_str_pp, const std::string& init_val) { - if (_is_owning_mode) { - _free(); - } + bind(is_owner, c_str_pp, init_val); } - void init(bool take_ownership, char** c_str_pp, const std::string& init_val) + void bind(bool is_owner, char** c_str_pp, const std::string& init_val) { - init(take_ownership, c_str_pp); + bind(is_owner, c_str_pp); this->operator=(init_val); } - void init(bool take_ownership, char** c_str_pp) + // bind and set ownership + void bind(bool is_owner, char** c_str_pp) { if (c_str_pp == nullptr) { - throw Exception{ EXSTR("cant init on a nullptr") }; + throw Exception{ EXSTR("cant bind on a nullptr") }; } - if (_is_initialized) { + if (_is_bound) { throw Exception{ EXSTR("double initialization") }; } // init _c_str_pp = c_str_pp; _c_str_p = *_c_str_pp; - _is_initialized = true; - _is_owning_mode = take_ownership; - pEpLogClass(to_string() + " - init " + (take_ownership ? "/taking ownership" : "")); + _is_bound = true; + pEpLogClass(to_string()); + set_owner(is_owner); + } + + ~String() + { + if (_is_owner) { + _free(); + } } // make a copy @@ -228,8 +262,19 @@ namespace pEp { return *_c_str_pp; } + bool operator==(const pEp::String& pstr) + { + return *(pstr.c_data()) == (*c_data()); + } + + bool operator!=(const pEp::String& pstr) + { + return !(*this == pstr); + } + std::string to_string() const { + if (_c_str_pp == nullptr) { throw Exception{ EXSTR("invalid state") }; } @@ -239,16 +284,28 @@ namespace pEp { return ret; } + void set_owner(bool is_owner) + { + pEpLogClass(std::to_string(is_owner)); + _is_owner = is_owner; + } + + bool is_owner() const + { + return _is_owner; + } + static bool log_enabled; private: // TODO: this is dodgy, do we really need _c_str_pp AND _c_str_p??? void _free() { + pEpLogClass("called"); // if the c_str points to a different address now, than the one we created if (*_c_str_pp != _c_str_p) { // a new string has been created, and we need to free it as well - pEpLog("raw access string change detected"); + pEpLogClass("raw access string change detected"); pEp::free(*_c_str_pp); } else { // WE ASSUME THAT: @@ -259,8 +316,8 @@ namespace pEp { } } - bool _is_initialized{ false }; - bool _is_owning_mode{ false }; + bool _is_bound{ false }; + bool _is_owner{ false }; char** _c_str_pp{ nullptr }; char* _c_str_p{ nullptr }; @@ -280,63 +337,49 @@ namespace pEp { return o << std::string(pEpStr); } - //--------------------------------------------------------------------------------------------- - - // TOOD: - // ctor not exception safe - class Identity { - public: - Identity( - const std::string& address = "", - const std::string& username = "", - const std::string& user_id = "", - const std::string& fpr = "") - { - pEpLogClass("called"); - _wrappee = ::new_identity(nullptr, nullptr, nullptr, nullptr); - - // set the pEp::String wrapper underlying c_str - this->address.init(true, &_wrappee->address, address); - this->username.init(true, &_wrappee->username, username); - this->user_id.init(true, &_wrappee->user_id, user_id); - this->fpr.init(true, &_wrappee->fpr, fpr); - } - - ~Identity() - { - _free(); - } - - - pEp::String address{}; - pEp::String username{}; - pEp::String user_id{}; - pEp::String fpr{}; - - operator ::pEp_identity*() - { - return _wrappee; - } - - static bool log_enabled; +} // namespace pEp - private: - void _free() - { - // pEp::free(_wrappee); - } +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); - ::pEp_identity* _wrappee{ nullptr }; - Adapter::pEpLog::pEpLogger logger{ "IdentWrappySP", log_enabled }; - Adapter::pEpLog::pEpLogger& m4gic_logger_n4me = logger; - }; + // compare values + if (c_int_p != nullptr) { + pEpLog("operator int(): " + std::to_string(pint)); + assert(pint == *c_int_p); + assert(pint == expected); - bool Identity::log_enabled{ true }; + // 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); -} // namespace pEp + 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); + } +} -::PEP_SESSION session; void test_getters(char** c_str_p, pEp::String& pstr, const std::string& expected) { @@ -384,48 +427,6 @@ void test_assign(char** c_str_p, pEp::String& pstr) } } -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); - } -} - - // We are testing wrappers for the c-datatypes: // * integral e.g (int) here // * enum @@ -444,9 +445,158 @@ extern "C" { int c_int; Test_enum c_enum; char* c_str; - } Test_struct; + } Test_struct1; + + typedef struct { + int c_int; + Test_enum c_enum; + char* c_str; + Test_struct1* c_struct1; + } Test_struct2; + + void free_test_struct(Test_struct1* c_struct) + { + if (c_struct) { + free(c_struct->c_str); + free(c_struct); + } + } + + Test_struct1* new_test_struct(int c_int, Test_enum c_enum, const char* c_str) + { + Test_struct1* result = (Test_struct1*)calloc(1, sizeof(Test_struct1)); + if (result) { + result->c_int = c_int; + result->c_enum = c_enum; + if (c_str) { + result->c_str = strdup(c_str); + if (result->c_str == NULL) { + free_test_struct(result); + return NULL; + } + } + } + return result; + } } +namespace pEp { + // TOOD: + // ctor not exception safe + class TestStruct1 { + public: + // Create a new instance + TestStruct1(int i, Test_enum e, const std::string& str) + { + pEpLogClass("called"); + _c_struct_p = ::new_test_struct(0, ONE, nullptr); //dont use raw-setters + bind(true, &_c_struct_p); + // need to use these setter because of value conversions + this->c_int = i; + this->c_enum = e; + this->c_str = str; + } + + // Wrap an existing instance + TestStruct1() + { + pEpLogClass("called"); + } + + // Best to use this constructor, as the object is in a valid state when the wrapped + // c_struct_pp is known + TestStruct1(bool is_owner, Test_struct1** c_struct_pp) + { + pEpLogClass("called"); + bind(is_owner, c_struct_pp); + } + + void bind(bool is_owner, Test_struct1** c_struct_pp) + { + if (c_struct_pp == nullptr) { + throw Exception{ EXSTR("cant bind on a nullptr") }; + } + + if (_is_bound) { + throw Exception{ EXSTR("double initialization") }; + } + + // init + _c_struct_pp = c_struct_pp; + _c_struct_p = *_c_struct_pp; + _is_bound = true; + pEpLogClass(to_string()); + _bind_members(); + set_owner(is_owner); + } + + ~TestStruct1() + { + if (_is_owner) { + _free(); + } + } + + operator ::Test_struct1*() + { + return _c_struct_p; + } + + std::string to_string() const + { + std::string ret{ "[" + CXX::Inspect::all(_c_struct_p) + "]" }; + return ret; + } + + void set_owner(bool is_owner) + { + pEpLogClass(std::to_string(is_owner)); + _is_owner = is_owner; + } + + bool is_owner() const + { + return _is_owner; + } + + pEp::POD c_int{}; + pEp::POD c_enum{}; + pEp::String c_str{}; + + static bool log_enabled; + + private: + void _bind_members() + { + this->c_int.bind(&_c_struct_p->c_int); + this->c_enum.bind(&_c_struct_p->c_enum); + this->c_str.bind(false, &_c_struct_p->c_str); + } + + void _free() + { + pEpLogClass("called"); + ::free_test_struct(_c_struct_p); + } + bool _is_bound{ false }; + bool _is_owner{ false }; + ::Test_struct1** _c_struct_pp{ nullptr }; + ::Test_struct1* _c_struct_p{ nullptr }; + + Adapter::pEpLog::pEpLogger logger{ "TestStruct", log_enabled }; + Adapter::pEpLog::pEpLogger& m4gic_logger_n4me = logger; + + class Exception : public std::runtime_error { + public: + explicit Exception(const std::string& msg) : std::runtime_error(msg) {} + }; + }; + + + bool TestStruct1::log_enabled{ true }; +} // namespace pEp + + int main() { // c-types are always POD @@ -458,16 +608,14 @@ int main() std::is_pod::value && !std::is_pointer::value, "not an integral"); static_assert( - std::is_pod::value && !std::is_pointer::value, + std::is_pod::value && !std::is_pointer::value, "not an integral"); - // static_assert(std::is_pod::value && !std::is_pointer::value, "not an integral"); - // pEp::Utils::readKey(); pEp::Adapter::pEpLog::set_enabled(true); // POD - if (1) { + if (0) { // VALID USAGE int init_val = 0; // new pEp::POD on int @@ -478,11 +626,23 @@ int main() 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) { + if (1) { //TODO: Test non-owning mode // INVALID USAGE @@ -521,37 +681,136 @@ int main() test_assign(&c_str, pstr); } - // initialize() + // bind() { - pEpLogH1("init()"); + pEpLogH1("bind()"); pEp::String pstr{}; char* c_str = strdup(init_val.c_str()); //TODO: PITYASSERT_THROWS - pstr.init(true, &c_str); + 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); + } } if (0) { - setenv("HOME", ".", 1); - ::init(&session, nullptr, nullptr, nullptr); - - // create identity - pEp::Identity id1{ "wrong@entry.lol", "wrong", "23", "INVA_FPR" }; - - pEpLog(id1); - id1.username = "alice"; - id1.address = "alice@peptest.org"; - - pEpLog(id1.address); - pEpLog(id1.username); - ::myself(session, id1); - pEpLog(id1); + // create + { + pEp::TestStruct1 pstruct1(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"); + } - pEp::Identity id2{ "bob" }; - ::update_identity(session, id2); - pEpLog(id2); + // wrap + { + 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); + } } + + // if (0) { + // ::PEP_SESSION session; + // setenv("HOME", ".", 1); + // ::init(&session, nullptr, nullptr, nullptr); + // + // // create identity + // pEp::Identity id1{ "wrong@entry.lol", "wrong", "23", "INVA_FPR" }; + // + // pEpLog(id1); + // id1.username = "alice"; + // id1.address = "alice@peptest.org"; + // + // pEpLog(id1.address); + // pEpLog(id1.username); + // ::myself(session, id1); + // pEpLog(id1); + // + // pEp::Identity id2{ "bob" }; + // ::update_identity(session, id2); + // pEpLog(id2); + // } } + +//--------------------------------------------------------------------------------------------- +// +//// TOOD: +//// ctor not exception safe +//class Identity { +//public: +// Identity( +// const std::string& address = "", +// const std::string& username = "", +// const std::string& user_id = "", +// const std::string& fpr = "") +// { +// pEpLogClass("called"); +// _wrappee = ::new_identity(nullptr, nullptr, nullptr, nullptr); +// +// // set the pEp::String wrapper underlying c_str +// this->address.bind(true, &_wrappee->address, address); +// this->username.bind(true, &_wrappee->username, username); +// this->user_id.bind(true, &_wrappee->user_id, user_id); +// this->fpr.bind(true, &_wrappee->fpr, fpr); +// } +// +// ~Identity() +// { +// _free(); +// } +// +// +// pEp::String address{}; +// pEp::String username{}; +// pEp::String user_id{}; +// pEp::String fpr{}; +// +// operator ::pEp_identity*() +// { +// return _wrappee; +// } +// +// static bool log_enabled; +// +//private: +// void _free() +// { +// // pEp::free(_wrappee); +// } +// +// ::pEp_identity* _wrappee{ nullptr }; +// Adapter::pEpLog::pEpLogger logger{ "IdentWrappySP", log_enabled }; +// Adapter::pEpLog::pEpLogger& m4gic_logger_n4me = logger; +//}; +// +//bool Identity::log_enabled{ true }; +//