core: add custom data types

This commit is contained in:
Vaxry 2023-12-29 19:35:23 +01:00
parent 8d538588ff
commit a081d90b19
6 changed files with 217 additions and 83 deletions

View file

@ -9,6 +9,8 @@
#include <fstream> #include <fstream>
class CConfigImpl; class CConfigImpl;
struct SConfigDefaultValue;
struct SSpecialCategory;
namespace Hyprlang { namespace Hyprlang {
@ -66,8 +68,30 @@ namespace Hyprlang {
/* typedefs */ /* typedefs */
typedef CParseResult (*PCONFIGHANDLERFUNC)(const char* COMMAND, const char* VALUE); typedef CParseResult (*PCONFIGHANDLERFUNC)(const char* COMMAND, const char* VALUE);
typedef CParseResult (*PCONFIGCUSTOMVALUEHANDLERFUNC)(const char* VALUE, void** data);
typedef void (*PCONFIGCUSTOMVALUEDESTRUCTOR)(void** data);
/* Container for a custom config value type
When creating, pass your handler.
Handler will receive a void** that points to a void* that you can set to your own
thing. Pass a dtor to free whatever you allocated when the custom value type is being released.
data may always be pointing to a nullptr.
*/
class CConfigCustomValueType {
public:
CConfigCustomValueType(PCONFIGCUSTOMVALUEHANDLERFUNC handler_, PCONFIGCUSTOMVALUEDESTRUCTOR dtor_, const char* defaultValue);
~CConfigCustomValueType();
private:
PCONFIGCUSTOMVALUEHANDLERFUNC handler = nullptr;
PCONFIGCUSTOMVALUEDESTRUCTOR dtor = nullptr;
void* data = nullptr;
std::string defaultVal = "";
friend class CConfigValue;
friend class CConfig;
};
struct SConfigValueImpl;
/* Container for a config value */ /* Container for a config value */
class CConfigValue { class CConfigValue {
public: public:
@ -76,9 +100,11 @@ namespace Hyprlang {
CConfigValue(const float value); CConfigValue(const float value);
CConfigValue(const char* value); CConfigValue(const char* value);
CConfigValue(const SVector2D value); CConfigValue(const SVector2D value);
CConfigValue(const CConfigValue&); CConfigValue(CConfigCustomValueType&& value);
CConfigValue(CConfigValue&&); CConfigValue(const CConfigValue&) = delete;
void operator=(const CConfigValue&); CConfigValue(CConfigValue&&) = delete;
CConfigValue(const CConfigValue&&) = delete;
CConfigValue(CConfigValue&) = delete;
~CConfigValue(); ~CConfigValue();
void* dataPtr() const; void* dataPtr() const;
@ -89,21 +115,26 @@ namespace Hyprlang {
case CONFIGDATATYPE_FLOAT: return std::any(*reinterpret_cast<float*>(m_pData)); case CONFIGDATATYPE_FLOAT: return std::any(*reinterpret_cast<float*>(m_pData));
case CONFIGDATATYPE_STR: return std::any(reinterpret_cast<const char*>(m_pData)); case CONFIGDATATYPE_STR: return std::any(reinterpret_cast<const char*>(m_pData));
case CONFIGDATATYPE_VEC2: return std::any(*reinterpret_cast<SVector2D*>(m_pData)); case CONFIGDATATYPE_VEC2: return std::any(*reinterpret_cast<SVector2D*>(m_pData));
case CONFIGDATATYPE_CUSTOM: return std::any(reinterpret_cast<CConfigCustomValueType*>(m_pData)->data);
default: throw; default: throw;
} }
return {}; // unreachable return {}; // unreachable
} }
private: private:
// remember to also edit config.hpp if editing
enum eDataType { enum eDataType {
CONFIGDATATYPE_EMPTY, CONFIGDATATYPE_EMPTY,
CONFIGDATATYPE_INT, CONFIGDATATYPE_INT,
CONFIGDATATYPE_FLOAT, CONFIGDATATYPE_FLOAT,
CONFIGDATATYPE_STR, CONFIGDATATYPE_STR,
CONFIGDATATYPE_VEC2, CONFIGDATATYPE_VEC2,
CONFIGDATATYPE_CUSTOM,
}; };
eDataType m_eType = eDataType::CONFIGDATATYPE_EMPTY; eDataType m_eType = eDataType::CONFIGDATATYPE_EMPTY;
void* m_pData = nullptr; void* m_pData = nullptr;
void defaultFrom(SConfigDefaultValue& ref);
void setFrom(std::any value);
friend class CConfig; friend class CConfig;
}; };
@ -117,7 +148,7 @@ namespace Hyprlang {
/* Add a config value, for example myCategory:myValue. /* Add a config value, for example myCategory:myValue.
This has to be done before commence() This has to be done before commence()
Value provided becomes default */ Value provided becomes default */
void addConfigValue(const char* name, const CConfigValue value); void addConfigValue(const char* name, const CConfigValue& value);
/* Register a handler. Can be called anytime, though not recommended /* Register a handler. Can be called anytime, though not recommended
to do this dynamically */ to do this dynamically */
@ -181,6 +212,7 @@ namespace Hyprlang {
CParseResult configSetValueSafe(const std::string& command, const std::string& value); CParseResult configSetValueSafe(const std::string& command, const std::string& value);
CParseResult parseVariable(const std::string& lhs, const std::string& rhs, bool dynamic = false); CParseResult parseVariable(const std::string& lhs, const std::string& rhs, bool dynamic = false);
void clearState(); void clearState();
void applyDefaultsToCat(SSpecialCategory& cat);
}; };
}; };
#endif #endif

