summaryrefslogtreecommitdiff
path: root/cpp
diff options
context:
space:
mode:
Diffstat (limited to 'cpp')
-rw-r--r--cpp/CMakeLists.txt15
-rw-r--r--cpp/pom.cpp171
-rw-r--r--cpp/pom.hpp86
3 files changed, 272 insertions, 0 deletions
diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt
new file mode 100644
index 0000000..f26258d
--- /dev/null
+++ b/cpp/CMakeLists.txt
@@ -0,0 +1,15 @@
+cmake_minimum_required(VERSION 3.0...3.31)
+project(pom)
+
+if (MSVC)
+ add_compile_options(/W4)
+else()
+ add_compile_options(-Wall -Wextra -Wpedantic -Wshadow)
+endif()
+
+
+add_library(pom++ STATIC pom.cpp)
+add_library(pom++-shared SHARED pom.cpp)
+set_target_properties(pom++-shared PROPERTIES OUTPUT_NAME pom++)
+target_include_directories(pom++ PRIVATE ..)
+target_include_directories(pom++-shared PRIVATE ..)
diff --git a/cpp/pom.cpp b/cpp/pom.cpp
new file mode 100644
index 0000000..a883d34
--- /dev/null
+++ b/cpp/pom.cpp
@@ -0,0 +1,171 @@
+#include "pom.hpp"
+
+#include <pom.h>
+#include <cstring>
+#include <cstdlib>
+#include <string>
+#include <istream>
+
+namespace pom {
+
+Configuration::~Configuration() {
+ pom_conf_free(static_cast<pom_conf *>(C));
+}
+
+Configuration::Configuration(): C(nullptr) {}
+
+Configuration::Configuration(void *c): C(c) {}
+
+Error::~Error() { if (m_is_original) free(C); }
+
+Error::Error(void *C_error): C(C_error), m_is_original(true) {
+ const pom_error *next = pom_error_next(static_cast<const pom_error *>(C));
+ m_next = std::unique_ptr<Error>(new Error(static_cast<const void *>(next)));
+}
+
+// const_cast here to avoid creating a separate ConstError type
+Error::Error(const void *C_error): C(const_cast<void *>(C_error)), m_is_original(false) {
+ const pom_error *next = pom_error_next(static_cast<const pom_error *>(C));
+ m_next = std::unique_ptr<Error>(new Error(static_cast<const void *>(next)));
+}
+
+std::string_view Error::file() const noexcept {
+ return pom_error_file(static_cast<const pom_error *>(C));
+}
+
+uint64_t Error::line() const noexcept {
+ return pom_error_line(static_cast<const pom_error *>(C));
+}
+
+std::string_view Error::message() const noexcept {
+ return pom_error_message(static_cast<const pom_error *>(C));
+}
+
+const Error *Error::next() const noexcept {
+ return m_next.get();
+}
+
+const char *Error::what() const noexcept {
+ return m_is_original ? pom_error_to_string(static_cast<pom_error *>(C))
+ : pom_error_message(static_cast<const pom_error *>(C));
+}
+
+std::string_view Error::to_string() noexcept {
+ return what();
+}
+
+Configuration Configuration::section(std::string_view name) const {
+ std::string name_str(name);
+ const pom_conf *C_section = pom_conf_section(
+ static_cast<const pom_conf *>(C), name_str.c_str());
+ pom_conf *C_section_copy = pom_conf_copy(C_section);
+ return Configuration(static_cast<void *>(C_section_copy));
+}
+
+static void *allocator_calloc(void *udata, size_t n, size_t sz) {
+ return static_cast<Allocator *>(udata)->calloc(n, sz);
+}
+
+static void *allocator_realloc(void *udata, void *ptr, size_t sz) {
+ return static_cast<Allocator *>(udata)->realloc(ptr, sz);
+}
+
+static void allocator_free(void *udata, void *ptr) {
+ return static_cast<Allocator *>(udata)->free(ptr);
+}
+
+void Settings::to_C(void *C) const {
+ pom_settings &C_settings = *static_cast<pom_settings *>(C);
+ strcpy(C_settings.error_lang, m_error_lang);
+ if (m_allocator) {
+ C_settings.allocator_udata = m_allocator.get();
+ C_settings.calloc = allocator_calloc;
+ C_settings.realloc = allocator_realloc;
+ C_settings.free = allocator_free;
+ }
+}
+
+static size_t readable_read(void *udata, char *buf, size_t count) {
+ return static_cast<Reader *>(udata)->read(buf, count);
+}
+
+Configuration 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));
+ }
+ pom_error *error;
+ std::string filename_str(filename);
+ void *C_conf = pom_load(&C_settings, filename_str.c_str(), readable_read, &source, &error);
+ if (error) {
+ throw Error(static_cast<void *>(error));
+ }
+ return Configuration(C_conf);
+}
+
+Configuration Configuration::load(std::string_view path, const Settings *settings) {
+ pom_settings C_settings = {};
+ if (settings) {
+ settings->to_C(static_cast<void *>(&C_settings));
+ }
+ pom_error *error;
+ std::string path_str(path);
+ void *C_conf = pom_load_path(&C_settings, path_str.c_str(), &error);
+ if (error) {
+ throw Error(static_cast<void *>(error));
+ }
+ return Configuration(C_conf);
+}
+
+class StringViewReader: public Reader {
+public:
+ explicit StringViewReader(std::string_view v): string(v) {}
+ virtual size_t read(char *buf, size_t count) {
+ count = std::min(count, string.size());
+ memcpy(buf, string.data(), count);
+ return count;
+ }
+private:
+ std::string_view string;
+};
+
+Configuration Configuration::load(std::string_view filename, std::string_view string, const Settings *settings) {
+ StringViewReader reader(string);
+ return load(filename, reader, settings);
+}
+
+class IStreamReader: public Reader {
+public:
+ explicit IStreamReader(std::istream &stream): ifstream(stream) {}
+ virtual size_t read(char *buf, size_t count) {
+ ifstream.read(buf, count);
+ return ifstream.gcount();
+ }
+private:
+ std::istream &ifstream;
+};
+
+
+Configuration Configuration::load(std::string_view filename, std::istream &stream, const Settings *settings) {
+ IStreamReader reader(stream);
+ return load(filename, reader, settings);
+}
+
+void Configuration::merge(const Configuration &other) {
+ pom_conf_merge(static_cast<pom_conf *>(C), static_cast<const pom_conf *>(other.C));
+}
+
+std::optional<std::string_view> Configuration::get(std::string_view key) const {
+ std::string key_str(key);
+ const char *value = pom_conf_get(static_cast<const pom_conf *>(C), key_str.c_str());
+ if (!value)
+ return {};
+ else
+ return value;
+}
+
+std::string_view Configuration::get_or_default(std::string_view key, std::string_view dflt) const {
+ return get(key).value_or(dflt);
+}
+
+} // namespace pom
diff --git a/cpp/pom.hpp b/cpp/pom.hpp
new file mode 100644
index 0000000..e3c66d4
--- /dev/null
+++ b/cpp/pom.hpp
@@ -0,0 +1,86 @@
+#ifndef POM_HPP_
+#define POM_HPP_
+
+#include <exception>
+#include <cstdint>
+#include <string_view>
+#include <memory>
+#include <optional>
+
+namespace pom {
+
+class Error: public std::exception {
+public:
+ ~Error();
+ std::string_view file() const noexcept;
+ uint64_t line() const noexcept;
+ std::string_view message() const noexcept;
+ const Error *next() const noexcept;
+ std::string_view to_string() noexcept;
+ /// You should only call this on the first error in an error list.
+ /// (This can't be enforced with constness because it needs to
+ /// override `std::exception::what`.)
+ virtual const char *what() const noexcept override;
+private:
+ friend class Configuration;
+ friend class Parser;
+ Error() = delete;
+ Error(void *C_error);
+ Error(const void *C_error);
+ void *C;
+ bool m_is_original;
+ std::unique_ptr<const Error> m_next;
+};
+
+class Allocator {
+public:
+ virtual ~Allocator() = 0;
+ virtual void *calloc(size_t, size_t) = 0;
+ virtual void *realloc(void *, size_t) = 0;
+ virtual void free(void *) = 0;
+};
+
+class Settings {
+public:
+ Settings();
+ /// Set allocator.
+ void set_allocator(std::shared_ptr<Allocator> allocator) {
+ m_allocator = allocator;
+ }
+ void set_error_language(std::string_view lang);
+private:
+ 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
+};
+
+class Reader {
+public:
+ virtual size_t read(char *buf, size_t count) = 0;
+};
+
+class Configuration {
+public:
+ Configuration();
+ ~Configuration();
+ std::optional<std::string_view> get(std::string_view key) const;
+ std::string_view get_or_default(std::string_view key, std::string_view dflt) const;
+ Configuration section(std::string_view name) const;
+ void merge(const Configuration &other);
+ static Configuration load(std::string_view filename, Reader &source, const Settings *settings = nullptr);
+ static Configuration load(std::string_view filename, std::istream &stream, const Settings *settings = nullptr);
+ static Configuration load(std::string_view path, const Settings *settings = nullptr);
+ static Configuration load(std::string_view filename, std::string_view string, const Settings *settings = nullptr);
+private:
+ explicit Configuration(void *c);
+ void *C;
+};
+
+
+
+
+} // namespace pom
+
+#endif // POM_HPP_