Browse Source

protyping: Structs wrapper / ownership / more tests

heck-rework
heck 3 years ago
parent
commit
1df9c84511
  1. 561
      test/test_nr1.cc

561
test/test_nr1.cc

@ -46,18 +46,47 @@ namespace pEp {
template<class T>
class POD {
static_assert(std::is_pod<T>::value && !std::is_pointer<T>::value, "not an integral");
static_assert(
std::is_pod<T>::value && !std::is_pointer<T>::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<T>::log_enabled{ true };
template<class T>
std::ostream& operator<<(std::ostream& o, pEp::POD<T> pEpEnum)
std::ostream& operator<<(std::ostream& o, pEp::POD<T> 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<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);
::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<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);
}
}
::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<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);
}
}
// 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<int> c_int{};
pEp::POD<Test_enum> 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<Test_enum>::value && !std::is_pointer<Test_enum>::value,
"not an integral");
static_assert(
std::is_pod<Test_struct>::value && !std::is_pointer<Test_struct>::value,
std::is_pod<Test_struct1>::value && !std::is_pointer<Test_struct1>::value,
"not an integral");
// static_assert(std::is_pod<char*>::value && !std::is_pointer<char*>::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<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) {
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 };
//

Loading…
Cancel
Save