#include #include #include #include #include #include #include #include #include #include //using namespace pEp; namespace pEp { // type ---------------------------------------- // same for pointer and value typee template std::string type_str(const T) { std::stringstream ss_type; ss_type << typeid(T).name(); return ss_type.str(); } // addr ---------------------------------------- template struct addr_str_helper { static std::string str(const T c) { std::stringstream ss_addr{}; ss_addr << static_cast(&c); return ss_addr.str(); } }; template struct addr_str_helper { static std::string str(const T* c) { std::stringstream ss_addr{}; ss_addr << static_cast(c); return ss_addr.str(); } }; // rertuns the address of c template std::string addr_str(T c) { return addr_str_helper::str(c); } // val ---------------------------------------- template struct val_str_helper { static std::string str(T c, size_t val_len = 30) { //value std::stringstream ss_val{}; ss_val << c; return pEp::Utils::clip(ss_val.str(), val_len); } }; template struct val_str_helper { static std::string str(T* c, size_t val_len = 30) { //value std::stringstream ss_val{}; if (c != nullptr) { ss_val << &c; } else { ss_val << ""; } return pEp::Utils::clip(ss_val.str(), val_len); } }; // returns the value of c (if T is pointer type, the address p is pointing to) template std::string val_str(T c, size_t val_len = 30) { return val_str_helper::str(c, val_len); } // type_addr_val ---------------------------------------- template struct type_addr_val_helper { static std::string str(T c, size_t val_len = 30) { std::stringstream ret{}; ret << "{ " << type_str(c) << " | " << addr_str(c) << " | " << val_str(c, val_len) << " }"; return ret.str(); } }; template struct type_addr_val_helper { static std::string str(T* c, size_t val_len = 30) { std::stringstream ret{}; ret << "{ " << type_str(c) << " | " << addr_str(c) << " | " << val_str(c, val_len) << " }"; return ret.str(); } }; template std::string type_addr_val(T c, size_t val_len = 30) { return type_addr_val_helper::str(c, val_len); } } // namespace pEp namespace pEp { char* alloc(const std::string& str) { char* ret = strdup(str.c_str()); pEpLog(type_addr_val(ret)); return ret; } template void free(T ptr_type) { pEpLog(type_addr_val(ptr_type)); ::pEp_free(ptr_type); } template<> void free(char* ptr_type) { pEpLog(type_addr_val(ptr_type)); ::pEp_free(ptr_type); } template<> void free(::pEp_identity* ptr_type) { pEpLog(type_addr_val(ptr_type)); ::pEp_free(ptr_type); } //--------------------------------------------------------------------------------------------- #define EXSTR(msg) std::string(__FUNCTION__) + " - " + msg class PODBase { public: static bool log_enabled; protected: bool _is_initialized{ false }; Adapter::pEpLog::pEpLogger logger{ "pEp::Enum", log_enabled }; Adapter::pEpLog::pEpLogger& m4gic_logger_n4me = logger; }; template class POD : public PODBase { static_assert(std::is_pod::value, "only POD types are allowed"); public: POD() = delete; explicit POD(T* pod_p) { if (pod_p == nullptr) { throw Exception{ EXSTR("cant initialize on a nullptr") }; } // init _pod_p = pod_p; pEpLogClass(to_string() + " - taking ownership"); } // make a copy POD& operator=(T value) { pEpLogClass("Before: " + to_string() + " - new val: '" + val_str(value) + "'"); *_pod_p = value; pEpLogClass("After: " + to_string()); return *this; } // return a copy operator T() const { return *_pod_p; } // return address of the wrappee T* data() { return _pod_p; } // return address of the wrappee (const) const T* c_data() const { return _pod_p; } std::string to_string() const { std::string ret{ "[" + type_addr_val(_pod_p) + "]" }; return ret; } private: bool _is_initialized{ false }; T* _pod_p{ nullptr }; class Exception : public std::runtime_error { public: explicit Exception(const std::string& msg) : std::runtime_error(msg) {} }; }; bool pEp::PODBase::log_enabled{ true }; template std::ostream& operator<<(std::ostream& o, pEp::POD pEpEnum) { return o << (int)pEpEnum; } //--------------------------------------------------------------------------------------------- // Manages a char* to appear like a std::string // char* c_str // c_str MUST point to either: // * NULL - means there is no value/mem alloc yet // * dyn allocated memory // in case the mem is already allocated, there must be 2 modes // * take ownership (and free it) // * 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 // set(). Otherwise all(most) functions will throw. // set() can only be called once, will throw otherwise. 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 explicit String(char** c_str_pp) { initialize(c_str_pp); } ~String() { _free(); } void initialize(char** c_str_pp) { if (c_str_pp == nullptr) { throw Exception{ EXSTR("cant initialize on a nullptr") }; } if (_is_initialized) { throw Exception{ EXSTR("double initialization") }; } // init _c_str_pp = c_str_pp; _c_str_p = *_c_str_pp; _is_initialized = true; pEpLogClass(to_string() + " - taking ownership"); } // make a copy String& operator=(const std::string& str) { pEpLogClass("Before: " + to_string() + " - new val: '" + pEp::Utils::clip(str, 30) + "'"); if (_c_str_pp == nullptr) { throw Exception{ EXSTR("invalid state") }; } // DEALLOCATION _free(); // ALLOCATION if (str.empty()) { // if the new value is empty str, lets point to nothing *_c_str_pp = nullptr; _c_str_p = *_c_str_pp; } else { *_c_str_pp = pEp::alloc(str); _c_str_p = *_c_str_pp; } pEpLogClass("After: " + to_string()); return *this; } // 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) { throw Exception{ EXSTR("invalid state") }; } if (*_c_str_pp != nullptr) { return { *_c_str_pp }; } return {}; } char** data() { return _c_str_pp; } const char* c_data() const { if (_c_str_pp == nullptr) { throw Exception{ EXSTR("invalid state") }; } return *_c_str_pp; } std::string to_string() const { if (_c_str_pp == nullptr) { throw Exception{ EXSTR("invalid state") }; } std::string ret{ "[" + type_addr_val(_c_str_pp) + " / " + type_addr_val(*_c_str_pp) + "]" }; return ret; } static bool log_enabled; private: void _free() { // 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"); pEp::free(*_c_str_pp); } else { // we anyways need to free the one we created // NO: WE ASSUME THAT: // if the char* has been replaced, it has been freed, as well if (_c_str_p != nullptr) { pEp::free(_c_str_p); } } } bool _is_initialized{ false }; char** _c_str_pp{ nullptr }; char* _c_str_p{ nullptr }; Adapter::pEpLog::pEpLogger logger{ "pEp::String", 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 pEp::String::log_enabled{ true }; std::ostream& operator<<(std::ostream& o, const pEp::String& pEpStr) { 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.initialize(&_wrappee->address); this->username.initialize(&_wrappee->username); this->user_id.initialize(&_wrappee->user_id); this->fpr.initialize(&_wrappee->fpr); // set the values this->address = address; this->username = username; this->user_id = user_id; this->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 }; } // namespace pEp ::PEP_SESSION session; void test_getters(char const* const* const c_str_p, pEp::String& pstr) { pEpLog("to_string(): " + pstr.to_string()); assert(pstr.c_data() == *c_str_p); assert(pstr.data() == c_str_p); if (*c_str_p != nullptr) { std::string tmp{ pstr }; pEpLog("operator std::string(): " + tmp); assert(tmp == std::string(*c_str_p)); // will segfault with nullptr, and this is correct std::string tmp2{ *pstr.data() }; pEpLog("data(): " + tmp2); assert(tmp2 == std::string(*c_str_p)); std::string tmp3{ pstr.c_data() }; pEpLog("c_data(): " + tmp2); assert(tmp3 == std::string(*c_str_p)); } else { std::string tmp{ pstr }; pEpLog("operator std::string(): " + tmp); assert(tmp.empty()); } } void test_assign_and_getters(char** c_str_p, pEp::String& pstr) { test_getters(c_str_p, pstr); pEpLogH2("assign operator"); pstr = "assign operator"; test_getters(c_str_p, pstr); pEpLogH2("raw c_str assign"); *c_str_p = strdup("raw c_str assign"); test_getters(c_str_p, pstr); } extern "C" { typedef enum { SOBER, WHIPPY, YIPPIE, HOORAY } Drunk_level; typedef struct { char* name; Drunk_level drk; int btc_fanlevel; } SchlossInsasse; } int main() { // pEp::Utils::readKey(); pEp::Adapter::pEpLog::set_enabled(true); SchlossInsasse tschappy; tschappy.name = strdup("tschappy"); tschappy.drk = YIPPIE; tschappy.btc_fanlevel = 100; pEp::POD<::Drunk_level> penum(&tschappy.drk); pEpLog(tschappy.drk); pEpLog(penum); penum = HOORAY; pEpLog(tschappy.drk); pEpLog(penum); pEp::POD pint(&tschappy.btc_fanlevel); pEpLog(tschappy.btc_fanlevel); pEpLog(pint); pint = 33; pEpLog(tschappy.btc_fanlevel); pEpLog(pint); // pEp::String::log_enabled = false; if (0) { // 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(&c_str); test_assign_and_getters(&c_str, pstr); } // new pEp::String on already initalized char* { pEpLogH1("new pEp::String on already initalized char*"); char* c_str = strdup("initialized c string"); pEp::String pstr(&c_str); test_assign_and_getters(&c_str, pstr); } // initialize() { pEpLogH1("initialize()"); pEp::String pstr{}; char* c_str = strdup("initialized c string"); //TODO: PITYASSERT_THROWS pstr.initialize(&c_str); test_assign_and_getters(&c_str, pstr); } } 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); pEp::Identity id2{ "bob" }; ::update_identity(session, id2); pEpLog(*id2); } }