diff options
author | pommicket <pommicket@gmail.com> | 2025-09-16 17:08:19 -0400 |
---|---|---|
committer | pommicket <pommicket@gmail.com> | 2025-09-16 17:08:19 -0400 |
commit | 62bb1ffdee060819657161e260e75e3e1df017ac (patch) | |
tree | d47eba6dda1528850c869e067944cdfa009198f9 /cpp | |
parent | 7ededc16f36099acbfac457f8e4e268879b0ce62 (diff) |
C++ library fixes
Diffstat (limited to 'cpp')
-rw-r--r-- | cpp/README.md | 5 | ||||
-rw-r--r-- | cpp/examples/all_functions.cpp | 139 | ||||
-rw-r--r-- | cpp/examples/conf.pom | 17 | ||||
-rw-r--r-- | cpp/pom.cpp | 83 | ||||
-rw-r--r-- | cpp/pom.hpp | 22 |
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; }; |