
2 changed files with 472 additions and 536 deletions
@ -0,0 +1,468 @@ |
|||||
|
#include <iostream> |
||||
|
#include <cctype> |
||||
|
#include <utility> |
||||
|
#include <pEp/utils.hh> |
||||
|
#include <pEp/pEpLog.hh> |
||||
|
#include <pEp/inspect.hh> |
||||
|
#include <type_traits> |
||||
|
#include "../examples/libc99/libc99.h" |
||||
|
|
||||
|
|
||||
|
namespace pEp { |
||||
|
|
||||
|
|
||||
|
//---------------------------------------------------------------------------------------------
|
||||
|
#define EXSTR(msg) std::string(__FUNCTION__) + "() - " + msg |
||||
|
|
||||
|
template<class T> |
||||
|
class POD { |
||||
|
static_assert( |
||||
|
std::is_pod<T>::value && !std::is_pointer<T>::value, |
||||
|
"only POD value types supported"); |
||||
|
|
||||
|
public: |
||||
|
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 bind on a nullptr") }; |
||||
|
} |
||||
|
|
||||
|
if (_is_initialized) { |
||||
|
throw Exception{ EXSTR("can only bind once") }; |
||||
|
} |
||||
|
// init
|
||||
|
_pod_p = pod_p; |
||||
|
_is_initialized = true; |
||||
|
pEpLogClass(to_string()); |
||||
|
} |
||||
|
|
||||
|
// make a copy
|
||||
|
POD& operator=(T value) |
||||
|
{ |
||||
|
pEpLogClass("Before: " + to_string() + " - new val: '" + CXX::Inspect::val(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{ "[" + CXX::Inspect::all(_pod_p) + "]" }; |
||||
|
return ret; |
||||
|
} |
||||
|
|
||||
|
static bool log_enabled; |
||||
|
|
||||
|
private: |
||||
|
bool _is_initialized{ false }; |
||||
|
T* _pod_p{ nullptr }; |
||||
|
|
||||
|
Adapter::pEpLog::pEpLogger logger{ "pEp::POD", 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) {} |
||||
|
}; |
||||
|
}; |
||||
|
|
||||
|
template<class T> |
||||
|
bool pEp::POD<T>::log_enabled{ false }; |
||||
|
|
||||
|
template<class T> |
||||
|
std::ostream& operator<<(std::ostream& o, pEp::POD<T> pEpPOD) |
||||
|
{ |
||||
|
return o << (T)pEpPOD; |
||||
|
} |
||||
|
|
||||
|
//---------------------------------------------------------------------------------------------
|
||||
|
// 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
|
||||
|
// 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() |
||||
|
{ |
||||
|
pEpLogClass("called"); |
||||
|
} |
||||
|
|
||||
|
// Best to use this constructor, as the object is in a valid state when the wrapped
|
||||
|
// c_str (char*) is known
|
||||
|
String(bool is_owner, char** c_str_pp) |
||||
|
{ |
||||
|
bind(is_owner, c_str_pp); |
||||
|
} |
||||
|
|
||||
|
String(bool is_owner, char** c_str_pp, const std::string& init_val) |
||||
|
{ |
||||
|
bind(is_owner, c_str_pp, init_val); |
||||
|
} |
||||
|
|
||||
|
void bind(bool is_owner, char** c_str_pp, const std::string& init_val) |
||||
|
{ |
||||
|
bind(is_owner, c_str_pp); |
||||
|
this->operator=(init_val); |
||||
|
} |
||||
|
|
||||
|
// bind and set ownership
|
||||
|
void bind(bool is_owner, char** c_str_pp) |
||||
|
{ |
||||
|
if (c_str_pp == nullptr) { |
||||
|
throw Exception{ EXSTR("cant bind on a nullptr") }; |
||||
|
} |
||||
|
|
||||
|
if (_is_bound) { |
||||
|
throw Exception{ EXSTR("can only be bound once") }; |
||||
|
} |
||||
|
|
||||
|
// init
|
||||
|
_c_str_pp = c_str_pp; |
||||
|
_is_bound = true; |
||||
|
pEpLogClass(to_string()); |
||||
|
set_owner(is_owner); |
||||
|
|
||||
|
//if the c_str_p is nullptr then init with ""
|
||||
|
if (*_c_str_pp == nullptr) { |
||||
|
this->operator=(""); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
~String() |
||||
|
{ |
||||
|
if (_is_owner) { |
||||
|
_free(*_c_str_pp); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 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(*_c_str_pp); |
||||
|
|
||||
|
// ALLOCATION
|
||||
|
*_c_str_pp = _copy(str); |
||||
|
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; |
||||
|
} |
||||
|
|
||||
|
bool operator==(const pEp::String& pstr) const |
||||
|
{ |
||||
|
return *(pstr.c_data()) == (*c_data()); |
||||
|
} |
||||
|
|
||||
|
bool operator!=(const pEp::String& pstr) const |
||||
|
{ |
||||
|
return !(*this == pstr); |
||||
|
} |
||||
|
|
||||
|
std::string to_string() const |
||||
|
{ |
||||
|
|
||||
|
if (_c_str_pp == nullptr) { |
||||
|
throw Exception{ EXSTR("invalid state") }; |
||||
|
} |
||||
|
|
||||
|
std::string ret{ "[" + CXX::Inspect::all(_c_str_pp) + " / " + |
||||
|
CXX::Inspect::all(*_c_str_pp) + "]" }; |
||||
|
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: |
||||
|
char* _copy(const std::string& str) |
||||
|
{ |
||||
|
char* ret = strdup(str.c_str()); |
||||
|
pEpLogClass(CXX::Inspect::all(ret)); |
||||
|
return ret; |
||||
|
} |
||||
|
|
||||
|
void _free(char* ptr_type) |
||||
|
{ |
||||
|
pEpLogClass(CXX::Inspect::all(ptr_type)); |
||||
|
::pEp_free(ptr_type); |
||||
|
} |
||||
|
|
||||
|
bool _is_bound{ false }; |
||||
|
bool _is_owner{ false }; |
||||
|
char** _c_str_pp{ 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{ false }; |
||||
|
|
||||
|
std::ostream& operator<<(std::ostream& o, const pEp::String& pEpStr) |
||||
|
{ |
||||
|
return o << std::string(pEpStr); |
||||
|
} |
||||
|
|
||||
|
//---------------------------------------------------------------------------------------------
|
||||
|
// TOOD:
|
||||
|
// ctor not exception safe
|
||||
|
// You can create an instance of a c-struct or you can wrap an already existing c-struct
|
||||
|
// In both cases, you can define if the wrapper owns the instance, or not.
|
||||
|
// If if owns it, it will free() it when the wrapper dies, otherwise it doesnt.
|
||||
|
template<typename T> |
||||
|
class PODStruct { |
||||
|
public: |
||||
|
PODStruct() = delete; |
||||
|
|
||||
|
// Creates a new instance or binds an existing one
|
||||
|
PODStruct(bool is_owner, T** c_struct_pp = nullptr) |
||||
|
{ |
||||
|
pEpLogClass("called"); |
||||
|
_init(is_owner, c_struct_pp); |
||||
|
} |
||||
|
|
||||
|
// Creates a new instance or binds an existing one
|
||||
|
// but takes custom alloc/free functions
|
||||
|
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); |
||||
|
} |
||||
|
|
||||
|
void bind(bool is_owner, T** c_struct_pp) |
||||
|
{ |
||||
|
if (c_struct_pp == nullptr) { |
||||
|
throw Exception{ EXSTR("cant bind on a nullptr") }; |
||||
|
} |
||||
|
|
||||
|
if (_is_bound) { |
||||
|
throw Exception{ EXSTR("can only bind once") }; |
||||
|
} |
||||
|
|
||||
|
// init
|
||||
|
_c_struct_pp = c_struct_pp; |
||||
|
_c_struct_p = *_c_struct_pp; |
||||
|
_is_bound = true; |
||||
|
pEpLogClass(to_string()); |
||||
|
set_owner(is_owner); |
||||
|
} |
||||
|
|
||||
|
~PODStruct() |
||||
|
{ |
||||
|
if (_is_owner) { |
||||
|
_effective_free(_c_struct_p); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
T** data() |
||||
|
{ |
||||
|
return _c_struct_pp; |
||||
|
} |
||||
|
|
||||
|
const T* c_data() const |
||||
|
{ |
||||
|
if (_c_struct_pp == nullptr) { |
||||
|
throw Exception{ EXSTR("invalid state") }; |
||||
|
} |
||||
|
return *_c_struct_pp; |
||||
|
} |
||||
|
|
||||
|
operator T*() |
||||
|
{ |
||||
|
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; |
||||
|
} |
||||
|
|
||||
|
static bool log_enabled; |
||||
|
|
||||
|
Adapter::pEpLog::pEpLogger logger{ typeid(T).name(), log_enabled }; |
||||
|
|
||||
|
protected: |
||||
|
Adapter::pEpLog::pEpLogger& m4gic_logger_n4me = logger; |
||||
|
|
||||
|
private: |
||||
|
const std::function<T*()> _effective_alloc{ std::bind(&PODStruct::_alloc, this) }; |
||||
|
const std::function<void(T*)> _effective_free{ |
||||
|
std::bind(&PODStruct::_free, this, std::placeholders::_1) |
||||
|
}; |
||||
|
|
||||
|
bool _is_bound{ false }; |
||||
|
bool _is_owner{ false }; |
||||
|
T** _c_struct_pp{ nullptr }; |
||||
|
T* _c_struct_p{ nullptr }; |
||||
|
|
||||
|
void _init(bool is_owner, T** c_struct_pp) |
||||
|
{ |
||||
|
// if no pp is given, alloc new,
|
||||
|
// otherwise bind to it
|
||||
|
if (!c_struct_pp) { |
||||
|
_c_struct_p = _effective_alloc(); |
||||
|
bind(is_owner, &_c_struct_p); |
||||
|
} else { |
||||
|
bind(is_owner, c_struct_pp); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// default alloc/free
|
||||
|
T* _alloc() |
||||
|
{ |
||||
|
T* ret = (T*)calloc(1, sizeof(T)); |
||||
|
pEpLogClass(CXX::Inspect::all(ret)); |
||||
|
return ret; |
||||
|
} |
||||
|
|
||||
|
void _free(T* ptr) |
||||
|
{ |
||||
|
pEpLogClass(CXX::Inspect::all(ptr)); |
||||
|
free(ptr); |
||||
|
} |
||||
|
|
||||
|
class Exception : public std::runtime_error { |
||||
|
public: |
||||
|
explicit Exception(const std::string& msg) : std::runtime_error(msg) {} |
||||
|
}; |
||||
|
}; |
||||
|
|
||||
|
//---------------------------------------------------------------------------------------------
|
||||
|
// TOOD:
|
||||
|
// ctor not exception safe
|
||||
|
// You can create an instance of a c-struct or you can wrap an already existing c-struct
|
||||
|
// In both cases, you can define if the wrapper owns the instance, or not.
|
||||
|
// If if owns it, it will free() it when the wrapper dies, otherwise it doesnt.
|
||||
|
//
|
||||
|
// alloc/free:
|
||||
|
// the alloc() and free() functions HAVE to use calloc/malloc NOT 'new', because we are interfacing
|
||||
|
// c99 code that could possibly get ownership of the struct and can only free memory that has
|
||||
|
// been allocated using malloc/calloc. (malloc/free - new/delete are NOT compatible)
|
||||
|
class TestStruct1 : public PODStruct<::Test_struct1> { |
||||
|
public: |
||||
|
TestStruct1(bool is_owner, ::Test_struct1** test_struct1_p = nullptr) : |
||||
|
PODStruct<::Test_struct1>(is_owner, test_struct1_p) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
// fields of the struct as 'properties' ;)
|
||||
|
pEp::POD<int> c_int{ &(*data())->c_int }; |
||||
|
pEp::POD<::Test_enum> c_enum{ &(*data())->c_enum }; |
||||
|
pEp::String c_str{ is_owner(), &(*data())->c_str }; |
||||
|
}; |
||||
|
|
||||
|
template<> |
||||
|
bool PODStruct<::Test_struct1>::log_enabled{ false }; |
||||
|
} // namespace pEp
|
Loading…
Reference in new issue