Reworked the property system and fixed a crash

This commit is contained in:
WolverinDEV 2021-03-01 14:16:43 +01:00
parent b26132fa54
commit 465975e9ca
5 changed files with 296 additions and 221 deletions

View File

@ -13,7 +13,7 @@ using namespace std::chrono;
using namespace ts;
BasicChannel::BasicChannel(ChannelId parentId, ChannelId channelId) {
this->setProperties(make_shared<Properties>());
this->setProperties(make_shared<PropertyManager>());
this->_properties->register_property_type<property::ChannelProperties>();
this->properties()[property::CHANNEL_ID] = channelId;
@ -27,7 +27,7 @@ void BasicChannel::setPermissionManager(const std::shared_ptr<permission::v2::Pe
this->update_properties_from_permissions(flag_view_update);
}
void BasicChannel::setProperties(const std::shared_ptr<ts::Properties>& props) {
void BasicChannel::setProperties(const std::shared_ptr<ts::PropertyManager>& props) {
if(this->_properties) {
(*props)[property::CHANNEL_ID] = this->channelId();
(*props)[property::CHANNEL_PID] = this->properties()[property::CHANNEL_PID].value();
@ -60,7 +60,7 @@ std::vector<property::ChannelProperties> BasicChannel::update_properties_from_pe
auto fvalue = permission_manager->permission_value_flagged(permission::i_icon_id);
if(fvalue.has_value)
target_icon_id = (IconId) fvalue.value;
if(this->properties()[property::CHANNEL_ICON_ID] != target_icon_id) {
if(this->properties()[property::CHANNEL_ICON_ID].as_or(0) != target_icon_id) {
this->properties()[property::CHANNEL_ICON_ID] = target_icon_id;
result.push_back(property::CHANNEL_ICON_ID);
}
@ -72,7 +72,7 @@ std::vector<property::ChannelProperties> BasicChannel::update_properties_from_pe
auto fvalue = permission_manager->permission_value_flagged(permission::i_client_needed_talk_power);
if(fvalue.has_value)
talk_power = fvalue.value;
if(this->properties()[property::CHANNEL_NEEDED_TALK_POWER] != talk_power) {
if(this->properties()[property::CHANNEL_NEEDED_TALK_POWER].as_or(0) != talk_power) {
this->properties()[property::CHANNEL_NEEDED_TALK_POWER] = talk_power;
result.push_back(property::CHANNEL_NEEDED_TALK_POWER);
}
@ -108,8 +108,8 @@ BasicChannel::BasicChannel(std::shared_ptr<BasicChannel> parent, ChannelId chann
BasicChannel::~BasicChannel() { }
ChannelType::ChannelType BasicChannel::channelType() {
if (this->properties()[property::CHANNEL_FLAG_PERMANENT].as<bool>()) return ChannelType::ChannelType::permanent;
else if (this->properties()[property::CHANNEL_FLAG_SEMI_PERMANENT].as<bool>()) return ChannelType::ChannelType::semipermanent;
if (this->properties()[property::CHANNEL_FLAG_PERMANENT].as_unchecked<bool>()) return ChannelType::ChannelType::permanent;
else if (this->properties()[property::CHANNEL_FLAG_SEMI_PERMANENT].as_unchecked<bool>()) return ChannelType::ChannelType::semipermanent;
else return ChannelType::ChannelType::temporary;
}
@ -119,21 +119,22 @@ void BasicChannel::setChannelType(ChannelType::ChannelType type) {
}
bool BasicChannel::passwordMatch(std::string password, bool hashed) {
if (!this->properties()[property::CHANNEL_FLAG_PASSWORD].as<bool>() || this->properties()[property::CHANNEL_PASSWORD].value().empty()) return true;
if (!this->properties()[property::CHANNEL_FLAG_PASSWORD].as_unchecked<bool>() || this->properties()[property::CHANNEL_PASSWORD].value().empty()) return true;
if (password.empty()) return false;
if(this->properties()[property::CHANNEL_PASSWORD].as<string>() == password)
if(this->properties()[property::CHANNEL_PASSWORD].as_unchecked<string>() == password)
return true;
password = base64::encode(digest::sha1(password));
return this->properties()[property::CHANNEL_PASSWORD].as<string>() == password;
return this->properties()[property::CHANNEL_PASSWORD].as_unchecked<string>() == password;
}
int64_t BasicChannel::emptySince() {
if (!properties().hasProperty(property::CHANNEL_LAST_LEFT))
return 0;
time_point<system_clock> lastLeft = time_point<system_clock>() + milliseconds(properties()[property::CHANNEL_LAST_LEFT].as<uint64_t>());
time_point<system_clock> lastLeft = time_point<system_clock>() + milliseconds(
properties()[property::CHANNEL_LAST_LEFT].as_unchecked<uint64_t>());
return (int64_t) duration_cast<seconds>(system_clock::now() - lastLeft).count();
}
@ -280,7 +281,7 @@ bool BasicChannelTree::setDefaultChannel(const shared_ptr<BasicChannel> &ch) {
std::shared_ptr<BasicChannel> BasicChannelTree::getDefaultChannel() {
for (auto elm : this->channels())
if (elm->properties()[property::CHANNEL_FLAG_DEFAULT].as<bool>()) return elm;
if (elm->properties()[property::CHANNEL_FLAG_DEFAULT].as_unchecked<bool>()) return elm;
return nullptr;
}

View File

@ -45,7 +45,7 @@ namespace ts {
inline std::string name(){ return properties()[property::CHANNEL_NAME]; }
inline ChannelId channelOrder(){ return this->previousChannelId(); }
inline Properties& properties() const { return *this->_properties; }
inline PropertyManager& properties() const { return *this->_properties; }
ChannelType::ChannelType channelType();
void setChannelType(ChannelType::ChannelType);
@ -55,7 +55,8 @@ namespace ts {
int64_t emptySince();
inline std::chrono::system_clock::time_point createdTimestamp() {
return std::chrono::system_clock::time_point() + std::chrono::milliseconds(this->properties()[property::CHANNEL_CREATED_AT].as<int64_t>());
return std::chrono::system_clock::time_point() + std::chrono::milliseconds(
this->properties()[property::CHANNEL_CREATED_AT].as_unchecked<int64_t>());
}
ts_always_inline bool permission_require_property_update(const permission::PermissionType& permission) {
@ -76,7 +77,7 @@ namespace ts {
ts_always_inline std::shared_ptr<permission::v2::PermissionManager> permissions(){ return this->_permissions; }
virtual void setPermissionManager(const std::shared_ptr<permission::v2::PermissionManager>&);
virtual void setProperties(const std::shared_ptr<Properties>&);
virtual void setProperties(const std::shared_ptr<PropertyManager>&);
private:
ts_always_inline
static bool permission_granted(const permission::v2::PermissionFlaggedValue& channel_permission_value, const permission::v2::PermissionFlaggedValue& granted_value, bool require_granted_value) {
@ -97,7 +98,7 @@ namespace ts {
void setLinkedHandle(const std::weak_ptr<TreeView::LinkedTreeEntry> &) override;
protected:
std::weak_ptr<TreeView::LinkedTreeEntry> _link;
std::shared_ptr<Properties> _properties;
std::shared_ptr<PropertyManager> _properties;
std::shared_ptr<permission::v2::PermissionManager> _permissions;
permission::v2::PermissionFlaggedValue last_view_power{0, false};

View File

@ -8,68 +8,76 @@ using namespace ts;
using namespace ts::property;
using namespace std;
Properties::Properties() {
memtrack::allocated<Properties>(this);
PropertyManager::PropertyManager() {
memtrack::allocated<PropertyManager>(this);
}
Properties::~Properties() {
memtrack::freed<Properties>(this);
PropertyManager::~PropertyManager() {
memtrack::freed<PropertyManager>(this);
}
bool Properties::has(property::PropertyType type, int index) {
bool PropertyManager::has(property::PropertyType type, int index) {
for(auto it = this->properties.begin(); it != this->properties.end(); it++) {
if(!*it) continue;
if(it->get()->type != type) continue;
auto& bundle = *it;
if(bundle->type != type) {
continue;
}
return index < it->get()->length;
return index < bundle->property_count;
}
return false;
}
PropertyWrapper Properties::find(property::PropertyType type, int index) {
Property PropertyManager::get(property::PropertyType type, int index) {
for (auto &bulk : this->properties) {
if(!bulk) continue;
if(bulk->type != type)
if(bulk->type != type) {
continue;
}
if(index >= bulk->length)
if(index >= bulk->property_count) {
break;
}
return PropertyWrapper{this, &bulk->properties[index], bulk};
return Property{this->weak_from_this().lock(), &bulk->properties[index], bulk};
}
throw std::invalid_argument("missing property type");
}
std::vector<PropertyWrapper> Properties::list_properties(ts::property::flag_type flagMask, ts::property::flag_type negatedFlagMask) {
vector<PropertyWrapper> result;
std::vector<Property> PropertyManager::list_properties(ts::property::flag_type flagMask, ts::property::flag_type negatedFlagMask) {
std::vector<Property> result{};
result.reserve(this->properties_count);
auto self_ref = this->weak_from_this().lock();
for (auto &bulk : this->properties) {
for(int index = 0; index < bulk->length; index++) {
for(int index = 0; index < bulk->property_count; index++) {
auto& property = bulk->properties[index];
if((property.description->flags & flagMask) > 0 && (property.description->flags & negatedFlagMask) == 0)
result.emplace_back(this, &property, bulk);
if((property.description->flags & flagMask) > 0 && (property.description->flags & negatedFlagMask) == 0) {
result.emplace_back(self_ref, &property, bulk);
}
}
}
return result;
}
std::vector<PropertyWrapper> Properties::all_properties() {
vector<PropertyWrapper> result;
std::vector<Property> PropertyManager::all_properties() {
std::vector<Property> result{};
result.reserve(this->properties_count);
auto self_ref = this->weak_from_this().lock();
for (auto &bulk : this->properties) {
for(int index = 0; index < bulk->length; index++)
result.emplace_back(this, &bulk->properties[index], bulk);
for(int index = 0; index < bulk->property_count; index++) {
result.emplace_back(self_ref, &bulk->properties[index], bulk);
}
}
return result;
}
PropertyWrapper::PropertyWrapper(ts::Properties* handle, ts::PropertyData *data, std::shared_ptr<ts::PropertyBundle> bundle_lock) : handle{handle}, data_ptr{data}, bundle_lock{std::move(bundle_lock)} {
Property::Property(std::shared_ptr<PropertyManager> handle, ts::PropertyData *data, std::shared_ptr<ts::PropertyBundle> bundle_lock)
: handle{std::move(handle)}, property_data{data}, bundle_lock{std::move(bundle_lock)} {
}
void PropertyWrapper::trigger_update() {
this->data_ptr->flag_modified = true;
void Property::trigger_update() {
this->property_data->flag_modified = true;
if(this->handle) {
for(const auto& elm : this->handle->notifyFunctions)
@ -77,43 +85,46 @@ void PropertyWrapper::trigger_update() {
}
}
bool Properties::register_property_type(ts::property::PropertyType type, size_t length) {
for(auto& bulk : this->properties)
if(bulk->type == type)
return false;
void PropertyManager::do_register_property_type(ts::property::PropertyType type, size_t length) {
for(auto& bulk : this->properties) {
if(bulk->type == type) {
return;
}
}
const auto alloc_length = sizeof(PropertyBundle) + sizeof(PropertyData) * length;
auto ptr = shared_ptr<PropertyBundle>((PropertyBundle*) malloc(alloc_length), [](PropertyBundle* bundle) {
if(!bundle) return;
if(!bundle) {
return;
}
for(int index = 0; index < bundle->length; index++) {
for(int index = 0; index < bundle->property_count; index++) {
auto& property = bundle->properties[index];
property.value.~string();
property.value_lock.~spin_mutex();
property.casted_value.~any();
}
::free(bundle);
});
ptr->type = type;
ptr->length = length;
ptr->property_count = length;
for(int index = 0; index < length; index++) {
auto& property = ptr->properties[index];
new (&property.casted_value) any();
new (&property.value_lock) spin_mutex();
new (&property.value) string();
new (&property.casted_value) any{};
new (&property.value) string{};
property.description = &property::describe(type, index);
property.flag_modified = false;
property.flag_db_reference = false;
property.flag_database_reference = false;
property.value = property.description->default_value;
this->properties_count++;
}
this->properties.push_back(ptr);
return false;
return;
}
namespace ts {

View File

@ -13,6 +13,8 @@
#include <functional>
#include <any>
#include <array>
#include <optional>
#include <type_traits>
#include "misc/spin_mutex.h"
#include "converters/converter.h"
@ -495,31 +497,55 @@ namespace ts {
}
template<typename>
constexpr inline PropertyType type_from_enum();
struct type_from_enum_t {
constexpr static auto supported{false};
constexpr static auto type{PropertyType::PROP_TYPE_UNKNOWN};
};
template<>
constexpr inline PropertyType type_from_enum<VirtualServerProperties>() { return PropertyType::PROP_TYPE_SERVER; }
struct type_from_enum_t<VirtualServerProperties> {
constexpr static auto supported{true};
constexpr static auto type{PropertyType::PROP_TYPE_SERVER};
};
template<>
constexpr inline PropertyType type_from_enum<ChannelProperties>() { return PropertyType::PROP_TYPE_CHANNEL; }
struct type_from_enum_t<ChannelProperties> {
constexpr static auto supported{true};
constexpr static auto type{PropertyType::PROP_TYPE_CHANNEL};
};
template<>
constexpr inline PropertyType type_from_enum<ClientProperties>() { return PropertyType::PROP_TYPE_CLIENT; }
struct type_from_enum_t<ClientProperties> {
constexpr static auto supported{true};
constexpr static auto type{PropertyType::PROP_TYPE_CLIENT};
};
template<>
constexpr inline PropertyType type_from_enum<ConnectionProperties>() { return PropertyType::PROP_TYPE_CONNECTION; }
struct type_from_enum_t<ConnectionProperties> {
constexpr static auto supported{true};
constexpr static auto type{PropertyType::PROP_TYPE_CONNECTION};
};
template<>
constexpr inline PropertyType type_from_enum<GroupProperties>() { return PropertyType::PROP_TYPE_GROUP; }
struct type_from_enum_t<GroupProperties> {
constexpr static auto supported{true};
constexpr static auto type{PropertyType::PROP_TYPE_GROUP};
};
template<>
constexpr inline PropertyType type_from_enum<InstanceProperties>() { return PropertyType::PROP_TYPE_INSTANCE; }
struct type_from_enum_t<InstanceProperties> {
constexpr static auto supported{true};
constexpr static auto type{PropertyType::PROP_TYPE_INSTANCE};
};
template<>
constexpr inline PropertyType type_from_enum<PlaylistProperties>() { return PropertyType::PROP_TYPE_PLAYLIST; }
struct type_from_enum_t<PlaylistProperties> {
constexpr static auto supported{true};
constexpr static auto type{PropertyType::PROP_TYPE_PLAYLIST};
};
template<>
constexpr inline PropertyType type_from_enum<UnknownProperties>() { return PropertyType::PROP_TYPE_UNKNOWN; }
template<typename T>
constexpr inline PropertyType type_from_enum() { return type_from_enum_t<T>::type; }
struct PropertyDescription {
std::string_view name{};
@ -545,7 +571,7 @@ namespace ts {
template <typename PropertyEnumType, typename std::enable_if<std::is_enum<PropertyEnumType>::value, int>::type = 0>
constexpr inline bool operator==(const PropertyEnumType& other) const {
return this->property_index == (int) other && this->type_property == type_from_enum<PropertyEnumType>();
return this->property_index == (int) other && this->type_property == type_from_enum_t<PropertyEnumType>::type;
}
[[nodiscard]] inline bool is_undefined() const { return property_index == 0; }
@ -645,20 +671,27 @@ namespace ts {
}
inline const auto& find(PropertyType type, const std::string_view& name) {
if(type >= property_list_info.end_index.size()) return undefined_property_description;
if(type >= property_list_info.end_index.size()) {
return undefined_property_description;
}
constexpr static auto buffer_size{128}; /* no property is longer than 128 bytes */
if(name.size() >= buffer_size) return undefined_property_description;
if(name.size() >= buffer_size) {
return undefined_property_description;
}
char buffer[buffer_size];
for(size_t index{0}; index < name.size(); index++)
for(size_t index{0}; index < name.size(); index++) {
buffer[index] = (char) tolower(name[index]);
}
const std::string_view lower_name{buffer, name.size()};
const auto begin = property_list_info.begin_index[type];
const auto end = property_list_info.end_index[type];
for(size_t index{begin}; index < end; index++)
if(property_list[index].name == lower_name)
for(size_t index{begin}; index < end; index++) {
if(property_list[index].name == lower_name) {
return property_list[index];
}
}
return property_list[begin]; /* begin index MUST be the undefined */
}
@ -697,15 +730,14 @@ namespace ts {
#undef const_modifier
}
class Properties;
class PropertyManager;
struct PropertyData {
spin_mutex value_lock;
std::any casted_value;
std::string value;
const property::PropertyDescription* description;
bool flag_db_reference;
bool flag_database_reference;
bool flag_modified;
};
@ -714,106 +746,118 @@ namespace ts {
#pragma warning( disable : 4200 )
#endif
struct PropertyBundle {
std::mutex value_mutex{};
property::PropertyType type;
size_t length;
size_t property_count;
PropertyData properties[0];
};
#ifdef WIN32
#pragma warning( pop )
#endif
template <typename T>
struct PropertyAccess {
inline static T get(PropertyData* data_ptr) {
std::lock_guard lock(data_ptr->value_lock);
if(data_ptr->casted_value.type() == typeid(T))
return std::any_cast<T>(data_ptr->casted_value);
data_ptr->casted_value = ts::converter<T>::from_string_view(std::string_view{data_ptr->value});
return std::any_cast<T>(data_ptr->casted_value);
}
};
template <>
struct PropertyAccess<std::string> {
inline static std::string get(PropertyData* data_ptr) { return data_ptr->value; }
};
struct PropertyWrapper {
friend class Properties;
struct Property {
friend class PropertyManager;
public:
bool operator==(const PropertyWrapper& other) {
if(this->data_ptr == other.data_ptr)
return true;
return this->data_ptr->value == other.data_ptr->value;
}
explicit Property(std::shared_ptr<PropertyManager> /* handle */, PropertyData* /* ptr */, std::shared_ptr<PropertyBundle> /* bundle */);
template <typename T>
bool operator==(const T& other) {
return this->as<T>() == other;
auto value = this->as<T>();
return value.has_value() && *value == other;
}
template <typename T>
bool operator!=(const T& other){
return !operator==(other);
return !(*this == other);
}
/*
//Math operators
PropertyWrapper&operator++(){ return operator=(as<int64_t>() + 1); }
PropertyWrapper&operator++(int){ return operator=(as<int64_t>() + 1); }
PropertyWrapper&operator+=(uint16_t val){ return operator=(as<uint16_t>() + val); }
PropertyWrapper&operator+=(int64_t val){ return operator=(as<int64_t>() + val); }
PropertyWrapper&operator+=(uint64_t val){ return operator=(as<uint64_t>() + val); }
inline Property&operator++() { return operator=(this->as_unchecked<int64_t>() + 1); }
inline Property&operator++(int) { return operator=(this->as_unchecked<int64_t>() + 1); }
inline Property&operator+=(uint16_t val) { return operator=(this->as_unchecked<uint16_t>() + val); }
inline Property&operator+=(int64_t val) { return operator=(this->as_unchecked<int64_t>() + val); }
inline Property&operator+=(uint64_t val) { return operator=(this->as_unchecked<uint64_t>() + val); }
*/
[[nodiscard]] bool hasDbReference() const { return this->data_ptr->flag_db_reference; }
void setDbReference(bool flag){ this->data_ptr->flag_db_reference = flag; }
/**
* Get the property manager for this property.
* Note: The handle might be null if the manager hasn't been created with
* `std::make_shared<..>()`.
*/
[[nodiscard]] inline const auto& get_handle() { return this->handle; }
[[nodiscard]] bool isModified() const { return this->data_ptr->flag_modified; }
void setModified(bool flag){ this->data_ptr->flag_modified = flag; }
template <typename T>
[[nodiscard]] T as() const {
static_assert(ts::converter<T>::supported, "as<T> isn't supported for type");
return PropertyAccess<T>::get(this->data_ptr);
}
[[nodiscard]] bool hasDbReference() const { return this->property_data->flag_database_reference; }
void setDbReference(bool flag){ this->property_data->flag_database_reference = flag; }
[[nodiscard]] bool isModified() const { return this->property_data->flag_modified; }
void setModified(bool flag){ this->property_data->flag_modified = flag; }
#ifndef WIN32
template <typename T>
[[nodiscard]] operator T(){ return this->as<T>(); }
[[nodiscard]] operator T(){ return this->as_unchecked<T>(); }
#endif
template <typename T>
[[nodiscard]] T as_save(const std::function<T()> defaultValue = []{ return T{}; }) const {
try {
std::lock_guard lock(this->data_ptr->value_lock);
if(this->data_ptr->casted_value.type() == typeid(T))
return std::any_cast<T>(this->data_ptr->casted_value);
[[nodiscard]] std::optional<T> as() const {
static_assert(ts::converter<T>::supported, "as<T> isn't supported for type");
static_assert(!ts::converter<T>::references, "as<T> only supports non reference types");
this->data_ptr->casted_value = ts::converter<T>::from_string_view(this->data_ptr->value);
return std::any_cast<T>(this->data_ptr->casted_value);
try {
std::lock_guard value_lock{this->bundle_lock->value_mutex};
if(this->property_data->casted_value.type() == typeid(T)) {
const auto& value = std::any_cast<T>(this->property_data->casted_value);
return std::make_optional(value);
}
auto value = ts::converter<T>::from_string_view(this->property_data->value);
this->property_data->casted_value = value;
return std::make_optional(std::move(value));
} catch(std::exception&) {
return std::nullopt;
}
}
template <typename T>
[[nodiscard]] T as_or(T fallback_value) const {
try {
std::lock_guard value_lock{this->bundle_lock->value_mutex};
if(this->property_data->casted_value.type() == typeid(T)) {
return std::any_cast<T>(this->property_data->casted_value);
}
this->property_data->casted_value = ts::converter<T>::from_string_view(this->property_data->value);
return std::any_cast<T>(this->property_data->casted_value);
} catch(std::exception&) {
return fallback_value;
}
}
template <typename T>
[[nodiscard]] T as_or_get(const std::function<T()> defaultValue = [] { return T{}; }) const {
try {
std::lock_guard value_lock{this->bundle_lock->value_mutex};
if(this->property_data->casted_value.type() == typeid(T)) {
return std::any_cast<T>(this->property_data->casted_value);
}
this->property_data->casted_value = ts::converter<T>::from_string_view(this->property_data->value);
return std::any_cast<T>(this->property_data->casted_value);
} catch(std::exception&) {
return defaultValue();
}
}
[[nodiscard]] const property::PropertyDescription& type() const { return *this->data_ptr->description; }
[[nodiscard]] std::string value() const {
std::lock_guard lock{this->data_ptr->value_lock};
return this->data_ptr->value;
/* TODO: Depricate */
template <typename T>
[[nodiscard]] T as_unchecked() const {
return std::move(*this->as<T>());
}
void value(const std::string &value, bool trigger_update = true){
{
std::lock_guard lock(this->data_ptr->value_lock);
if(this->data_ptr->value == value)
return;
this->data_ptr->casted_value.reset();
this->data_ptr->value = value;
}
if(trigger_update)
this->trigger_update();
[[nodiscard]] const property::PropertyDescription& type() const { return *this->property_data->description; }
[[nodiscard]] std::string value() const {
std::lock_guard value_lock{this->bundle_lock->value_mutex};
return this->property_data->value;
}
[[nodiscard]] const std::string_view& default_value() const {
@ -821,92 +865,105 @@ namespace ts {
}
template <typename T>
PropertyWrapper& operator=(const T& value) {
this->update_value(value);
return *this;
}
template <typename T>
bool update_value(const T& value) {
bool update_value(T value) {
static_assert(ts::converter<T>::supported, "type isn't supported for type");
{
std::any any_value{value};
std::any any_value{std::move(value)};
auto value_string = ts::converter<T>::to_string(any_value);
std::lock_guard lock(this->data_ptr->value_lock);
if(value_string == this->data_ptr->value) {
std::lock_guard value_lock{this->bundle_lock->value_mutex};
if(value_string == this->property_data->value) {
return false;
}
this->data_ptr->casted_value = any_value;
this->data_ptr->value = value_string;
this->property_data->casted_value = any_value;
this->property_data->value = std::move(value_string);
}
this->trigger_update();
return true;
}
PropertyWrapper& operator=(const std::string& value) {
this->value(value);
template <typename T>
Property& operator=(const T& value) {
this->update_value(value);
return *this;
}
PropertyWrapper& operator=(const char* value) {
this->value(value);
Property& operator=(const char* value) {
this->update_value(std::string_view{value});
return *this;
}
template <int N>
PropertyWrapper& operator=(char(value)[N]) {
this->value(value);
Property& operator=(char(value)[N]) {
this->update_value(std::string_view{value, N});
return *this;
}
void trigger_update();
/**
* Increment the value, if numeric, by the given value.
* If the value isn't cast able to T `false` will be returned.
* Note: This action is not atomic.
* @tparam T
* @param value
* @return
*/
template<typename T, std::enable_if_t<std::is_integral<T>::value, int> = 0>
bool increment_by(T value) {
auto current_value = this->as<T>();
if(current_value.has_value()) {
this->update_value(*current_value + value);
return true;
} else {
return false;
}
}
PropertyWrapper(Properties* /* handle */, PropertyData* /* ptr */, std::shared_ptr<PropertyBundle> /* bundle */);
[[nodiscard]] inline Properties* get_handle() { return this->handle; }
private:
Properties* handle = nullptr;
PropertyData* data_ptr = nullptr;
/* Will be initialized by the constructor */
PropertyData* property_data;
std::shared_ptr<PropertyManager> handle;
std::shared_ptr<PropertyBundle> bundle_lock;
void trigger_update();
};
typedef PropertyWrapper Property;
typedef std::function<void(PropertyWrapper&)> PropertyNotifyFn;
class Properties {
friend struct PropertyWrapper;
typedef std::function<void(Property&)> PropertyNotifyFn;
class PropertyManager : public std::enable_shared_from_this<PropertyManager> {
friend struct Property;
public:
Properties();
~Properties();
Properties(const Properties&) = delete;
Properties(Properties&&) = default;
PropertyManager();
~PropertyManager();
std::vector<PropertyWrapper> list_properties(property::flag_type flagMask = (property::flag_type) ~0UL, property::flag_type negatedFlagMask = 0);
std::vector<PropertyWrapper> all_properties();
PropertyManager(const PropertyManager&) = delete;
PropertyManager(PropertyManager&&) = default;
std::vector<Property> list_properties(property::flag_type flagMask = (property::flag_type) ~0UL, property::flag_type negatedFlagMask = 0);
std::vector<Property> all_properties();
template <typename Type>
bool register_property_type() {
void register_property_type() {
constexpr auto type = property::type_from_enum<Type>();
return this->register_property_type(type, property::property_count<Type>());
this->do_register_property_type(type, property::property_count<Type>());
}
template <typename T>
bool hasProperty(T type) { return this->has(property::type_from_enum<T>(), type); }
template <typename T, typename std::enable_if<std::is_enum<T>::value, int>::type = 0>
PropertyWrapper operator[](T type) {
return this->find(property::type_from_enum<T>(), type);
template <typename T, typename std::enable_if<property::type_from_enum_t<T>::supported, int>::type = 0>
[[nodiscard]] Property operator[](T type) {
return this->get(property::type_from_enum_t<T>::type, type);
}
PropertyWrapper operator[](const property::PropertyDescription& type) {
return this->find(type.type_property, type.property_index);
[[nodiscard]] Property operator[](const property::PropertyDescription& type) {
return this->get(type.type_property, type.property_index);
}
PropertyWrapper operator[](const property::PropertyDescription* type) {
return this->find(type->type_property, type->property_index);
[[nodiscard]] Property operator[](const property::PropertyDescription* type) {
return this->get(type->type_property, type->property_index);
}
void registerNotifyHandler(const PropertyNotifyFn &fn){
@ -914,26 +971,28 @@ namespace ts {
}
void triggerAllModified(){
for(auto& prop : this->all_properties())
if(prop.isModified())
for(auto& elm : notifyFunctions)
for(auto& prop : this->all_properties()) {
if(prop.isModified()) {
for(auto& elm : this->notifyFunctions) {
elm(prop);
}
}
}
}
void toggleSave(bool flag) { this->save = flag; }
bool isSaveEnabled(){ return this->save; }
[[nodiscard]] bool isSaveEnabled(){ return this->save; }
PropertyWrapper find(property::PropertyType type, int index);
bool has(property::PropertyType type, int index);
[[nodiscard]] Property get(property::PropertyType type, int index);
[[nodiscard]] bool has(property::PropertyType type, int index);
template <typename T, typename std::enable_if<std::is_enum<T>::value, int>::type = 0>
bool has(T type) { return this->has(property::type_from_enum<T>(), (int) type); }
[[nodiscard]] bool has(T type) { return this->has(property::type_from_enum<T>(), (int) type); }
private:
bool register_property_type(property::PropertyType /* type */, size_t /* length */);
void do_register_property_type(property::PropertyType /* type */, size_t /* length */);
bool save{true};
std::vector<std::function<void(PropertyWrapper&)>> notifyFunctions{};
std::vector<std::function<void(Property&)>> notifyFunctions{};
size_t properties_count{0};
std::vector<std::shared_ptr<PropertyBundle>> properties;

View File

@ -10,16 +10,18 @@ namespace ts {
/* Converter stuff */
template <typename T>
struct converter {
static constexpr bool supported = false;
static constexpr bool supported{false};
static constexpr bool references{false};
static constexpr std::string(*to_string)(const std::any&) = nullptr;
static constexpr T(*from_string_view)(const std::string_view&) = nullptr;
};
#define DECLARE_CONVERTER(type, decode, encode) \
#define DECLARE_CONVERTER(type, decode, encode, references_) \
template <> \
struct converter<type> { \
static constexpr bool supported = true; \
static constexpr bool supported{true}; \
static constexpr bool references{references_}; \
\
static constexpr std::string(*to_string)(const std::any&) = encode; \
static constexpr type(*from_string_view)(const std::string_view&) = decode; \
@ -29,38 +31,38 @@ namespace ts {
#define CONVERTER_METHOD_ENCODE(type, name) std::string name(const std::any& value)
/* helper for primitive types */
#define CONVERTER_PRIMITIVE(type) \
#define CONVERTER_PRIMITIVE(type, references) \
namespace impl { \
CONVERTER_METHOD_DECODE(type, converter_ ##type ##_decode); \
CONVERTER_METHOD_ENCODE(type, converter_ ##type ##_encode); \
} \
DECLARE_CONVERTER(type, ::ts::impl::converter_ ##type ##_decode, ::ts::impl::converter_ ##type ##_encode);
DECLARE_CONVERTER(type, ::ts::impl::converter_ ##type ##_decode, ::ts::impl::converter_ ##type ##_encode, references);
CONVERTER_PRIMITIVE(bool);
CONVERTER_PRIMITIVE(float);
CONVERTER_PRIMITIVE(double);
CONVERTER_PRIMITIVE(long_double);
CONVERTER_PRIMITIVE(bool, false);
CONVERTER_PRIMITIVE(float, false);
CONVERTER_PRIMITIVE(double, false);
CONVERTER_PRIMITIVE(long_double, false);
CONVERTER_PRIMITIVE(int8_t);
CONVERTER_PRIMITIVE(uint8_t);
CONVERTER_PRIMITIVE(int8_t, false);
CONVERTER_PRIMITIVE(uint8_t, false);
CONVERTER_PRIMITIVE(int16_t);
CONVERTER_PRIMITIVE(uint16_t);
CONVERTER_PRIMITIVE(int16_t, false);
CONVERTER_PRIMITIVE(uint16_t, false);
CONVERTER_PRIMITIVE(int32_t);
CONVERTER_PRIMITIVE(uint32_t);
CONVERTER_PRIMITIVE(int32_t, false);
CONVERTER_PRIMITIVE(uint32_t, false);
CONVERTER_PRIMITIVE(int64_t);
CONVERTER_PRIMITIVE(uint64_t);
CONVERTER_PRIMITIVE(int64_t, false);
CONVERTER_PRIMITIVE(uint64_t, false);
#if __x86_64__
CONVERTER_PRIMITIVE(long_long_unsigned_int_t);
CONVERTER_PRIMITIVE(long_long_unsigned_int_t, false);
#endif
typedef std::string std__string;
typedef std::string_view std__string_view;
typedef const char* const_char__;
CONVERTER_PRIMITIVE(std__string);
CONVERTER_PRIMITIVE(std__string_view);
CONVERTER_PRIMITIVE(const_char__);
CONVERTER_PRIMITIVE(std__string, false);
CONVERTER_PRIMITIVE(std__string_view, true);
CONVERTER_PRIMITIVE(const_char__, true);
/* const expr char literal */
template <int length>
@ -78,7 +80,8 @@ namespace ts {
namespace ts { \
template <> \
struct converter<class> { \
static constexpr bool supported = true; \
static constexpr bool supported{true}; \
static constexpr bool references{false}; \
\
static constexpr std::string(*to_string)(const std::any&) = [](const std::any& val) { \
return std::to_string(std::any_cast<class>(val)); \