summaryrefslogtreecommitdiff
path: root/cpp
diff options
context:
space:
mode:
authorpommicket <pommicket@gmail.com>2025-09-16 17:08:19 -0400
committerpommicket <pommicket@gmail.com>2025-09-16 17:08:19 -0400
commit62bb1ffdee060819657161e260e75e3e1df017ac (patch)
treed47eba6dda1528850c869e067944cdfa009198f9 /cpp
parent7ededc16f36099acbfac457f8e4e268879b0ce62 (diff)
C++ library fixes
Diffstat (limited to 'cpp')
-rw-r--r--cpp/README.md5
-rw-r--r--cpp/examples/all_functions.cpp139
-rw-r--r--cpp/examples/conf.pom17
-rw-r--r--cpp/pom.cpp83
-rw-r--r--cpp/pom.hpp22
5 files changed, 237 insertions, 29 deletions
diff --git a/cpp/README.md b/cpp/README.md
new file mode 100644
index 0000000..5367cfe
--- /dev/null
+++ b/cpp/README.md
@@ -0,0 +1,5 @@
+# libpom++
+
+C++ parser for the [POM configuration language](https://pom.computer).
+
+Requires at least C++17.
diff --git a/cpp/examples/all_functions.cpp b/cpp/examples/all_functions.cpp
new file mode 100644
index 0000000..326ae39
--- /dev/null
+++ b/cpp/examples/all_functions.cpp
@@ -0,0 +1,139 @@
+// Demonstrates almost all of libpom++'s API
+
+#include <pom.hpp>
+#include <fcntl.h>
+#include <unistd.h>
+#include <iostream>
+
+class FdReader: public pom::Reader {
+public:
+ explicit FdReader(const char *path) {
+ m_fd = open(path, O_RDONLY);
+ if (m_fd == -1) {
+ throw "file doesn't exist";
+ }
+ }
+ ~FdReader() { close(m_fd); }
+ size_t read(char *buf, size_t size) {
+ size_t total_read = 0;
+ while (true) {
+ // must call read in a loop to fill buf up as much as possible!
+ // (read isn't guaranteed to read len bytes even if it could)
+ ssize_t ret = ::read(m_fd, buf, size);
+ if (ret < 0) {
+ // read error
+ throw "read error";
+ } else if (ret == 0) {
+ // end-of-file
+ break;
+ } else {
+ total_read += ret;
+ buf += ret;
+ size -= ret;
+ }
+ }
+ return total_read;
+ }
+private:
+ int m_fd;
+};
+
+int main(void) {
+ try {
+ // ordinary usage: load from a path
+ pom::Configuration conf("conf.pom");
+ // get a key
+ std::optional<std::string> indentation_type = conf.get("indentation-type");
+ std::cout << "Indentation type: " << indentation_type.value_or("(none)") << "\n";
+
+ // load configuration with custom settings and a custom reader
+ pom::Settings settings = {};
+ settings.set_error_language("fr"); // erreurs en français
+ FdReader reader("conf.pom");
+ conf = pom::Configuration(
+ "conf.pom", // file name for error messages
+ reader, // object used to read file
+ &settings // settings
+ );
+
+ // nicer way of doing what we did above
+ std::cout << "Indentation type: "
+ << conf.get_or_default("indentation-type", "(none)")
+ << "\n";
+
+ // parse value as signed integer
+ std::optional<int64_t> tab_size = conf.get_int("tab-size");
+ std::cout << "tab size: ";
+ if (tab_size.has_value())
+ std::cout << tab_size.value();
+ else
+ std::cout << "(none set)";
+ std::cout << "\n";
+
+ // parse value as unsigned integer, use default of 2
+ std::cout << "padding pixels: "
+ << conf.get_uint_or_default("padding-pixels", 2)
+ << "\n";
+
+ // parse value as double
+ std::cout << "font size: "
+ << conf.get_float_or_default("font-size", 12.5)
+ << "\n";
+
+ // parse value as boolean
+ std::cout << "show line numbers: "
+ << (conf.get_bool_or_default("show-line-numbers", true) ? "yes" : "no")
+ << "\n";
+
+ // extract section out of configuration
+ pom::Configuration file_extensions = conf.section("file-extensions");
+
+ // parse value as list
+ std::vector<std::string> Cpp_extensions = file_extensions.get_list_or_default("Cpp", {});
+ if (!Cpp_extensions.empty()) {
+ for (const std::string &extension: Cpp_extensions)
+ std::cout << "C++ extension: " << extension << "\n";
+ } else {
+ printf("no extensions defined for C++\n");
+ }
+
+ // iterate over keys in section
+ pom::Configuration plugins = conf.section("plug-in");
+ for (std::string &key: plugins.keys()) {
+ pom::Configuration plugin = plugins.section(key);
+ auto path = plugin.get_or_default("path", "(none)");
+ bool enabled = plugin.get_bool_or_default("enabled", true);
+ // get location where key was defined
+ pom::Location location = plugins.location(key).value();
+
+ std::cout << location.file() << ":" << location.line()
+ << ": plug-in " << key
+ << " (path = " << path <<
+ ", enabled = " << enabled << ")\n";
+ }
+
+ // load config from string
+ pom::Configuration overrides("<built-in overrides>", "tab-size = 12");
+
+ // merge configurations
+ conf.merge(overrides);
+
+ // iterate over items (key-value pairs) in configuration
+ for (const auto &item: conf.items()) {
+ if (item->key().find('b') != std::string_view::npos)
+ std::cout << item->key() << ": " << item->value() << "\n";
+ }
+
+ // iterate over all the keys which haven't been accessed directly
+ for (const std::string &key: conf.unread_keys()) {
+ std::cout << "unrecognized key " << key << "\n";
+ }
+ } catch (pom::Error &error) { // error from libpom++
+ std::cerr << error;
+ return EXIT_FAILURE;
+ } catch (const char *error) { // error from FdReader
+ std::cerr << error << "\n";
+ return EXIT_FAILURE;
+ }
+ return 0;
+}
diff --git a/cpp/examples/conf.pom b/cpp/examples/conf.pom
new file mode 100644
index 0000000..82e1abd
--- /dev/null
+++ b/cpp/examples/conf.pom
@@ -0,0 +1,17 @@
+indentation-type = tabs
+show-line-numbers = yes
+tab-size = 4
+font-size = "18"
+
+[file-extensions]
+C = .c, .h
+Cpp = .cpp, .hpp, .cc, .hh
+
+[plug-in.edit-over-ssh]
+path = ~/misc/edit-over-ssh.so
+enabled = yes
+
+[plug-in.wrap-text]
+path = ~/misc/wrap_text_v3.5.7.so
+enabled = no
+
diff --git a/cpp/pom.cpp b/cpp/pom.cpp
index 7c2651c..ffa70a2 100644
--- a/cpp/pom.cpp
+++ b/cpp/pom.cpp
@@ -5,6 +5,7 @@
#include <cstdlib>
#include <string>
#include <istream>
+#include <iostream>
namespace pom {
@@ -55,6 +56,7 @@ std::ostream &operator<<(std::ostream &o, Error &e) {
}
Configuration Configuration::section(std::string_view name) const {
+ if (!C) return {};
std::string name_str(name);
const pom_conf *C_section = pom_conf_section(
static_cast<const pom_conf *>(C), name_str.c_str());
@@ -95,8 +97,8 @@ static size_t readable_read(void *udata, char *buf, size_t count) {
return static_cast<Reader *>(udata)->read(buf, count);
}
-Configuration::Configuration(std::string_view filename, Reader &source, const Settings *settings) {
- pom_settings C_settings = {};
+void Configuration::load(std::string_view filename, Reader &source, const Settings *settings) {
+ pom_settings C_settings = {};
if (settings) {
settings->to_C(static_cast<void *>(&C_settings));
}
@@ -108,8 +110,12 @@ Configuration::Configuration(std::string_view filename, Reader &source, const Se
}
}
+Configuration::Configuration(std::string_view filename, Reader &source, const Settings *settings) {
+ load(filename, source, settings);
+}
+
Configuration::Configuration(std::string_view path, const Settings *settings) {
- pom_settings C_settings = {};
+ pom_settings C_settings = {};
if (settings) {
settings->to_C(static_cast<void *>(&C_settings));
}
@@ -135,7 +141,7 @@ private:
Configuration::Configuration(std::string_view filename, std::string_view string, const Settings *settings) {
StringViewReader reader(string);
- Configuration(filename, reader, settings);
+ load(filename, reader, settings);
}
class IStreamReader: public Reader {
@@ -152,7 +158,7 @@ private:
Configuration::Configuration(std::string_view filename, std::istream &stream, const Settings *settings) {
IStreamReader reader(stream);
- Configuration(filename, reader, settings);
+ load(filename, reader, settings);
}
void Configuration::merge(const Configuration &other) {
@@ -183,6 +189,7 @@ std::string Configuration::get_or_default(std::string_view key, std::string_view
}
std::vector<std::string> Configuration::unread_keys() const {
+ if (!C) return {};
std::vector<std::string> unread;
pom_unread_key_iter *iter = nullptr;
const char *key;
@@ -193,6 +200,7 @@ std::vector<std::string> Configuration::unread_keys() const {
}
std::vector<std::string> Configuration::keys() const {
+ if (!C) return {};
std::vector<std::string> keys;
pom_key_iter *iter = nullptr;
const char *key;
@@ -220,6 +228,7 @@ public:
};
std::vector<std::shared_ptr<Item>> Configuration::items() const {
+ if (!C) return {};
std::vector<std::shared_ptr<Item>> items;
pom_item_iter *iter = nullptr;
const pom_item *item;
@@ -230,6 +239,7 @@ std::vector<std::shared_ptr<Item>> Configuration::items() const {
}
std::optional<int64_t> Configuration::get_int(std::string_view key) const {
+ if (!C) return {};
std::string key_str(key);
if (!pom_conf_has(static_cast<const pom_conf *>(C), key_str.c_str())) {
return {};
@@ -242,15 +252,11 @@ std::optional<int64_t> Configuration::get_int(std::string_view key) const {
}
int64_t Configuration::get_int_or_default(std::string_view key, int64_t dflt) const {
- std::string key_str(key);
- int64_t value;
- pom_error *error = pom_conf_get_int_or_default(static_cast<const pom_conf *>(C), key_str.c_str(), &value, dflt);
- if (error)
- throw Error(error);
- return value;
+ return get_int(key).value_or(dflt);
}
std::optional<uint64_t> Configuration::get_uint(std::string_view key) const {
+ if (!C) return {};
std::string key_str(key);
if (!pom_conf_has(static_cast<const pom_conf *>(C), key_str.c_str())) {
return {};
@@ -263,15 +269,11 @@ std::optional<uint64_t> Configuration::get_uint(std::string_view key) const {
}
uint64_t Configuration::get_uint_or_default(std::string_view key, uint64_t dflt) const {
- std::string key_str(key);
- uint64_t value;
- pom_error *error = pom_conf_get_uint_or_default(static_cast<const pom_conf *>(C), key_str.c_str(), &value, dflt);
- if (error)
- throw Error(error);
- return value;
+ return get_uint(key).value_or(dflt);
}
std::optional<double> Configuration::get_float(std::string_view key) const {
+ if (!C) return {};
std::string key_str(key);
if (!pom_conf_has(static_cast<const pom_conf *>(C), key_str.c_str())) {
return {};
@@ -284,15 +286,11 @@ std::optional<double> Configuration::get_float(std::string_view key) const {
}
double Configuration::get_float_or_default(std::string_view key, double dflt) const {
- std::string key_str(key);
- double value;
- pom_error *error = pom_conf_get_float_or_default(static_cast<const pom_conf *>(C), key_str.c_str(), &value, dflt);
- if (error)
- throw Error(error);
- return value;
+ return get_float(key).value_or(dflt);
}
std::optional<bool> Configuration::get_bool(std::string_view key) const {
+ if (!C) return {};
std::string key_str(key);
if (!pom_conf_has(static_cast<const pom_conf *>(C), key_str.c_str())) {
return {};
@@ -305,15 +303,30 @@ std::optional<bool> Configuration::get_bool(std::string_view key) const {
}
bool Configuration::get_bool_or_default(std::string_view key, bool dflt) const {
+ return get_bool(key).value_or(dflt);
+}
+
+std::optional<std::vector<std::string>> Configuration::get_list(std::string_view key) const {
+ if (!C) return {};
std::string key_str(key);
- bool value;
- pom_error *error = pom_conf_get_bool_or_default(static_cast<const pom_conf *>(C), key_str.c_str(), &value, dflt);
- if (error)
- throw Error(error);
- return value;
+ char **list = pom_conf_get_list(static_cast<const pom_conf *>(C), key_str.c_str());
+ std::vector<std::string> vec;
+ for (size_t i = 0; list[i]; i++)
+ vec.emplace_back(list[i]);
+ free(list);
+ return vec;
+}
+
+std::vector<std::string> Configuration::get_list_or_default(std::string_view key, const std::vector<std::string> &dflt) const {
+ auto list = get_list(key);
+ if (list.has_value())
+ return list.value();
+ else
+ return dflt;
}
Configuration &Configuration::operator=(const Configuration &other) {
+ pom_conf_free(static_cast<pom_conf *>(C));
C = static_cast<void *>(pom_conf_copy(static_cast<const pom_conf *>(other.C)));
return *this;
}
@@ -324,5 +337,19 @@ std::ostream &operator<<(std::ostream &o, const Configuration &conf) {
return o;
}
+std::optional<Location> Configuration::location(std::string_view key) const {
+ if (!C) return {};
+ const char *file;
+ uint64_t line;
+ std::string key_str(key);
+ if (pom_conf_location(static_cast<const pom_conf *>(C), key_str.c_str(), &file, &line))
+ return Location(file, line);
+ else
+ return {};
+}
+
+std::ostream &operator<<(std::ostream &o, const Location &location) {
+ return o << location.file() << ":" << location.line();
+}
} // namespace pom
diff --git a/cpp/pom.hpp b/cpp/pom.hpp
index f9d7475..69a979d 100644
--- a/cpp/pom.hpp
+++ b/cpp/pom.hpp
@@ -53,11 +53,13 @@ public:
}
void set_error_language(std::string_view lang);
private:
+ void check_version() const;
void to_C(void *C) const;
friend class Configuration;
char m_error_lang[16] = {};
std::shared_ptr<Allocator> m_allocator;
- uint32_t version = 1; // future-proofing
+ // to allow for future extensions without breaking backwards compatibility
+ const uint32_t version = 1;
};
class Reader {
@@ -78,6 +80,20 @@ public:
virtual uint64_t line() const noexcept = 0;
};
+class Location {
+public:
+ inline std::string_view file() const { return m_file; }
+ inline uint64_t line() const { return m_line; }
+private:
+ inline Location(std::string file, uint64_t line):
+ m_file(file), m_line(line) {}
+ friend class Configuration;
+ std::string m_file;
+ uint64_t m_line;
+ void *_reserved[4] = {};
+};
+std::ostream &operator<<(std::ostream &, const Location &);
+
class Configuration {
public:
Configuration();
@@ -88,6 +104,7 @@ public:
Configuration(std::string_view path, const Settings *settings = nullptr);
Configuration(std::string_view filename, std::string_view string, const Settings *settings = nullptr);
~Configuration();
+ std::optional<Location> location(std::string_view key) const;
std::optional<std::string> get(std::string_view key) const;
std::string get_or_default(std::string_view key, std::string_view dflt) const;
std::optional<int64_t> get_int(std::string_view key) const;
@@ -98,12 +115,15 @@ public:
double get_float_or_default(std::string_view key, double dflt) const;
std::optional<bool> get_bool(std::string_view key) const;
bool get_bool_or_default(std::string_view key, bool dflt) const;
+ std::optional<std::vector<std::string>> get_list(std::string_view key) const;
+ std::vector<std::string> get_list_or_default(std::string_view key, const std::vector<std::string> &dflt) const;
Configuration section(std::string_view name) const;
std::vector<std::string> unread_keys() const;
std::vector<std::string> keys() const;
std::vector<std::shared_ptr<Item>> items() const;
void merge(const Configuration &other);
private:
+ void load(std::string_view filename, Reader &source, const Settings *settings);
explicit Configuration(void *c): C(c) {}
void *C;
};