User:Space Mission
From cppreference.com
Contents |
Today is 2024/07/23 yesterday's tomorrow.
Update routinely (A reminder):
(check/fill/synchronize the following feature-test-macro pages)
- cpp/feature_test#Library_features
- cpp/feature_test#Example
- cpp/utility/feature_test
- cpp/symbol_index/macro
- cpp/compiler_support/23
TODO
- add new range adaptors pages...
- gather new names (C++17/20/23) for GeShi highlighter/links generator
Links useful for editing
- WikiMedia: Variables
- WikiMedia: Parser Functions
- WikiMedia: Advanced templates
- WikiMedia: Enchantment
- WikiMedia: Syntax Highlight
- Common HTML entities
- Unicode symbols
A note on syntax highlight
cppreference uses GeShi syntax highlighter, the currently used version of which does not recognize most of new (C++17/20/23) keywords. Nonetheless, there is a possibility to make them highlighted (in the source-blocks) by editing the 'on-server' file that is actually a copy of the geshi/cpp.php. The new keywords (e.g. char8_t, consteval, etc.) should be added into 'KEYWORDS' => array of that file.
Some of my examples
Utilities
Not to be lost:
- FTM difference finder (draft vs Symbol Index).
- Compiler Features Dump.
- FTM table generator for cpp/utility/feature_test:
v1. Feature-test macros extractor / table generator (cpp/utility/feature_test); Source: online C++ draft (HTML) |
---|
//! //! \abstract This program downloads the "Language support library macros" //! [page](https://eel.is/c++draft/version.syn), parses it, and then generates the //! wiki-source for the [page](https://en.cppreference.com/w/cpp/utility/feature_test). //! //! \usage: just compile & run, the wiki-page will be sent to terminal. //! //! \dependencies: the `curl` program, C++20. //! //! \author: (c) 2021. Space Mission. For cppreference.com internal usage. //! \license: [CC-BY-SA](https://en.cppreference.com/w/Cppreference:Copyright/CC-BY-SA). //! //! \news: //! 2021-06-12 : initial release (used an array that contains the plain text copied from //! : the eel page). //! 2021-12-19 : added options and means to generate the whole wiki-page instead of only //! : the wiki-table. Note that this requires updates if the original //! : [page](cppreference.com/w/cpp/utility/feature_test) was changed. //! 2021-12-20 : (optimizations) e.g. the output is generated w/o a temporary vector. //! 2021-12-21 : added "curl" downloading of the source html file from //! : [the site](https://eel.is/c++draft/version.syn). //! 2021-12-23 : added data extractor from the html using std::regex library. //! 2021-12-27 : added more test. //! 2022-01-29 : fixed SourceDownloader::load() always returned `false`; nonetheless, //! : everything worked due to try_local_file_if_download_failed == true. //! 2023-02-27 : removed page's (volatile) header/footer generating code; left only the //! : table generation; placed the macros counter in the table's footer. //! 2023-04-05 : added support of macros w/o headers, e.g., __cpp_lib_modules. //! //! TODO: add "freestanding" support, see P2198R4 (Freestanding Feature-Test Macros) #include <algorithm> #include <cassert> #include <cstdlib> #include <filesystem> #include <fstream> #include <iomanip> #include <iostream> #include <regex> #include <sstream> #include <stdexcept> #include <string> #include <string_view> using namespace std::literals; /** * @brief global options */ struct option { // //! A command to download the source html page. This string will be appended with the //! target file and then executed by std::system( command / tmp-dir / file-name ); //! static inline constexpr auto command{ "curl https://eel.is/c++draft/version.syn --silent -o "sv}; // //! Do not fail if the source html file was not loaded (due to Internet problems) in //! this session, but still exists locally (left from a previous session). //! Useful mostly for debugging. static inline constexpr bool try_local_file_if_download_failed {false}; // //! Remove downloaded source html file (only if it was downloaded in this session). static inline constexpr bool remove_downloaded_file_at_exit {false}; // //! Print additional info, such as success/failure. static inline constexpr bool verbose {false}; // //! Test mode only. static inline constexpr bool enable_tests {false}; }; //! //! \brief A downloader: gets the source file from the Internet. //! class SourceDownloader final { std::string downloaded_file_name_; public: SourceDownloader() = default; SourceDownloader(SourceDownloader const&) = default; SourceDownloader& operator=(SourceDownloader const&) = default; ~SourceDownloader() { cleanup(); } public: [[nodiscard]] bool load(); [[nodiscard]] const std::string& file_name() const noexcept { return downloaded_file_name_; } void cleanup(); }; /** * @brief Loads source html file from the "https://eel.is/c++draft/". * @return return true if the file was downloaded successfully. */ inline bool SourceDownloader::load() { constexpr auto html {"eel_cpp_header_version.html"sv}; downloaded_file_name_ = std::filesystem::temp_directory_path() / html; const std::string command {option::command.data() + downloaded_file_name_}; if constexpr (option::verbose) { std::cout << "Downloading with: [" << command << "]\n"; std::cout << "Destination file: " << downloaded_file_name_ << '\n'; } if (const int ret_code {std::system(command.data())}; ret_code != 0) { if constexpr (option::verbose) { std::cout << "Can't download the file: error #" << ret_code << '\n'; // TODO: maybe decipher the error return code. } return false; } if constexpr (option::verbose) std::cout << "OK. The file was downloaded successfully.\n"; return true; } /** * @brief Reset file and (conditionally) remove downloaded html source file. */ inline void SourceDownloader::cleanup() { if constexpr (option::remove_downloaded_file_at_exit) { if (!downloaded_file_name_.empty()) { std::filesystem::remove(downloaded_file_name_); downloaded_file_name_.clear(); } } } /** * @brief class FeatureTestTable. * Extracts data from a given string, that must be a part of * [table entry](https://eel.is/c++draft/version.syn) html page * and generates the page [or (conditionally) only the table]: * [page](https://en.cppreference.com/w/cpp/utility/feature_test) */ class FeatureTestTable { public: FeatureTestTable() = default; public: [[nodiscard]] std::string generate_entry(std::string_view source); private: [[nodiscard]] bool parse_line(std::string_view source); public: static bool generate(); public: [[nodiscard]] static bool self_test(); private: [[nodiscard]] static std::string_view table_head(); [[nodiscard]] static std::string table_tail(unsigned); private: using string_view_vector = std::vector<std::string_view>; private: string_view_vector headers_; // e.g. {"vector", "type_traits"} std::string_view macro_; // e.g. "__cpp_lib_any" std::string_view date_; // e.g. "201606L" }; /** * @brief Parses the source line and sets internal values for macro_, date_, headers_ * @param[in] source - The source html line * @exception std::logic_error - throws if the source html has a wrong format. * @return true - if line was parsed successfully. */ inline bool FeatureTestTable::parse_line(std::string_view source) { headers_.clear(); macro_ = date_ = ""; const char* suffix; std::cmatch m; constexpr std::regex::flag_type flags{std::regex_constants::ECMAScript | std::regex_constants::optimize}; /* Obtain macro (e.g. "__cpp_lib_byte") and date (e.g. "201603L") * HTML example: <span class='preprocessordirective'>#define</span> <span id='lib:__cpp_lib_byte'>... ...<span class='literal'>202011L</span>... */ static const std::regex re_get_macro_and_date{ "'lib:(__cpp_lib_[_a-z0-9]{3,50})'.*'literal'>(20[1-4][0-9]{3}L)", flags }; if (!std::regex_search(source.data(), m, re_get_macro_and_date) or m.size() != 3) return false; macro_ = std::string_view(m[1].first, m[1].second - m[1].first); date_ = std::string_view(m[2].first, m[2].second - m[2].first); suffix = m.suffix().first; // contains the tail with at least one header if (macro_.length() < "__cpp_lib_xxx"sv.length() or date_.length() != (/*a sample:*/ "202002L"sv).length()) return false; /* Obtain header(s) in the cycle. * HTML example: ...<span id='headerref:<string_view>___'>... */ static const std::regex re_find_header{ R"regex((?:headerref:<)([_a-z\d]+(?:\.h)?)>)regex", flags}; for (; std::regex_search(suffix, m, re_find_header) and m.size() == 2; suffix = m.suffix().first) // contains the tail with zero or more headers headers_.emplace_back(m[1].first, m[1].second - m[1].first); return true; } /** * @brief Performs self test and returns. * * @return `true` if self-test passed. */ inline bool FeatureTestTable::self_test() { FeatureTestTable tab; constexpr auto fake_html = "<span class='preprocessordirective'>#define</span> <span id='lib:__cpp_lib_byte'>..." "...<span class='literal'>202011L</span>..." "...<span id='headerref:<string>___'>..." "...<span id='headerref:<string_view>___'>..."sv; return tab.parse_line(fake_html) and tab.macro_ == "__cpp_lib_byte" and tab.date_ == "202011L" and tab.headers_.size() == 2 and tab.headers_[0] == "string" and tab.headers_[1] == "string_view"; } /** * @brief Generates the header of the table. */ inline std::string_view FeatureTestTable::table_head() { return R"--( {| class="wikitable sortable" |- ! Macro name ! Value ! Header )--"sv; } /** * @brief Generates the footer of the table. */ inline std::string FeatureTestTable::table_tail(unsigned entry_count) { return "|-\n" "! colspan=\"3\" | Total number of macros: " + std::to_string(entry_count) + "\n" "|}\n"; } /** * @brief Generates cppreference table entry. * @param source - source string to parse. * @return non-empty table entry string, if success. An empty string otherwise. */ inline std::string FeatureTestTable::generate_entry(std::string_view source) { if (!parse_line(source)) return {}; /* cppreference table entry sample: |- | {{tt|__cpp_lib_byte}} | 201603L | {{header|atomic}} {{header|filesystem}} ... */ std::ostringstream str("", std::ios_base::ate); str << "|-\n" "| {{tt|" << macro_ << "}}\n" "| " << date_ << "\n" "|"; for (auto const& header : headers_) { str << " {{header|" << header << "}}"; } return str.str(); } // TODO: ? implement input_iterator interface for SourceFileReader: // begin(), end() [i.e. sentinel] operator*, and operator++. class SourceFileReader { public: SourceFileReader(SourceFileReader const&) = delete; SourceFileReader& operator=(SourceFileReader const&) = delete; explicit SourceFileReader(std::string_view const file_name); [[nodiscard]] bool is_open() const { return ok_; } [[nodiscard]] bool goto_next_line(); [[nodiscard]] std::string_view get_line() const { return cur_; } operator std::ifstream& () noexcept { return file_; } private: std::ifstream file_; std::string cur_; std::string next_; bool ok_{false}; }; /** * @brief Opens the source html file. * Sets the success-flag that should be checked with is_open(). * @param file_name - the source html file name. */ inline SourceFileReader::SourceFileReader(std::string_view const file_name) { const auto file = std::filesystem::path{file_name}; auto ec = std::error_code{}; if (!std::filesystem::exists(file, ec)) { std::cerr << "ERROR: source html file not found: " << file << '\n'; return; } constexpr auto min_file_size = std::uintmax_t{90'000}; // bytes if (auto size = std::uintmax_t{0}; (size = std::filesystem::file_size(file, ec)) < min_file_size) { std::cerr << "ERROR: source html file: " << file << '\n' << " is too small, size = " << size << " bytes;\n" << " expected size >= " << min_file_size << " bytes\n"; return; } file_.open(file_name.data()); if (not(ok_ = file_.is_open())) { std::cerr << "ERROR: can't open the source html file: " << file << '\n'; return; } } /** * @brief Go to the next line, if it is available. * @exception may throw std::logic_error in case of wrong file format * @return true, if next line is available */ inline bool SourceFileReader::goto_next_line() { if (not ok_) return false; constexpr auto significant {"preprocessordirective"sv}; if (cur_.empty()) { // This is a first run. Find the next valid line. while ((ok_ = !!std::getline(file_, cur_)) && cur_.find(significant) == ""sv.npos) ; if (not ok_) throw std::logic_error("Invalid file format."); } else { // This is not the first run. Thus next_ contains the begin. cur_ = std::move(next_); } // Find the next begin (or EOF), appending the dependent lines. while ((ok_ = !!std::getline(file_, next_)) && next_.find(significant) == ""sv.npos) { cur_ += next_; } return true; } /** * @brief Generates/prints the table with standard library feature-testing macros. * @note The format of the output is the wiki-media language. The result is meant to * be copied to [page](https://en.cppreference.com/w/cpp/utility/feature_test). */ inline bool FeatureTestTable::generate() { SourceDownloader html; if (not html.load()) { if constexpr (option::try_local_file_if_download_failed) { if constexpr (option::verbose) std::cerr << "Trying to use local source file: " << html.file_name() << "\n\n"; } else { return false; } } auto lines {SourceFileReader{html.file_name()}}; if (not lines.is_open()) { return false; } std::cout << FeatureTestTable::table_head(); auto entry_count {0U}; try { for (FeatureTestTable table; lines.goto_next_line(); ) { const std::string line{ table.generate_entry(lines.get_line()) }; if (not line.empty()) { std::cout << line << '\n'; ++entry_count; } } } catch (std::logic_error const& ex) { if constexpr (option::verbose) std::cerr << "ERROR: " << ex.what() << '\n'; return false; } std::cout << FeatureTestTable::table_tail(entry_count) << '\n'; // Some sanity checks: constexpr auto min_entries {179u}; // as per 2023-02-13 if (entry_count < min_entries) { std::cerr << "\nWARNING: Not enough entries! Expected at least " << min_entries << '\n'; } return true; } /** * @brief Performs self test and returns. * * @return `true` if self-tests passed. */ inline bool self_tests() { if constexpr (not option::enable_tests) return true; bool ok = FeatureTestTable::self_test(); if (ok) { if constexpr (option::verbose) std::cerr << "OK. Tests passed.\n"; } else { std::cerr << "TESTS FAILED.\n"; } return ok; } int main() { return self_tests() and FeatureTestTable::generate() ? EXIT_SUCCESS : EXIT_FAILURE; } |
v2. Feature-test macros extractor / table generator (cpp/utility/feature_test); Source: github C++ draft (TEX) |
---|
//! \abstract This program downloads the "Language support library macros" //! [page](https://github.com/cplusplus/draft/raw/main/source/support.tex) (used to be //! [page](https://eel.is/c++draft/version.syn)), parses it, and then generates the //! MediaWiki table for the [page](https://en.cppreference.com/w/cpp/utility/feature_test). //! //! \usage: just compile & run, the MediaWiki table will be sent to the terminal. //! //! \dependencies: [curl](https://en.wikipedia.org/wiki/CURL), C++23. //! //! \author: (c) 2021-2024. Space Mission. For cppreference.com internal usage. //! \license: [CC-BY-SA](https://en.cppreference.com/w/Cppreference:Copyright/CC-BY-SA). //! //! \news: //! 2021-06-12 : initial release (used an array that contains the plain text copied from //! : the eel page). //! 2021-12-19 : added options and means to generate the whole wiki-page instead of only //! : the wiki-table. Note that this requires updates if the original //! : [page](cppreference.com/w/cpp/utility/feature_test) was changed. //! 2021-12-20 : (optimizations) e.g. the output is generated w/o a temporary vector. //! 2021-12-21 : added "curl" downloading of the source html file from //! : [the site](https://eel.is/c++draft/version.syn). //! 2021-12-23 : added data extractor from the html using std::regex library. //! 2021-12-27 : added more test. //! 2022-01-29 : fixed SourceDownloader::load() always returned `false`; nonetheless, //! : everything worked due to try_local_file_if_download_failed == true. //! 2023-02-27 : removed page's (volatile) header/footer generating code; left only the //! : table generation; placed the macros counter in the table's footer. //! 2023-04-05 : added support of macros w/o headers, e.g., __cpp_lib_modules. //! 2024-07-21 : source switching: the source site now is github C++draft repo; the file //! : format to parse is "tex"; added "freestanding" support. //! 2024-07-22 : table style: added 4th ("freestanding") column; added FTM links. #include <algorithm> #include <cassert> #include <cstdlib> #include <filesystem> #include <fstream> #include <iomanip> #include <iostream> #include <regex> #include <sstream> #include <stdexcept> #include <string> #include <string_view> using namespace std::literals; /** * @brief global options */ struct option { //! Do not fail if the source file was not loaded (due to Internet problems) in //! this session, but still exists locally (left from a previous session). static inline constexpr bool try_local_file_if_download_failed{true}; //! Remove downloaded source file (only if it was downloaded in this session). static inline constexpr bool remove_downloaded_file_at_exit{false}; //! Print log and additional info, e.g. success/failure. static inline constexpr bool verbose{false}; //! Test mode only. static inline constexpr bool enable_self_tests{false}; }; //! //! \brief The source file downloader that gets the source file from the Internet. //! class SourceDownloader final { std::string file_name_; public: SourceDownloader() = default; SourceDownloader(SourceDownloader const&) = default; SourceDownloader& operator=(SourceDownloader const&) = default; ~SourceDownloader(); public: [[nodiscard]] bool load(); [[nodiscard]] const std::string& file_name() const noexcept { return file_name_; } }; /** * @brief Loads source TEX file from the C++-draft github repo. * @return true, if the file was downloaded successfully. */ inline bool SourceDownloader::load() { file_name_ = std::filesystem::temp_directory_path() / "cxx_draft__support.tex"; const std::string command{ "curl -LJ https://github.com/cplusplus/draft/raw/main/source/support.tex "s + (option::verbose ? "-o "s : "--silent -o "s) + file_name_}; if constexpr (option::verbose) { std::cout << "Downloading with: [" << command << "]\n" "Destination file: " << file_name_ << '\n'; } if (const int ret_code{std::system(command.data())}; ret_code) { if constexpr (option::verbose) { std::cout << "Can't download the file: error #" << ret_code << '\n'; // TODO: maybe decipher the error return code. } return false; } if constexpr (option::verbose) { std::cout << "OK. The file was downloaded successfully.\n"; } return true; } /** * @brief Conditionally remove downloaded source file. */ inline SourceDownloader::~SourceDownloader() { if constexpr (option::remove_downloaded_file_at_exit) if (!file_name_.empty()) std::filesystem::remove(file_name_); } /** * @brief class FtmTableEntry. * * Extracts data from a given string, that must be a part of * [source page](https://github.com/cplusplus/draft/raw/main/source/support.tex) * tex page; generates the page [or (conditionally) only the table]: * [target page](https://en.cppreference.com/w/cpp/utility/feature_test) */ class FtmTableEntry { public: FtmTableEntry() = default; public: [[nodiscard]] std::string generate_entry(std::string_view source); void clear(); private: [[nodiscard]] bool parse_line(std::string_view source); public: static bool generate(); public: [[nodiscard]] static bool self_test(); private: [[nodiscard]] static std::string_view table_head() noexcept; [[nodiscard]] static std::string table_tail(unsigned); private: std::vector<std::string_view> headers_; // e.g. {"vector", "type_traits"} std::string macro_; // e.g. "__cpp_lib_any" std::string_view date_; // e.g. "201606L" bool free_{}; // true, if "freestanding" }; /** * @brief Parses the source line and sets internal values for: */ inline void FtmTableEntry::clear() { headers_.clear(); macro_.clear(); date_ = std::string_view{}; free_ = false; } /** * @brief Parses the source line and sets internal values for: * macro_, date_, headers_, free_. * @param source, the TEX source line. * @exception std::logic_error, if the source line has a wrong format. * @return true, if line was parsed successfully. */ inline bool FtmTableEntry::parse_line(std::string_view source) { clear(); std::cmatch m; constexpr std::regex::flag_type flags{std::regex_constants::ECMAScript | std::regex_constants::optimize}; // A fake example: // #define @\defnlibxname{cpp_lib_atomic}@ 201911L // freestanding, also in // \libheader{atomic}, \libheader{memory} if (static const std::regex re_ftm_and_date{ R"FTM(#define @\\defnlibxname\{(cpp_lib_[_a-z0-9]{3,50})\}@)FTM" R"DATE([ ]+(20[1-4][0-9]{3}L)[ ]+// )DATE", flags}; !std::regex_search(source.data(), m, re_ftm_and_date) or m.size() != 3) return false; macro_ = "__" + std::string(m[1].first, m[1].second - m[1].first); date_ = std::string_view(m[2].first, m[2].second - m[2].first); // sanity check: if (macro_.length() < "__cpp_lib_xxx"sv.length() or date_.length() != (/*sample:*/"202002L"sv).length()) return false; source.remove_prefix(m.suffix().first - source.data()); // the tail constexpr auto free{"freestanding"sv}; // Extract "freestaning" flag, if any. if (const auto pos{source.substr(0, free.length() + 8).find(free)}; pos != ""sv.npos) { source.remove_prefix(pos + free.length()); free_ = true; } // Obtain header(s) in cycle. // TEX example: ... also in \libheader{atomic}, \libheader{memory} ... static const std::regex re_header{ R"RE(\libheader\{([a-z][_a-z\d\.]+)\})RE", flags}; while (std::regex_search(source.data(), m, re_header) and m.size() == 2) { headers_.emplace_back(m[1].first, m[1].second - m[1].first); source.remove_prefix(m.suffix().first - source.data()); // update the tail } return true; } /** * @brief Performs self test and returns. * * @return true, if self-test has passed. */ inline bool FtmTableEntry::self_test() { FtmTableEntry tab; if constexpr (1) { tab.clear(); constexpr auto fake_tex = "#define @\\defnlibxname{cpp_lib_atomic_lock}@ " "201907L // also in \\libheader{atomic}"sv; if (!(tab.parse_line(fake_tex) and tab.macro_ == "__cpp_lib_atomic_lock" and tab.date_ == "201907L" and tab.free_ == false and tab.headers_.size() == 1 and tab.headers_[0] == "atomic")) { std::cerr << "TESTS #1 FAILED.\n"; return false; } } if constexpr (1) { tab.clear(); constexpr auto fake_tex = "#define @\\defnlibxname{cpp_lib_char8_t}@ 201907L // freestanding, " "also in \\libheader{atomic}, \\libheader{filesystem}, " "// \\libheader{istream}, \\libheader{limits}"sv; if (!(tab.parse_line(fake_tex) and tab.macro_ == "__cpp_lib_char8_t" and tab.date_ == "201907L" and tab.free_ and tab.headers_.size() == 4 and tab.headers_[0] == "atomic" and tab.headers_[1] == "filesystem" and tab.headers_[2] == "istream" and tab.headers_[3] == "limits")) { std::cerr << "TESTS #2 FAILED.\n"; return false; } } if constexpr (option::verbose) std::cerr << "OK. Tests passed.\n"; return true; } /** * @brief Generates the header of the table. */ inline std::string_view FtmTableEntry::table_head() noexcept { return R"--( {|class="wikitable sortable" style="font-size:100%;" |- !Macro name!!Value!!Header!!Free-<br>standing )--"sv; } /** * @brief Generates the footer of the table. */ inline std::string FtmTableEntry::table_tail(unsigned entry_count) { return "|-\n" "!colspan=\"4\"|Total number of macros: " + std::to_string(entry_count) + "\n" "|}\n"; } /** * @brief Generates cppreference table entry. * @param source - source string to parse. * @return non-empty table entry string, if success. An empty string otherwise. */ inline std::string FtmTableEntry::generate_entry(std::string_view source) { if (!parse_line(source)) return {}; /* cppreference table entry sample: |- |{{ftm link|__cpp_lib_byte}} |201603L |{{header|atomic}} {{header|filesystem}} ... |{{yes}} */ std::ostringstream str("", std::ios_base::ate); str << "|-\n" "|{{ftm link|" << macro_ << "}}\n" "|{{c|" << date_ << "}}\n" "|"; for (int n{}; auto const& header : headers_) str << (n++ ? " " : "") << "{{header|" << header << "}}"; str << (free_ ? "\n|{{yes}}" : "\n|"); return str.str(); } class SourceFileReader { public: explicit SourceFileReader(std::string_view const file_name); SourceFileReader(SourceFileReader const&) = delete; SourceFileReader& operator=(SourceFileReader const&) = delete; [[nodiscard]] operator std::ifstream& () noexcept { return file_; } [[nodiscard]] std::string_view cur_line() const { return cur_; } [[nodiscard]] bool is_open() const { return ok_; } [[nodiscard]] bool next_line(); private: std::ifstream file_; std::string cur_; std::string next_; bool ok_{false}; }; /** * @brief Opens the source TEX file. * Sets the success-flag that should be checked with is_open(). * @param file_name - the source TEX file name. */ inline SourceFileReader::SourceFileReader(std::string_view const file_name) { const auto file = std::filesystem::path{file_name}; auto ec = std::error_code{}; if (!std::filesystem::exists(file, ec)) { std::cerr << "ERROR: source file not found: " << file << '\n'; return; } constexpr std::uintmax_t min_file_size{200'000}; // bytes if (std::uintmax_t size; (size = std::filesystem::file_size(file, ec)) < min_file_size) { std::cerr << "ERROR: source file: " << file << "\n" " is too small, size = " << size << " bytes;\n" " expected size >= " << min_file_size << " bytes\n"; return; } if (file_.open(file_name.data()); not(ok_ = file_.is_open())) { std::cerr << "ERROR: can't open the source file: " << file << '\n'; return; } } /** * @brief Obtains the next line, if it is available (may concatinate few lines) * @exception std::logic_error, in case of wrong TEX file format * @return true, if next line is available */ inline bool SourceFileReader::next_line() { if (not ok_) return false; constexpr auto intro{"#define "sv}; constexpr auto outro{"\\end"sv}; // \end{codeblock} if (cur_.empty()) { // This is a first run. Find the first valid line. // Exprected preliminary line are: // // "Future revisions of this document might replace" // " the values of these macros with greater values." // "\end{note}" // "" // "\begin{codeblock}" // "#define ..." <== intro // constexpr auto nearest{"Future revisions of this document"sv}; while ((ok_ = !!std::getline(file_, cur_)) && !cur_.starts_with(nearest)) {} while ((ok_ = !!std::getline(file_, cur_)) && !cur_.starts_with(intro)) {} if (not ok_) throw std::logic_error("Unexpected file format."); } else { // This is not the first run. Thus next_ contains the beginning. cur_ = std::move(next_); } // Find the next begin (or EOB), appending the dependent lines. while ((ok_ = !!std::getline(file_, next_)) && !next_.starts_with(intro)) { if (next_.starts_with(outro)) { ok_ = false; // End Of Block break; } cur_ += next_; } return true; } /** * @brief Generates/prints the table with standard library feature-testing macros. * @note The result is a table described in MediaWiki language. The output should be * copied onto "https://en.cppreference.com/w/cpp/utility/feature_test" page. */ inline bool FtmTableEntry::generate() { SourceDownloader downloader; if (not downloader.load()) { if constexpr (option::try_local_file_if_download_failed) { if constexpr (option::verbose) std::cerr << "Trying to use local source file: " << downloader.file_name() << "\n\n"; } else { return false; } } auto lines{SourceFileReader{downloader.file_name()}}; if (not lines.is_open()) { return false; } std::cout << FtmTableEntry::table_head(); auto entry_count{0U}; try { for (FtmTableEntry table; lines.next_line(); ) { const std::string line{ table.generate_entry(lines.cur_line()) }; if (not line.empty()) { std::cout << line << '\n'; ++entry_count; } } } catch (std::logic_error const& ex) { if constexpr (option::verbose) std::cerr << "ERROR: " << ex.what() << '\n'; return false; } std::cout << FtmTableEntry::table_tail(entry_count) << '\n'; // A sanity check :) constexpr auto min_entries{233u}; // as per 2024-07 if (entry_count < min_entries) { std::cerr << "\nWARNING: Too few entries! Expected " << min_entries << " or more.\n"; } return true; } /** * @brief Performs self test and returns. * * @return true, if self-tests passed. */ inline bool self_tests() { if constexpr (not option::enable_self_tests) return true; return FtmTableEntry::self_test(); } int main() { return self_tests() and FtmTableEntry::generate() ? EXIT_SUCCESS : EXIT_FAILURE; } |