Skip to content

Instantly share code, notes, and snippets.

@KoKuToru
Created December 17, 2014 17:33
Show Gist options
  • Save KoKuToru/460cfffecd89d73cd376 to your computer and use it in GitHub Desktop.
Save KoKuToru/460cfffecd89d73cd376 to your computer and use it in GitHub Desktop.
CDynamicStruct
#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(&parameter);
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