Initial Commit

This commit is contained in:
2026-03-29 19:38:17 -07:00
commit 9a30e9b426
5 changed files with 293 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
*.o
*.so
*.so.*

45
Makefile Normal file
View File

@@ -0,0 +1,45 @@
CXX ?= g++
VERSION := 0.0.1
TARGET_NOVERSION := liboptions.so
TARGET := $(TARGET_NOVERSION).$(VERSION)
SRCS := $(wildcard *.cpp)
OBJS := $(SRCS:.cpp=.o)
CXXFLAGS := -std=c++17 -fPIC -shared
OPTFLAGS := -O3
DBGFLAGS := -g
LDFLAGS :=
# Default build
all: release
release: CXXFLAGS += $(OPTFLAGS)
release: $(TARGET)
debug: CXXFLAGS += $(DBGFLAGS)
debug: $(TARGET)
# Linker
$(TARGET): $(OBJS)
$(CXX) $(CXXFLAGS) -o $@ $^ $(LDFLAGS)
# Compiler
%.o: %.cpp
$(CXX) $(CXXFLAGS) -c $< -o $@
clean:
rm -f $(OBJS) $(TARGET_NOVERSION) $(TARGET_NOVERSION).*
install:
cp $(TARGET) /usr/lib/$(TARGET)
cp options.h /usr/include/options.h
ln -s /usr/lib/$(TARGET) /usr/lib/$(TARGET_NOVERSION)
uninstall:
unlink /usr/lib/$(TARGET_NOVERSION)
rm /usr/lib/$(TARGET)
rm /usr/include/options.h

63
options.cpp Normal file
View File

@@ -0,0 +1,63 @@
#include "options.h"
using namespace OptionLib;
bool isValidKeyChar(char c) {
return (c >= 'a' && c <= 'z') ||
(c >= '0' && c <= '9') ||
(c == '_' || c == '-');
}
const std::string validateKey(const std::string &key) {
for (char c : key) {
if (!isValidKeyChar(c)) {
throw std::invalid_argument("Options key contains invalid characters, must match [a-z0-9\\_\\-]");
}
}
return key;
}
__OptionBase::__OptionBase(const std::string &key) : key(validateKey(key)) {}
void OptionsBase::registerOption(__OptionBase* opt) {
options.push_back(opt);
}
const std::vector<__OptionBase*>& OptionsBase::getOptions() const {
return options;
}
void OptionsBase::store(const std::string& filename, std::string) {
std::ofstream file(filename);
if(!comment.empty()) {
file << "# " << comment << std::endl;
}
for (auto* opt : getOptions()) {
file << opt->key << "=";
opt->store(file);
file << std::endl;
}
}
void OptionsBase::read(const std::string& filename) {
std::ifstream file(filename);
std::unordered_map<std::string, std::string> kv;
std::string key, value, line;
while (std::getline(file, line)) {
if (line.size() < 1 || line[0] == '#') continue; // skip comment lines TODO make better
auto pos = line.find('=');
if (pos == std::string::npos) continue; // skip invalid lines
std::string key = line.substr(0, pos);
std::string value = line.substr(pos + 1);
kv[key] = value;
}
for (auto* opt : getOptions()) {
if (kv.count(opt->key)) {
opt->setFromString(kv[opt->key]);
}
}
}

63
options.h Normal file
View File

@@ -0,0 +1,63 @@
#ifndef OPTIONS_H
#define OPTIONS_H
#include <vector>
#include <sstream>
#include <fstream>
#include <ostream>
#include <unordered_map>
namespace OptionLib {
class __OptionBase {
public:
const std::string key;
__OptionBase(const std::string &key);
virtual ~__OptionBase() = default;
virtual void setFromString(const std::string& value) = 0;
virtual void store(std::ostream& stream) = 0;
};
class OptionsBase {
private:
std::vector<__OptionBase*> options;
std::string comment = "";
public:
void registerOption(__OptionBase* opt);
const std::vector<__OptionBase*>& getOptions() const;
void store(const std::string& filename, std::string comment="");
void read(const std::string& filename);
};
template<typename T>
class Option : public __OptionBase{
private:
T value;
public:
Option(OptionsBase * parent, std::string name) : __OptionBase(name) {
parent->registerOption(this);
}
Option(OptionsBase * parent, std::string name, T defaultValue) : __OptionBase(name) {
parent->registerOption(this);
set(defaultValue);
}
const T& get() const {return value;}
void set(const T& v) {value = v;}
void setFromString(const std::string& str) override {
std::istringstream iss(str);
iss >> value;
}
void store(std::ostream& stream) {
stream << value;
};
};
}
#endif

118
options_headeronly.h Normal file
View File

@@ -0,0 +1,118 @@
#ifndef OPTIONS_H
#define OPTIONS_H
#include <vector>
#include <sstream>
#include <fstream>
#include <ostream>
#include <unordered_map>
namespace OptionLib {
// TODO There has to be a better way of doing this
bool isValidKeyChar(char c) {
return (c >= 'a' && c <= 'z') ||
(c >= '0' && c <= '9') ||
(c == '_' || c == '-');
}
const std::string validateKey(const std::string &key) {
for (char c : key) {
if (!isValidKeyChar(c)) {
throw std::invalid_argument("Options key contains invalid characters, must match [a-z0-9\\_\\-]");
}
}
return key;
}
class __OptionBase {
public:
const std::string key;
__OptionBase(const std::string &key) : key(validateKey(key)) {}
virtual ~__OptionBase() = default;
virtual void setFromString(const std::string& value) = 0;
virtual void store(ostream& stream) = 0;
};
class OptionsBase {
private:
std::vector<__OptionBase*> options;
std::string comment = "";
public:
void registerOption(__OptionBase* opt) {
options.push_back(opt);
}
const std::vector<__OptionBase*>& getOptions() const {
return options;
}
void store(const std::string& filename, std::string comment="") {
std::ofstream file(filename);
if(!comment.empty()) {
file << "# " << comment << endl;
}
for (auto* opt : getOptions()) {
file << opt->key << "=";
opt->store(file);
file << endl;
}
}
void read(const std::string& filename) {
std::ifstream file(filename);
std::unordered_map<std::string, std::string> kv;
std::string key, value, line;
while (std::getline(file, line)) {
if (line.size() < 1 || line[0] == '#') continue; // skip comment lines TODO make better
auto pos = line.find('=');
if (pos == std::string::npos) continue; // skip invalid lines
std::string key = line.substr(0, pos);
std::string value = line.substr(pos + 1);
kv[key] = value;
}
for (auto* opt : getOptions()) {
if (kv.count(opt->key)) {
opt->setFromString(kv[opt->key]);
}
}
}
};
template<typename T>
class Option : public __OptionBase{
private:
T value;
public:
Option(OptionsBase * parent, std::string name) : __OptionBase(name) {
parent->registerOption(this);
}
Option(OptionsBase * parent, std::string name, T defaultValue) : __OptionBase(name) {
parent->registerOption(this);
set(defaultValue);
}
const T& get() const {return value;}
void set(const T& v) {value = v;}
void setFromString(const std::string& str) override {
std::istringstream iss(str);
iss >> value;
}
void store(ostream& stream) {
stream << value;
};
};
}
#endif