Reworked the property system and fixed a crash
This commit is contained in:
parent
b26132fa54
commit
465975e9ca
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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};
|
||||
|
@ -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 {
|
||||
|
339
src/Properties.h
339
src/Properties.h
@ -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;
|
||||
|
@ -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)); \
|
||||
|
Loading…
x
Reference in New Issue
Block a user