.. _program_listing_file_mcfp_mcfp.hpp: Program Listing for File mcfp.hpp ================================= |exhale_lsh| :ref:`Return to documentation for file ` (``mcfp/mcfp.hpp``) .. |exhale_lsh| unicode:: U+021B0 .. UPWARDS ARROW WITH TIP LEFTWARDS .. code-block:: cpp /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2022-2025 Maarten L. hekkelman * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #pragma once // IWYU pragma: begin_exports #include "mcfp/error.hpp" #include "mcfp/options.hpp" #include "mcfp/sections.hpp" #include "mcfp/text.hpp" // IWYU pragma: end_exports #include #include #include #include #include #include #include #include #include #include namespace mcfp { // -------------------------------------------------------------------- class config { public: void set_usage(std::string usage) { m_usage = std::move(usage); } template requires(std::is_base_of_v and ...) config &init(std::string usage, Options &&...options) { using std::operator""sv; m_sections.clear(); m_usage = std::move(usage); m_ignore_unknown = false; add_section("", std::forward(options)...); for (auto &f : get_section_factories()) { std::unique_ptr
sp(f->create()); auto si = std::lower_bound(m_sections.begin(), m_sections.end(), sp->name(), [](const std::unique_ptr
&s, std::string_view name) { return s->name().compare(name) < 0; }); if (si == m_sections.end()) m_sections.insert(si, std::move(sp)); } return *this; } template requires(std::is_base_of_v and ...) config &add_section(const std::string §ion_name, Options &&...options) { auto si = std::lower_bound(m_sections.begin(), m_sections.end(), section_name, [](const std::unique_ptr
&s, std::string_view name) { return s->name().compare(name) < 0; }); auto s = std::make_unique
(section_name, std::forward(options)...); if (si != m_sections.end()) *si = std::move(s); else m_sections.insert(si, std::move(s)); return *this; } template requires(std::is_base_of_v and ...) static void init_lib(std::string section_name, Options &&...options) { get_section_factories().emplace_back(new section_factory(std::move(section_name), std::forward(options)...)); } void set_ignore_unknown(bool ignore_unknown) { m_ignore_unknown = ignore_unknown; } static config &instance() { static std::unique_ptr s_instance; if (not s_instance) s_instance.reset(new config); return *s_instance; } [[nodiscard]] std::string get_last_option() const { return s_last_option; } [[nodiscard]] bool has(std::string_view name) const { auto opt = get_option(name); return opt != nullptr and (opt->m_seen > 0 or opt->m_default_value.has_value()); } [[nodiscard]] int count(std::string_view name) const { auto opt = get_option(name); return opt ? opt->m_seen : 0; } template [[nodiscard]] auto get(std::string_view name) const { using return_type = std::remove_cv_t; std::error_code ec; return_type result = get(name, ec); if (ec) throw std::system_error(ec, "while getting option '" + std::string{ name } + '\''); return result; } template auto get(std::string_view name, std::error_code &ec) const { using return_type = std::remove_cv_t; // store name for inspection later on s_last_option = name; return_type result{}; auto opt = get_option(name); // if opt is null, the programmer has made an error requesting // an option that was not specified in the config::init call. if (opt == nullptr) ec = make_error_code(config_error::unknown_option); else result = opt->get_value(ec); return result; } [[nodiscard]] std::string get(std::string_view name) const { return get(name); } template [[nodiscard]] auto get_optional(std::string_view name) const { using return_type = std::optional>; std::error_code ec; return_type result = get(name, ec); if (ec and ec != config_error::option_not_specified) throw std::system_error(ec, "while getting option '" + std::string{ name } + '\''); return result; } [[nodiscard]] auto get_optional(std::string_view name) const { return get_optional(name); } [[nodiscard]] std::string get(std::string_view name, std::error_code &ec) const { return get(name, ec); } [[nodiscard]] const std::vector &operands() const { return m_operands; } friend std::ostream &operator<<(std::ostream &os, const config &conf); // -------------------------------------------------------------------- void parse(int argc, const char *const argv[]); void parse_config_file(std::string_view config_option, std::string_view config_file_name, std::initializer_list search_dirs); void parse_config_file(std::string_view config_option, std::string_view config_file_name, std::initializer_list search_dirs, std::error_code &ec); void parse_config_file(const std::filesystem::path &file, std::error_code &ec); private: static bool is_name_char(int ch) { return std::isalnum(ch) or ch == '_' or ch == '-'; } static constexpr bool is_eoln(int ch) { return ch == '\n' or ch == '\r' or ch == std::char_traits::eof(); } public: void parse_config_file(std::istream &is, std::error_code &ec); void parse(int argc, const char *const argv[], std::error_code &ec); // -------------------------------------------------------------------- constexpr static std::tuple split_name(std::string_view name) noexcept { using std::operator""sv; auto p = name.find('.'); return p == std::string_view::npos ? std::make_tuple(""sv, name) : std::make_tuple(name.substr(0, p), name.substr(p + 1)); } // -------------------------------------------------------------------- public: config(const config &) = delete; config &operator=(const config &) = delete; private: config() = default; // -------------------------------------------------------------------- [[nodiscard]] option_base *get_option(std::string_view section_name, std::string_view option_name) const { option_base *result = nullptr; for (auto &s : m_sections) { if (s->name() != section_name) continue; result = s->get_option(option_name); break; } return result; } [[nodiscard]] option_base *get_option(std::string_view name) const { auto [section_name, option_name] = split_name(name); return get_option(section_name, option_name); } [[nodiscard]] option_base *get_option(char short_name) const { option_base *result = nullptr; for (auto &s : m_sections) { result = s->get_option(short_name); if (result != nullptr) break; } return result; } [[nodiscard]] size_t get_option_width() const { size_t result = 0; for (auto &s : m_sections) { auto w = s->get_option_width(); if (result < w) result = w; } return result; } // -------------------------------------------------------------------- class section_factory_base { public: virtual ~section_factory_base() = default; [[nodiscard]] virtual section *create() const = 0; }; template class section_factory : public section_factory_base { public: explicit section_factory(std::string name, Options &&...options) : m_name(std::move(name)) , m_options(std::forward(options)...) { } [[nodiscard]] section *create() const override { return std::apply([this](Options const &...opts) { return new section(m_name, opts...); }, m_options); } std::string m_name; std::tuple m_options; }; static std::vector> &get_section_factories() { static std::vector> s_factories; return s_factories; } // -------------------------------------------------------------------- bool m_ignore_unknown = false; std::string m_usage; std::vector m_operands; std::vector> m_sections; static thread_local std::string s_last_option; }; // -------------------------------------------------------------------- template auto make_option(ostring name, std::string description) requires(not is_container_type_v) { return option(name.m_long, name.m_short, std::move(description), false); } template auto make_option(ostring name, std::string description) requires(is_container_type_v) { return multiple_option(name.m_long, name.m_short, std::move(description), false); } template auto make_option(ostring name, const T &v, std::string description) requires(not is_container_type_v) { return option(name.m_long, name.m_short, v, std::move(description), false); } template auto make_hidden_option(ostring name, std::string description) requires(not is_container_type_v) { return option(name.m_long, name.m_short, description, true); } template auto make_hidden_option(ostring name, std::string description) requires(is_container_type_v) { return multiple_option(name.m_long, name.m_short, description, true); } template auto make_hidden_option(ostring name, const T &v, std::string description) requires(not is_container_type_v) { return option(name.m_long, name.m_short, v, description, true); } } // namespace mcfp