View file

@ -1,4 +1,5 @@
#include "public.hpp" #include "public.hpp"
#include "config.hpp"
#include <string.h> #include <string.h>
using namespace Hyprlang; using namespace Hyprlang;
@ -16,6 +17,9 @@ void CParseResult::setError(const char* err) {
} }
CConfigValue::~CConfigValue() { CConfigValue::~CConfigValue() {
if (m_eType == CONFIGDATATYPE_CUSTOM)
reinterpret_cast<CConfigCustomValueType*>(m_pData)->dtor(&reinterpret_cast<CConfigCustomValueType*>(m_pData)->data);
if (m_pData) if (m_pData)
free(m_pData); free(m_pData);
} }
@ -44,65 +48,10 @@ CConfigValue::CConfigValue(const char* value) {
m_eType = CONFIGDATATYPE_STR; m_eType = CONFIGDATATYPE_STR;
} }
CConfigValue::CConfigValue(const CConfigValue& ref) { CConfigValue::CConfigValue(CConfigCustomValueType&& value) {
m_eType = ref.m_eType; m_pData = calloc(1, sizeof(CConfigCustomValueType));
switch (ref.m_eType) { new (m_pData) CConfigCustomValueType(value);
case eDataType::CONFIGDATATYPE_INT: { m_eType = CONFIGDATATYPE_CUSTOM;
m_pData = calloc(1, sizeof(int64_t));
*reinterpret_cast<int64_t*>(m_pData) = std::any_cast<int64_t>(ref.getValue());
break;
}
case eDataType::CONFIGDATATYPE_FLOAT: {
m_pData = calloc(1, sizeof(float));
*reinterpret_cast<float*>(m_pData) = std::any_cast<float>(ref.getValue());
break;
}
case eDataType::CONFIGDATATYPE_VEC2: {
m_pData = calloc(1, sizeof(SVector2D));
*reinterpret_cast<SVector2D*>(m_pData) = std::any_cast<SVector2D>(ref.getValue());
break;
}
case eDataType::CONFIGDATATYPE_STR: {
auto str = std::any_cast<const char*>(ref.getValue());
m_pData = calloc(1, strlen(str) + 1);
strncpy((char*)m_pData, str, strlen(str));
break;
}
}
}
void CConfigValue::operator=(const CConfigValue& ref) {
m_eType = ref.m_eType;
switch (ref.m_eType) {
case eDataType::CONFIGDATATYPE_INT: {
m_pData = calloc(1, sizeof(int64_t));
*reinterpret_cast<int64_t*>(m_pData) = std::any_cast<int64_t>(ref.getValue());
break;
}
case eDataType::CONFIGDATATYPE_FLOAT: {
m_pData = calloc(1, sizeof(float));
*reinterpret_cast<float*>(m_pData) = std::any_cast<float>(ref.getValue());
break;
}
case eDataType::CONFIGDATATYPE_VEC2: {
m_pData = calloc(1, sizeof(SVector2D));
*reinterpret_cast<SVector2D*>(m_pData) = std::any_cast<SVector2D>(ref.getValue());
break;
}
case eDataType::CONFIGDATATYPE_STR: {
auto str = std::any_cast<const char*>(ref.getValue());
m_pData = calloc(1, strlen(str) + 1);
strncpy((char*)m_pData, str, strlen(str));
break;
}
}
}
CConfigValue::CConfigValue(CConfigValue&& ref) {
m_pData = ref.dataPtr();
m_eType = ref.m_eType;
ref.m_eType = eDataType::CONFIGDATATYPE_EMPTY;
ref.m_pData = nullptr;
} }
CConfigValue::CConfigValue() { CConfigValue::CConfigValue() {
@ -112,3 +61,91 @@ CConfigValue::CConfigValue() {
void* CConfigValue::dataPtr() const { void* CConfigValue::dataPtr() const {
return m_pData; return m_pData;
} }
CConfigCustomValueType::CConfigCustomValueType(PCONFIGCUSTOMVALUEHANDLERFUNC handler_, PCONFIGCUSTOMVALUEDESTRUCTOR dtor_, const char* def) {
handler = handler_;
dtor = dtor_;
defaultVal = def;
}
CConfigCustomValueType::~CConfigCustomValueType() {
dtor(&data);
}
void CConfigValue::defaultFrom(SConfigDefaultValue& ref) {
m_eType = (CConfigValue::eDataType)ref.type;
switch (m_eType) {
case CONFIGDATATYPE_FLOAT: {
if (!m_pData)
m_pData = calloc(1, sizeof(float));
*reinterpret_cast<float*>(m_pData) = std::any_cast<float>(ref.data);
break;
}
case CONFIGDATATYPE_INT: {
if (!m_pData)
m_pData = calloc(1, sizeof(int64_t));
*reinterpret_cast<int64_t*>(m_pData) = std::any_cast<int64_t>(ref.data);
break;
}
case CONFIGDATATYPE_STR: {
if (!m_pData)
free(m_pData);
std::string str = std::any_cast<std::string>(ref.data);
m_pData = calloc(1, str.length() + 1);
strncpy((char*)m_pData, str.c_str(), str.length());
break;
}
case CONFIGDATATYPE_VEC2: {
if (!m_pData)
m_pData = calloc(1, sizeof(SVector2D));
*reinterpret_cast<SVector2D*>(m_pData) = std::any_cast<SVector2D>(ref.data);
break;
}
case CONFIGDATATYPE_CUSTOM: {
if (!m_pData)
m_pData = calloc(1, sizeof(CConfigCustomValueType));
CConfigCustomValueType* type = reinterpret_cast<CConfigCustomValueType*>(m_pData);
type->handler = ref.handler;
type->dtor = ref.dtor;
type->handler(std::any_cast<std::string>(ref.data).c_str(), &type->data);
break;
}
default: {
throw "bad defaultFrom type";
}
}
}
void CConfigValue::setFrom(std::any value) {
switch (m_eType) {
case CONFIGDATATYPE_FLOAT: {
*reinterpret_cast<float*>(m_pData) = std::any_cast<float>(value);
break;
}
case CONFIGDATATYPE_INT: {
*reinterpret_cast<int64_t*>(m_pData) = std::any_cast<int64_t>(value);
break;
}
case CONFIGDATATYPE_STR: {
free(m_pData);
std::string str = std::any_cast<std::string>(value);
m_pData = calloc(1, str.length() + 1);
strncpy((char*)m_pData, str.c_str(), str.length());
break;
}
case CONFIGDATATYPE_VEC2: {
*reinterpret_cast<SVector2D*>(m_pData) = std::any_cast<SVector2D>(value);
break;
}
// case CONFIGDATATYPE_CUSTOM: {
// CConfigCustomValueType* type = reinterpret_cast<CConfigCustomValueType*>(m_pData);
// type->handler = ref.handler;
// type->dtor = ref.dtor;
// type->handler(std::any_cast<std::string>(ref.data).c_str(), &type->data);
// break;
// }
default: {
throw "bad defaultFrom type";
}
}
}

View file

@ -52,11 +52,19 @@ CConfig::~CConfig() {
delete impl; delete impl;
} }
void CConfig::addConfigValue(const char* name, const CConfigValue value) { void CConfig::addConfigValue(const char* name, const CConfigValue& value) {
if (m_bCommenced) if (m_bCommenced)
throw "Cannot addConfigValue after commence()"; throw "Cannot addConfigValue after commence()";
impl->defaultValues[std::string{name}] = value; if ((eDataType)value.m_eType != CONFIGDATATYPE_CUSTOM && (eDataType)value.m_eType != CONFIGDATATYPE_STR)
impl->defaultValues.emplace(name, SConfigDefaultValue{value.getValue(), (eDataType)value.m_eType});
else if ((eDataType)value.m_eType == CONFIGDATATYPE_STR)
impl->defaultValues.emplace(name, SConfigDefaultValue{std::string{std::any_cast<const char*>(value.getValue())}, (eDataType)value.m_eType});
else
impl->defaultValues.emplace(name,
SConfigDefaultValue{reinterpret_cast<CConfigCustomValueType*>(value.m_pData)->defaultVal, (eDataType)value.m_eType,
reinterpret_cast<CConfigCustomValueType*>(value.m_pData)->handler,
reinterpret_cast<CConfigCustomValueType*>(value.m_pData)->dtor});
} }
void CConfig::addSpecialConfigValue(const char* cat, const char* name, const CConfigValue value) { void CConfig::addSpecialConfigValue(const char* cat, const char* name, const CConfigValue value) {
@ -65,7 +73,15 @@ void CConfig::addSpecialConfigValue(const char* cat, const char* name, const CCo
if (IT == impl->specialCategoryDescriptors.end()) if (IT == impl->specialCategoryDescriptors.end())
throw "No such category"; throw "No such category";
IT->get()->defaultValues[std::string{name}] = value; if ((eDataType)value.m_eType != CONFIGDATATYPE_CUSTOM && (eDataType)value.m_eType != CONFIGDATATYPE_STR)
IT->get()->defaultValues.emplace(name, SConfigDefaultValue{value.getValue(), (eDataType)value.m_eType});
else if ((eDataType)value.m_eType == CONFIGDATATYPE_STR)
IT->get()->defaultValues.emplace(name, SConfigDefaultValue{std::string{std::any_cast<const char*>(value.getValue())}, (eDataType)value.m_eType});
else
IT->get()->defaultValues.emplace(name,
SConfigDefaultValue{reinterpret_cast<CConfigCustomValueType*>(value.m_pData)->defaultVal, (eDataType)value.m_eType,
reinterpret_cast<CConfigCustomValueType*>(value.m_pData)->handler,
reinterpret_cast<CConfigCustomValueType*>(value.m_pData)->dtor});
} }
void CConfig::addSpecialCategory(const char* name, SSpecialCategoryOptions options) { void CConfig::addSpecialCategory(const char* name, SSpecialCategoryOptions options) {
@ -85,16 +101,16 @@ void CConfig::addSpecialCategory(const char* name, SSpecialCategoryOptions optio
} }
} }
void SSpecialCategory::applyDefaults() { void CConfig::applyDefaultsToCat(SSpecialCategory& cat) {
for (auto& [k, v] : descriptor->defaultValues) { for (auto& [k, v] : cat.descriptor->defaultValues) {
values[k] = v; cat.values[k].defaultFrom(v);
} }
} }
void CConfig::commence() { void CConfig::commence() {
m_bCommenced = true; m_bCommenced = true;
for (auto& [k, v] : impl->defaultValues) { for (auto& [k, v] : impl->defaultValues) {
impl->values[k] = v; impl->values[k].defaultFrom(v);
} }
} }
@ -240,7 +256,7 @@ CParseResult CConfig::configSetValueSafe(const std::string& command, const std::
PCAT->key = sc->key; PCAT->key = sc->key;
addSpecialConfigValue(sc->name.c_str(), sc->key.c_str(), CConfigValue("0")); addSpecialConfigValue(sc->name.c_str(), sc->key.c_str(), CConfigValue("0"));
PCAT->applyDefaults(); applyDefaultsToCat(*PCAT);
VALUEIT = PCAT->values.find(valueName.substr(sc->name.length() + 1)); VALUEIT = PCAT->values.find(valueName.substr(sc->name.length() + 1));
@ -267,7 +283,7 @@ CParseResult CConfig::configSetValueSafe(const std::string& command, const std::
switch (VALUEIT->second.m_eType) { switch (VALUEIT->second.m_eType) {
case CConfigValue::eDataType::CONFIGDATATYPE_INT: { case CConfigValue::eDataType::CONFIGDATATYPE_INT: {
try { try {
VALUEIT->second = {configStringToInt(value)}; VALUEIT->second.setFrom(configStringToInt(value));
} catch (std::exception& e) { } catch (std::exception& e) {
result.setError(std::format("failed parsing an int: {}", e.what())); result.setError(std::format("failed parsing an int: {}", e.what()));
return result; return result;
@ -276,7 +292,7 @@ CParseResult CConfig::configSetValueSafe(const std::string& command, const std::
} }
case CConfigValue::eDataType::CONFIGDATATYPE_FLOAT: { case CConfigValue::eDataType::CONFIGDATATYPE_FLOAT: {
try { try {
VALUEIT->second = {std::stof(value)}; VALUEIT->second.setFrom(std::stof(value));
} catch (std::exception& e) { } catch (std::exception& e) {
result.setError(std::format("failed parsing a float: {}", e.what())); result.setError(std::format("failed parsing a float: {}", e.what()));
return result; return result;
@ -294,7 +310,7 @@ CParseResult CConfig::configSetValueSafe(const std::string& command, const std::
if (LHS.contains(" ") || RHS.contains(" ")) if (LHS.contains(" ") || RHS.contains(" "))
throw std::runtime_error("too many args"); throw std::runtime_error("too many args");
VALUEIT->second = {SVector2D{std::stof(LHS), std::stof(RHS)}}; VALUEIT->second.setFrom(SVector2D{std::stof(LHS), std::stof(RHS)});
} catch (std::exception& e) { } catch (std::exception& e) {
result.setError(std::format("failed parsing a vec2: {}", e.what())); result.setError(std::format("failed parsing a vec2: {}", e.what()));
return result; return result;
@ -302,7 +318,11 @@ CParseResult CConfig::configSetValueSafe(const std::string& command, const std::
break; break;
} }
case CConfigValue::eDataType::CONFIGDATATYPE_STR: { case CConfigValue::eDataType::CONFIGDATATYPE_STR: {
VALUEIT->second = {value.c_str()}; VALUEIT->second.setFrom(value);
break;
}
case CConfigValue::eDataType::CONFIGDATATYPE_CUSTOM: {
reinterpret_cast<CConfigCustomValueType*>(VALUEIT->second.m_pData)->handler(value.c_str(), &reinterpret_cast<CConfigCustomValueType*>(VALUEIT->second.m_pData)->data);
break; break;
} }
default: { default: {
@ -468,10 +488,10 @@ CParseResult CConfig::parse() {
clearState(); clearState();
for (auto& [k, v] : impl->defaultValues) { for (auto& [k, v] : impl->defaultValues) {
impl->values.at(k) = v; impl->values.at(k).defaultFrom(v);
} }
for (auto& sc : impl->specialCategories) { for (auto& sc : impl->specialCategories) {
sc->applyDefaults(); applyDefaultsToCat(*sc);
} }
CParseResult fileParseResult = parseFile(impl->path); CParseResult fileParseResult = parseFile(impl->path);

View file

@ -16,10 +16,30 @@ struct SVariable {
std::vector<std::string> linesContainingVar; // for dynamic updates std::vector<std::string> linesContainingVar; // for dynamic updates
}; };
// remember to also edit CConfigValue if editing
enum eDataType {
CONFIGDATATYPE_EMPTY,
CONFIGDATATYPE_INT,
CONFIGDATATYPE_FLOAT,
CONFIGDATATYPE_STR,
CONFIGDATATYPE_VEC2,
CONFIGDATATYPE_CUSTOM,
};
// CUSTOM is stored as STR!!
struct SConfigDefaultValue {
std::any data;
eDataType type = CONFIGDATATYPE_EMPTY;
// this sucks but I have no better idea
Hyprlang::PCONFIGCUSTOMVALUEHANDLERFUNC handler = nullptr;
Hyprlang::PCONFIGCUSTOMVALUEDESTRUCTOR dtor = nullptr;
};
struct SSpecialCategoryDescriptor { struct SSpecialCategoryDescriptor {
std::string name = ""; std::string name = "";
std::string key = ""; std::string key = "";
std::unordered_map<std::string, Hyprlang::CConfigValue> defaultValues; std::unordered_map<std::string, SConfigDefaultValue> defaultValues;
bool dontErrorOnMissing = false; bool dontErrorOnMissing = false;
}; };
@ -38,7 +58,7 @@ class CConfigImpl {
std::string path = ""; std::string path = "";
std::unordered_map<std::string, Hyprlang::CConfigValue> values; std::unordered_map<std::string, Hyprlang::CConfigValue> values;
std::unordered_map<std::string, Hyprlang::CConfigValue> defaultValues; std::unordered_map<std::string, SConfigDefaultValue> defaultValues;
std::vector<SHandler> handlers; std::vector<SHandler> handlers;
std::vector<SVariable> variables; std::vector<SVariable> variables;
std::vector<SVariable> envVariables; std::vector<SVariable> envVariables;

View file

@ -13,6 +13,8 @@ testEnv = $SHELL
source = ./colors.conf source = ./colors.conf
customType = abc
testCategory { testCategory {
testValueInt = 123456 testValueInt = 123456
testValueHex = 0xF testValueHex = 0xF

View file

@ -48,6 +48,24 @@ static Hyprlang::CParseResult handleSource(const char* COMMAND, const char* VALU
return pConfig->parseFile(PATH); return pConfig->parseFile(PATH);
} }
static Hyprlang::CParseResult handleCustomValueSet(const char* VALUE, void** data) {
if (!*data)
*data = calloc(1, sizeof(int64_t));
std::string V = VALUE;
if (V == "abc")
*reinterpret_cast<int64_t*>(*data) = 1;
else
*reinterpret_cast<int64_t*>(*data) = 2;
Hyprlang::CParseResult result;
return result;
}
static void handleCustomValueDestroy(void** data) {
if (*data)
free(*data);
}
int main(int argc, char** argv, char** envp) { int main(int argc, char** argv, char** envp) {
int ret = 0; int ret = 0;
@ -80,6 +98,7 @@ int main(int argc, char** argv, char** envp) {
config.addConfigValue("myColors:pink", 0L); config.addConfigValue("myColors:pink", 0L);
config.addConfigValue("myColors:green", 0L); config.addConfigValue("myColors:green", 0L);
config.addConfigValue("myColors:random", 0L); config.addConfigValue("myColors:random", 0L);
config.addConfigValue("customType", {Hyprlang::CConfigCustomValueType{&handleCustomValueSet, &handleCustomValueDestroy, "def"}});
config.registerHandler(&handleDoABarrelRoll, "doABarrelRoll", {false}); config.registerHandler(&handleDoABarrelRoll, "doABarrelRoll", {false});
config.registerHandler(&handleFlagsTest, "flags", {true}); config.registerHandler(&handleFlagsTest, "flags", {true});
@ -159,6 +178,10 @@ int main(int argc, char** argv, char** envp) {
EXPECT(std::any_cast<int64_t>(config.getConfigValue("myColors:green")), 0xFF14f014L); EXPECT(std::any_cast<int64_t>(config.getConfigValue("myColors:green")), 0xFF14f014L);
EXPECT(std::any_cast<int64_t>(config.getConfigValue("myColors:random")), 0xFFFF1337L); EXPECT(std::any_cast<int64_t>(config.getConfigValue("myColors:random")), 0xFFFF1337L);
// test custom type
std::cout << " → Testing custom types\n";
EXPECT(*reinterpret_cast<int64_t*>(std::any_cast<void*>(config.getConfigValue("customType"))), 1L);
} catch (const char* e) { } catch (const char* e) {
std::cout << Colors::RED << "Error: " << Colors::RESET << e << "\n"; std::cout << Colors::RED << "Error: " << Colors::RESET << e << "\n";
return 1; return 1;