Created
December 17, 2014 17:33
-
-
Save KoKuToru/460cfffecd89d73cd376 to your computer and use it in GitHub Desktop.
CDynamicStruct
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef CDYNAMICSTRUCT | |
#define CDYNAMICSTRUCT | |
#pragma GCC push_options | |
#pragma GCC optimize (3) | |
#include <iostream> | |
#include <type_traits> | |
#include <string> | |
#include <vector> | |
#include <map> | |
#include <set> | |
#include <memory> | |
#include <string.h> | |
#include <algorithm> | |
#include <sstream> | |
#include <tuple> | |
#include <array> | |
#include "int_cast.h" | |
#include "num.h" | |
class var | |
{ | |
friend class CDynamicStruct; | |
friend class CDynamicStructMaster; | |
friend class var_init; | |
private: | |
virtual void constructor(char* memory) const = 0; | |
virtual void deconstructor(char* ptr) const = 0; | |
virtual int getIndex() const = 0; | |
virtual const char* getName() const = 0; | |
virtual std::string getAsString(const char* memory) const = 0; | |
virtual void setAsString(char* memory, const std::string& value) const = 0; | |
virtual void copy(const char* memory_from, char* memory_to) const = 0; | |
virtual size_t size() const = 0; | |
}; | |
class CDynamicStructMaster | |
{ | |
friend class CDynamicStruct; | |
friend class var_init; | |
private: | |
std::vector<int> index_list; | |
struct varcomp | |
{ | |
bool operator() (const var* lhs, const var* rhs) const | |
{ | |
return lhs->getIndex()<rhs->getIndex(); | |
} | |
}; | |
std::set<const var*, varcomp> memory_types; | |
static std::map<std::string, const var*> var_type_register; | |
public: | |
int getIndex(int i) | |
{ | |
if ((i >= index_list.size()) || (i < 0)) | |
{ | |
return -1; | |
} | |
return index_list[(size_t)i]; | |
} | |
void setIndex(int i, int ri) | |
{ | |
if ((i >= index_list.size()) || (i < 0)) | |
{ | |
index_list.resize((size_t)(i+1), -1); | |
} | |
index_list[(size_t)i] = ri; | |
} | |
}; | |
template<typename T> class var_type; | |
template<typename T> | |
class var_set | |
{ | |
friend class CDynamicStruct; | |
private: | |
const var_type<T>* parameter; | |
const T value; | |
public: | |
constexpr var_set(const var_type<T>* parameter, T value): parameter(parameter), value(value) {} | |
}; | |
template<typename T> | |
struct fix | |
{ | |
static const T& result(const T& a) { return a; } | |
}; | |
template<> | |
struct fix<char> | |
{ | |
static int result(char a) { return a; } | |
}; | |
template<> | |
struct fix<unsigned char> | |
{ | |
static int result(unsigned char a) { return a; } | |
}; | |
template<> | |
struct fix<std::string> | |
{ | |
static std::string result(const std::string& a) { return std::string("\"") + a + "\""; } | |
}; | |
template<typename U> | |
struct fix<std::vector<U>>; | |
template<typename U> | |
struct fix<::koku::num<U>>; | |
template<typename U, size_t S> | |
struct fix<std::array<U, S>>; | |
template<typename C, typename B> | |
struct fix<std::pair<C,B>>; | |
template<typename U> | |
struct fix<std::vector<U>> | |
{ | |
static std::string result(const std::vector<U>& a) | |
{ | |
std::stringstream ss; | |
ss << "["; | |
for(int i = 0; i < a.size(); ++i) | |
{ | |
if (i != 0) ss << ", "; | |
ss << fix<U>::result(a[i]); | |
} | |
ss << "]"; | |
return ss.str(); | |
} | |
}; | |
template<typename U, size_t S> | |
struct fix<std::array<U, S>> | |
{ | |
static std::string result(const std::array<U, S>& a) | |
{ | |
std::stringstream ss; | |
ss << "["; | |
for(int i = 0; i < a.size(); ++i) | |
{ | |
if (i != 0) ss << ", "; | |
ss << fix<U>::result(a[i]); | |
} | |
ss << "]"; | |
return ss.str(); | |
} | |
}; | |
template<typename C, typename B> | |
struct fix<std::pair<C,B>> | |
{ | |
static std::string result(const std::pair<C,B>& a) | |
{ | |
std::stringstream ss; | |
ss << "("; | |
ss << fix<C>::result(a.first); | |
ss << ", "; | |
ss << fix<B>::result(a.second); | |
ss << ")"; | |
return ss.str(); | |
} | |
}; | |
template<typename U> | |
struct fix<::koku::num<U>> | |
{ | |
static U result(const ::koku::num<U>& a) | |
{ | |
return a.value; | |
} | |
}; | |
template<typename T> | |
struct can_be_read | |
{ | |
typedef char yes[1]; | |
typedef char no[2]; | |
template<typename C> | |
static yes& f(C& r, decltype(&(std::cin >> r))); | |
template<typename> | |
static no & f(...); | |
T dummy; | |
static const bool value = sizeof(f<T>(dummy, nullptr)) == sizeof(yes); | |
}; | |
template<typename T> | |
class var_type: public var | |
{ | |
friend class CDynamicStruct; | |
public: | |
typedef T type; | |
private: | |
const char* name; | |
const int index; | |
size_t size() const | |
{ | |
if (std::is_pod<T>::value) | |
{ | |
return sizeof(T); | |
} | |
return sizeof(T*); | |
} | |
void set(char* memory, const T& value) const | |
{ | |
if (std::is_pod<T>::value) | |
{ | |
memcpy(memory, &value, sizeof(T)); | |
} | |
else | |
{ | |
T* tmp = nullptr; | |
memcpy(&tmp, memory, sizeof(T*)); | |
if (tmp != nullptr) | |
{ | |
*tmp = value; | |
} | |
else | |
{ | |
//should never occur | |
} | |
} | |
} | |
template <typename U> | |
typename std::enable_if< can_be_read<U>::value, U >::type | |
string2value(const std::string& value) const | |
{ | |
U real_value; | |
std::istringstream(value) >> real_value; | |
return real_value; | |
} | |
template <typename U> | |
typename std::enable_if<!can_be_read<U>::value, U >::type | |
string2value(const std::string&) const | |
{ | |
throw std::string("This type can't be converted from string to value"); | |
return U(); | |
} | |
void setAsString(char* memory, const std::string& value) const | |
{ | |
set(memory, string2value<T>(value)); | |
} | |
T get(const char* memory) const | |
{ | |
T value; | |
if (std::is_pod<T>::value) | |
{ | |
memcpy(&value, memory, sizeof(T)); | |
} | |
else | |
{ | |
T* tmp; | |
memcpy(&tmp, memory, sizeof(T*)); | |
if (tmp != nullptr) | |
{ | |
value = *tmp; | |
} | |
else | |
{ | |
value = T(); | |
} | |
} | |
return value; | |
} | |
void copy(const char* memory_from, char* memory_to) const | |
{ | |
set(memory_to, get(memory_from)); | |
} | |
std::string getAsString(const char* memory) const | |
{ | |
std::stringstream ss; | |
ss << fix<T>::result(get(memory)); | |
return ss.str(); | |
} | |
void constructor(char* memory) const | |
{ | |
if (std::is_pod<T>::value) | |
{ | |
T tmp = T(); | |
memcpy(memory, &tmp, sizeof(T)); | |
} | |
else | |
{ | |
//init data: | |
T* tmp = new T; | |
memcpy(memory, &tmp, sizeof(T*)); | |
} | |
} | |
void deconstructor(char* memory) const | |
{ | |
if (!std::is_pod<T>::value) | |
{ | |
T* tmp = nullptr; | |
memcpy(&tmp, memory, sizeof(T*)); | |
if (tmp != nullptr) | |
{ | |
delete tmp; | |
tmp = nullptr; | |
memcpy(memory, &tmp, sizeof(T*)); | |
} | |
} | |
} | |
int getIndex() const | |
{ | |
return index; | |
} | |
const char* getName() const | |
{ | |
return name; | |
} | |
public: | |
constexpr var_type(const char* name, int index): name(name), index(index) {} | |
const var_set<T> operator()(T value) const | |
{ | |
return var_set<T>(this, value); | |
} | |
const var_set<T> operator()() const | |
{ | |
return var_set<T>(this, T()); | |
} | |
}; | |
struct var_init | |
{ | |
var_init(const var* v) | |
{ | |
CDynamicStructMaster::var_type_register.insert({v->getName(), v}); | |
} | |
}; | |
#define REG(a) namespace hidden { var_init a##_init(&a); } | |
class CDynamicStruct | |
{ | |
private: | |
static std::map<size_t, CDynamicStructMaster> helper_cache; | |
CDynamicStructMaster* helper; | |
size_t helper_hash; | |
std::vector<char> memory; | |
CDynamicStruct* memory_tracker; | |
struct get_helper | |
{ | |
template<int index, typename S> struct iter; | |
template<typename S> struct iter<0, S> | |
{ | |
iter(const CDynamicStruct* from, CDynamicStruct* to, const S& parameters) {} | |
}; | |
template<int index, typename S> struct iter | |
{ | |
iter(const CDynamicStruct* from, CDynamicStruct* to, const S& parameters) | |
{ | |
iter<index-1, S>(from, to, parameters); | |
to->set(std::get<index-1>(parameters), from->get(std::get<index-1>(parameters))); | |
} | |
}; | |
}; | |
void init(const var* parameter) | |
{ | |
int memory_index = helper->getIndex(parameter->getIndex()); | |
if (memory_index == -1) | |
{ | |
//do not exists add it | |
CDynamicStructMaster* helper_old = helper; | |
hash_try_again: | |
helper = helper_old; //restore helper | |
//std::cout << "Update HASH from " << std::hex << helper_hash; | |
//new hash (using random functions.. no idea if it's save): | |
/*static std::mt19937_64 rng; | |
rng.seed(helper_hash + parameter->getIndex() + 1); | |
helper_hash = rng();*/ | |
helper_hash = (helper_hash << 1) + (helper_hash >> (sizeof(size_t)*8-1)) + (size_t)parameter->getIndex(); | |
//std::cout << " to " << std::hex << helper_hash << " added " << parameter->getName() << std::dec << std::endl; | |
//copy actual helper: | |
auto insert_result = helper_cache.insert({helper_hash, *helper}); | |
helper = &(insert_result.first->second); | |
//set new index: | |
memory_index = koku::int_cast<int>(memory.size()); | |
if (insert_result.second) | |
{ | |
//new helper_cache | |
helper->setIndex(parameter->getIndex(), memory_index); | |
} | |
else | |
{ | |
//got old helper_cache | |
if (helper->getIndex(parameter->getIndex()) != memory_index) | |
{ | |
//something wrong | |
//std::cout << "HASH collision !" << std::endl; | |
goto hash_try_again; | |
} | |
} | |
//add new memory-type to | |
helper->memory_types.insert(parameter); | |
//resize memory: | |
memory.resize ((size_t)memory_index + parameter->size()); | |
memory.reserve((size_t)memory_index + sizeof(void*)); //padding for pointer-read | |
//init data: | |
parameter->constructor(memory.data() + memory_index); | |
} | |
} | |
public: | |
template<typename T> | |
const __attribute__((always_inline, flatten)) T get(const var_type<T>& parameter) const | |
{ | |
int memory_index = helper->getIndex(parameter.index); | |
if (memory_index == -1) | |
{ | |
//do not exists | |
//std::cout << "Paramete " << parameter.getName() << " not found" << std::endl; | |
return T(); | |
} | |
return parameter.get(memory.data() + memory_index); | |
} | |
const std::string get(const std::string& name) const | |
{ | |
//iterate all members | |
std::string result; | |
for_each(helper->memory_types.begin(), helper->memory_types.end(), [this, &result, &name](const var* ptr) | |
{ | |
if (result.empty()) | |
{ | |
if (ptr->getName() == name) | |
{ | |
int memory_index = helper->getIndex(ptr->getIndex()); | |
if (memory_index != -1) | |
{ | |
result = ptr->getAsString(memory.data() + memory_index); | |
} | |
} | |
} | |
}); | |
return result; | |
} | |
template<typename ... T> | |
CDynamicStruct get(const std::tuple<const var_type<T>& ... >& parameters) const | |
{ | |
CDynamicStruct result; | |
get_helper::iter<sizeof...(T), decltype(parameters)>(this, &result, parameters); | |
return result; | |
} | |
template<typename T> | |
const T operator[](const var_type<T>& parameter) const | |
{ | |
return get(parameter); | |
} | |
const std::string operator[](const std::string& name) const | |
{ | |
return get(name); | |
} | |
template<typename ... T> | |
CDynamicStruct operator[](std::tuple<const var_type<T>& ... > parameters) const | |
{ | |
return get(parameters); | |
} | |
template<typename T> | |
void __attribute__((flatten)) set(const var_type<T>& parameter, const T& value) | |
{ | |
if (memory_tracker != nullptr) | |
{ | |
//check if already used | |
if (memory_tracker->helper->getIndex(parameter.index) == -1) | |
{ | |
//backup data | |
memory_tracker->set(parameter, get(parameter)); | |
} | |
} | |
init(¶meter); | |
int memory_index = helper->getIndex(parameter.index); | |
parameter.set(memory.data() + memory_index, value); | |
} | |
void set(const std::string& name, const std::string& value) | |
{ | |
//iterate all members | |
bool worked = false; | |
for_each(helper->memory_types.begin(), helper->memory_types.end(), [this, &value, &name, &worked](const var* ptr) | |
{ | |
if (!worked) | |
{ | |
if (ptr->getName() == name) | |
{ | |
int memory_index = helper->getIndex(ptr->getIndex()); | |
if (memory_index != -1) | |
{ | |
ptr->setAsString(memory.data() + memory_index, value); | |
worked = true; | |
} | |
} | |
} | |
}); | |
if (!worked) | |
{ | |
auto iter = CDynamicStructMaster::var_type_register.find(name); | |
if (iter == CDynamicStructMaster::var_type_register.end()) | |
{ | |
throw std::string("Name doesn't exists globally !"); | |
} | |
init(iter->second); | |
int memory_index = helper->getIndex(iter->second->getIndex()); | |
iter->second->setAsString(memory.data() + memory_index, value); | |
} | |
} | |
void set(const CDynamicStruct& other) | |
{ | |
//iterate all members | |
for_each(other.helper->memory_types.begin(), other.helper->memory_types.end(), [this, &other](const var* ptr) | |
{ | |
int memory_index = other.helper->getIndex(ptr->getIndex()); | |
if (memory_index != -1) | |
{ | |
init(ptr); | |
int this_memory_index = helper->getIndex(ptr->getIndex()); | |
ptr->copy(other.memory.data() + memory_index, memory.data() + this_memory_index); | |
} | |
}); | |
} | |
template<typename T> | |
CDynamicStruct& operator<<(const var_set<T>& value_set) | |
{ | |
set(*value_set.parameter, value_set.value); | |
return *this; | |
} | |
CDynamicStruct& operator<<(const CDynamicStruct& other) | |
{ | |
set(other); | |
return *this; | |
} | |
const CDynamicStruct& operator=(const CDynamicStruct& other) | |
{ | |
helper = other.helper; | |
helper_hash = other.helper_hash; | |
memory.clear(); | |
memory.resize(other.memory.size()); //don't copy data, just make same size | |
memory.reserve(memory.capacity()); | |
if (memory_tracker != nullptr) | |
{ | |
throw std::string("Can't copy into tracked CDynamicStruct !"); | |
} | |
//iterate all members | |
for_each(helper->memory_types.begin(), helper->memory_types.end(), [this, &other](const var* ptr) | |
{ | |
int memory_index = helper->getIndex(ptr->getIndex()); | |
if (memory_index != -1) | |
{ | |
ptr->constructor(memory.data() + memory_index); | |
ptr->copy(other.memory.data() + memory_index, memory.data() + memory_index); | |
} | |
}); | |
return *this; | |
} | |
CDynamicStruct(const CDynamicStruct& other, bool tracked = false): memory_tracker(nullptr) | |
{ | |
operator=(other); | |
if (tracked) | |
{ | |
//this will track memory changes | |
memory_tracker = new CDynamicStruct(); | |
} | |
} | |
CDynamicStruct(bool tracked = false): memory_tracker(nullptr) | |
{ | |
//std::cout << "CDynamicStruct " << (void*)this << std::endl; | |
static size_t root_hash = 0; | |
static CDynamicStructMaster* root_helper = nullptr; | |
//new hash: | |
helper_hash = 0; | |
if (root_helper == nullptr) | |
{ | |
//new helper: | |
/*static std::mt19937_64 rng; | |
rng.seed(helper_hash); | |
root_hash = rng();*/ | |
root_hash = 1; | |
root_helper = &(helper_cache.insert({root_hash, CDynamicStructMaster()}).first->second); | |
} | |
helper = root_helper; | |
helper_hash = root_hash; | |
if (tracked) | |
{ | |
//this will track memory changes | |
memory_tracker = new CDynamicStruct(); | |
} | |
} | |
~CDynamicStruct() | |
{ | |
//std::cout << "~CDynamicStruct " << (void*)this << std::endl; | |
//iterate all members | |
for_each(helper->memory_types.begin(), helper->memory_types.end(), [this](const var* ptr) | |
{ | |
int memory_index = helper->getIndex(ptr->getIndex()); | |
if (memory_index != -1) | |
{ | |
ptr->deconstructor(memory.data() + memory_index); | |
} | |
}); | |
if (memory_tracker != nullptr) | |
{ | |
delete memory_tracker; | |
memory_tracker = nullptr; | |
} | |
} | |
std::vector<std::string> parameters() const | |
{ | |
std::vector<std::string> tmp; | |
for_each(helper->memory_types.begin(), helper->memory_types.end(), [this, &tmp](const var* ptr) | |
{ | |
int memory_index = helper->getIndex(ptr->getIndex()); | |
if (memory_index != -1) | |
{ | |
tmp.push_back(ptr->getName()); | |
} | |
}); | |
return tmp; | |
} | |
std::vector<std::string> values() const | |
{ | |
std::vector<std::string> tmp; | |
for_each(helper->memory_types.begin(), helper->memory_types.end(), [this, &tmp](const var* ptr) | |
{ | |
int memory_index = helper->getIndex(ptr->getIndex()); | |
if (memory_index != -1) | |
{ | |
tmp.push_back(ptr->getAsString(memory.data() + memory_index)); | |
} | |
}); | |
return tmp; | |
} | |
std::vector<std::pair<std::string, std::string>> parameters_values() const | |
{ | |
std::vector<std::pair<std::string, std::string>> tmp; | |
for_each(helper->memory_types.begin(), helper->memory_types.end(), [this, &tmp](const var* ptr) | |
{ | |
int memory_index = helper->getIndex(ptr->getIndex()); | |
if (memory_index != -1) | |
{ | |
std::stringstream ss; | |
ss << ptr->getIndex() << " => " << ptr->getName(); | |
tmp.push_back({ss.str(), ptr->getAsString(memory.data() + memory_index)}); | |
} | |
}); | |
return tmp; | |
} | |
//flushes tracker_info | |
void flush() | |
{ | |
if (memory_tracker != nullptr) | |
{ | |
//call deconstructor | |
memory_tracker->~CDynamicStruct(); | |
//reuse old memory | |
new (memory_tracker) CDynamicStruct; | |
} | |
} | |
template<typename T> | |
bool updated(const var_type<T>& parameter, T& old_value) const | |
{ | |
if (memory_tracker == nullptr) | |
{ | |
return false; | |
} | |
if (!memory_tracker->exists(parameter)) | |
{ | |
return false; | |
} | |
old_value = memory_tracker->get(parameter); | |
return true; | |
} | |
template<typename T> | |
bool updated(const var_type<T>& parameter) const | |
{ | |
if (memory_tracker == nullptr) | |
{ | |
return false; | |
} | |
if (!memory_tracker->exists(parameter)) | |
{ | |
return false; | |
} | |
return true; | |
} | |
template<typename T> | |
bool exists(const var_type<T>& parameter) const | |
{ | |
return helper->getIndex(parameter.index) != -1; | |
} | |
void reset() | |
{ | |
this->~CDynamicStruct(); | |
new (this) CDynamicStruct; | |
} | |
void reset(const CDynamicStruct& init) | |
{ | |
bool tracked = (memory_tracker != nullptr); | |
this->~CDynamicStruct(); | |
new (this) CDynamicStruct(init, tracked); | |
} | |
}; | |
#pragma GCC pop_options | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment