diff --git a/src/issues.h b/src/issues.h index 18ca1b7d67..8bfbcda84c 100644 --- a/src/issues.h +++ b/src/issues.h @@ -33,12 +33,6 @@ struct issue { bool has_resolution; // 'true' if 'text' contains a proposed resolution }; -struct order_by_issue_number { - bool operator()(issue const & x, issue const & y) const noexcept { return x.num < y.num; } - bool operator()(issue const & x, int y) const noexcept { return x.num < y; } - bool operator()(int x, issue const & y) const noexcept { return x < y.num; } -}; - auto parse_issue_from_file(std::string file_contents, std::string const & filename, lwg::metadata & meta) -> issue; // Seems appropriate constructor behavior. // diff --git a/src/list_issues.cpp b/src/list_issues.cpp index 15e859af02..9d978d1d55 100644 --- a/src/list_issues.cpp +++ b/src/list_issues.cpp @@ -60,8 +60,8 @@ auto read_file_into_string(fs::path const & filename) -> std::string { auto is_issue_xml_file(fs::directory_entry const & e) { if (e.is_regular_file()) { - fs::path f = e.path().filename(); - return f.string().starts_with("issue") && f.extension() == ".xml"; + auto f = e.path().filename().string(); + return f.starts_with("issue") && f.ends_with(".xml"); } return false; } @@ -82,9 +82,8 @@ void filter_issues(fs::path const & issues_path, lwg::metadata & meta, std::func } } // Write the sorted issue numbers to stdout. - std::sort(nums.begin(), nums.end()); - for (auto num : nums) - std::cout << num << '\n'; + std::ranges::sort(nums); + std::ranges::copy(nums, std::ostream_iterator(std::cout, "\n")); } // ============================================================================================================ diff --git a/src/lists.cpp b/src/lists.cpp index 23a1bba943..7d4ed8dc6b 100644 --- a/src/lists.cpp +++ b/src/lists.cpp @@ -72,8 +72,8 @@ auto read_file_into_string(fs::path const & filename) -> std::string { auto is_issue_xml_file(fs::directory_entry const & e) { if (e.is_regular_file()) { - fs::path f = e.path().filename(); - return f.string().starts_with("issue") && f.extension() == ".xml"; + auto f = e.path().filename().string(); + return f.starts_with("issue") && f.ends_with(".xml"); } return false; } @@ -95,7 +95,7 @@ auto read_issues(fs::path const & issues_path, lwg::metadata & meta) -> std::vec } -auto read_issues_from_toc(std::string const & s) -> std::vector > { +auto read_issues_from_toc(std::string const & s) -> std::vector> { // parse all issues from the specified stream, 'is'. // Throws 'runtime_error' if *any* parse step fails // @@ -126,7 +126,7 @@ auto read_issues_from_toc(std::string const & s) -> std::vector > issues; + std::vector> issues; for(;;) { i = s.find("", i+4); if (i == std::string::npos) { @@ -231,8 +231,7 @@ std::string paper_title_attr(std::string paper_number, lwg::metadata& meta) { } void format_issue_as_html(lwg::issue & is, - std::vector::iterator first_issue, - std::vector::iterator last_issue, + std::span issues, lwg::metadata & meta) { auto& section_db = meta.section_db; @@ -289,7 +288,6 @@ void format_issue_as_html(lwg::issue & is, return std::isalpha(c) || c == '/' || c == '!'; }; - // Reformat the issue text for the specified 'is' as valid HTML, replacing all the issue-list // specific XML markup as appropriate: // tag replacement @@ -310,7 +308,7 @@ void format_issue_as_html(lwg::issue & is, // (unknown) section is discovered, it will be inserted into the supplied // section index, 'section_db'. // - // The behavior is undefined unless the issues in the supplied vector range are sorted by issue-number. + // The behavior is undefined unless the issues in the supplied span are sorted by issue-number. // // Essentially, this function is a tiny xml-parser driven by a stack of open tags, that pops as tags // are closed. @@ -411,8 +409,8 @@ void format_issue_as_html(lwg::issue & is, } } - auto n = std::lower_bound(first_issue, last_issue, num, lwg::order_by_issue_number{}); - if (n == last_issue or n->num != num) { + auto n = std::ranges::lower_bound(issues, num, {}, &lwg::issue::num); + if (n == issues.end() or n->num != num) { fail("Could not find issue " + r + " for ", context); } @@ -489,17 +487,17 @@ void format_issue_as_html(lwg::issue & is, } -void prepare_issues(std::vector & issues, lwg::metadata & meta) { +void prepare_issues(std::span issues, lwg::metadata & meta) { // Initially sort the issues by issue number, so each issue can be correctly 'format'ted - sort(issues.begin(), issues.end(), lwg::order_by_issue_number{}); + std::ranges::sort(issues, {}, &lwg::issue::num); // Then we format the issues, which should be the last time we need to touch the issues themselves // We may turn this into a two-stage process, analysing duplicates and then applying the links // This will allow us to better express constness when the issues are used purely for reference. - // Currently, the 'format' function takes a reference-to-non-const-vector-of-issues purely to + // Currently, the 'format' function takes a span of non-const-issues purely to // mark up information related to duplicates, so processing duplicates in a separate pass may // clarify the code. - for (auto & i : issues) { format_issue_as_html(i, issues.begin(), issues.end(), meta); } + for (auto & i : issues) { format_issue_as_html(i, issues, meta); } // Issues will be routinely re-sorted in later code, but contents should be fixed after formatting. // This suggests we may want to be storing some kind of issue handle in the functions that keep @@ -509,17 +507,16 @@ void prepare_issues(std::vector & issues, lwg::metadata & meta) { // ============================================================================================================ -auto prepare_issues_for_diff_report(std::vector const & issues) -> std::vector > { - std::vector > result; - std::transform( issues.begin(), issues.end(), back_inserter(result), -#if 1 - [](lwg::issue const & iss) { return std::make_tuple(iss.num, iss.stat); } +auto prepare_issues_for_diff_report(std::vector const & issues) -> std::vector> { + auto make_tuple = [](lwg::issue const & iss) { return std::make_tuple(iss.num, iss.stat); }; +#ifdef __cpp_lib_ranges_to_container + return std::ranges::to(issues | std::views::transform(make_tuple)); #else - // This form does not work because tuple constructors are explicit - [](lwg::issue const & iss) -> std::tuple { return {iss.num, iss.stat}; } -#endif - ); + std::vector> result; + result.reserve(issues.size()); + std::ranges::transform(issues, back_inserter(result), make_tuple); return result; +#endif } struct list_issues { @@ -546,14 +543,14 @@ struct find_num { struct discover_new_issues { - std::vector > const & old_issues; - std::vector > const & new_issues; + std::vector> const & old_issues; + std::vector> const & new_issues; }; auto operator<<( std::ostream & out, discover_new_issues const & x) -> std::ostream & { - std::vector > const & old_issues = x.old_issues; - std::vector > const & new_issues = x.new_issues; + std::vector> const & old_issues = x.old_issues; + std::vector> const & new_issues = x.new_issues; struct status_order { // predicate for 'map' @@ -591,14 +588,14 @@ auto operator<<( std::ostream & out, discover_new_issues const & x) -> std::ostr struct discover_changed_issues { - std::vector > const & old_issues; - std::vector > const & new_issues; + std::vector> const & old_issues; + std::vector> const & new_issues; }; auto operator << (std::ostream & out, discover_changed_issues x) -> std::ostream & { - std::vector > const & old_issues = x.old_issues; - std::vector > const & new_issues = x.new_issues; + std::vector> const & old_issues = x.old_issues; + std::vector> const & new_issues = x.new_issues; struct status_transition_order { using status_string = std::string; @@ -639,10 +636,10 @@ auto operator << (std::ostream & out, discover_changed_issues x) -> std::ostream } -void count_issues(std::vector > const & issues, int & n_open, int & n_reassigned, int & n_closed) { - n_open = 0; - n_reassigned = 0; - n_closed = 0; +auto count_issues(std::span> issues) -> std::tuple { + int n_open = 0; + int n_reassigned = 0; + int n_closed = 0; for(auto const & elem : issues) { if (lwg::is_assigned_to_another_group(std::get<1>(elem))) { @@ -655,27 +652,21 @@ void count_issues(std::vector > const & issues, int ++n_closed; } } + return {n_open, n_reassigned, n_closed}; } struct write_summary { - std::vector > const & old_issues; - std::vector > const & new_issues; + std::vector> const & old_issues; + std::vector> const & new_issues; }; auto operator << (std::ostream & out, write_summary const & x) -> std::ostream & { - std::vector > const & old_issues = x.old_issues; - std::vector > const & new_issues = x.new_issues; - - int n_open_new = 0; - int n_open_old = 0; - int n_reassigned_new = 0; - int n_reassigned_old = 0; - int n_closed_new = 0; - int n_closed_old = 0; - count_issues(old_issues, n_open_old, n_reassigned_old, n_closed_old); - count_issues(new_issues, n_open_new, n_reassigned_new, n_closed_new); + + auto [n_open_old, n_reassigned_old, n_closed_old] = count_issues(x.old_issues); + auto [n_open_new, n_reassigned_new, n_closed_new] = count_issues(x.new_issues); + auto write_change = [&out](int n_new, int n_old){ out << (n_new >= n_old ? "up by " : "down by ") << std::abs(n_new - n_old); @@ -704,8 +695,8 @@ auto operator << (std::ostream & out, write_summary const & x) -> std::ostream & void print_current_revisions( std::ostream & out - , std::vector > const & old_issues - , std::vector > const & new_issues + , std::vector> const & old_issues + , std::vector> const & new_issues ) { out << "
    \n" "
  • Summary:
      \n" @@ -783,7 +774,7 @@ int main(int argc, char* argv[]) { // issues must be sorted by number before making the mailing list documents - //sort(issues.begin(), issues.end(), order_by_issue_number{}); + // std::ranges::sort(issues, {}, &lwg::issue::num); // Collect a report on all issues that have changed status // This will be added to the revision history of the 3 standard documents diff --git a/src/mailing_info.cpp b/src/mailing_info.cpp index fa2e9924da..e3ef936bec 100644 --- a/src/mailing_info.cpp +++ b/src/mailing_info.cpp @@ -10,7 +10,7 @@ namespace { -void replace_all_irefs(std::vector const & issues, std::string & s) { +void replace_all_irefs(std::span issues, std::string & s) { // Replace all tagged "issues references" in string 's' with an HTML anchor-link to the live issue // in its appropriate issue list, as determined by the issue's status. // Format of an issue reference: @@ -40,7 +40,7 @@ void replace_all_irefs(std::vector const & issues, std::string & s) throw std::runtime_error{"bad number in iref"}; } - auto n = std::lower_bound(issues.begin(), issues.end(), num, lwg::order_by_issue_number{}); + auto n = std::ranges::lower_bound(issues, num, {}, &lwg::issue::num); if (n == issues.end() || n->num != num) { std::ostringstream er; er << "couldn't find number " << num << " in iref"; @@ -153,7 +153,7 @@ auto mailing_info::get_revision() const -> std::string { } -auto mailing_info::get_revisions(std::vector const & issues, std::string const & diff_report) const -> std::string { +auto mailing_info::get_revisions(std::span issues, std::string const & diff_report) const -> std::string { auto i = m_data.find(""); if (i == std::string::npos) { throw std::runtime_error{"Unable to find in lwg-issues.xml"}; diff --git a/src/mailing_info.h b/src/mailing_info.h index cb936d357f..74bda46a8f 100644 --- a/src/mailing_info.h +++ b/src/mailing_info.h @@ -3,7 +3,7 @@ #include #include -#include +#include namespace lwg { @@ -17,7 +17,7 @@ struct mailing_info { auto get_intro(std::string doc) const -> std::string; auto get_maintainer() const -> std::string; auto get_revision() const -> std::string; - auto get_revisions(std::vector const & issues, std::string const & diff_report) const -> std::string; + auto get_revisions(std::span issues, std::string const & diff_report) const -> std::string; auto get_statuses() const -> std::string; auto get_date() const -> std::string; auto get_title() const -> std::string; diff --git a/src/report_generator.cpp b/src/report_generator.cpp index b73f25f518..b1fa432325 100644 --- a/src/report_generator.cpp +++ b/src/report_generator.cpp @@ -18,7 +18,8 @@ #include #include #include -#include +#include +#include namespace { @@ -54,6 +55,16 @@ struct order_by_first_tag { } }; +// Similar to lwg::section_num but only looks at the first num in e.g. 17.5.2 +using major_section_key = std::pair; + +// Find key for major section (i.e. Clause number, within a given IS or TS) +auto lookup_major_section(lwg::section_map& db, const lwg::issue& i) -> major_section_key { + assert(!i.tags.empty()); + const lwg::section_num& sect = db[i.tags[0]]; + return { sect.prefix, sect.num[0] }; +} + struct order_by_major_section { explicit order_by_major_section(lwg::section_map & sections) : section_db(sections) @@ -61,11 +72,7 @@ struct order_by_major_section { } auto operator()(lwg::issue const & x, lwg::issue const & y) const -> bool { - assert(!x.tags.empty()); - assert(!y.tags.empty()); - lwg::section_num const & xn = section_db[x.tags[0]]; - lwg::section_num const & yn = section_db[y.tags[0]]; - return std::tie(xn.prefix, xn.num[0]) < std::tie(yn.prefix, yn.num[0]); + return lookup_major_section(section_db, x) < lookup_major_section(section_db, y); } private: @@ -124,17 +131,17 @@ inline std::string spaces_to_underscores(std::string s) { } -auto major_section(lwg::section_num const & sn) -> std::string { - std::string const prefix{sn.prefix}; +auto to_string(major_section_key sn) -> std::string { + auto [prefix, num] = sn; std::ostringstream out; if (!prefix.empty()) { out << prefix << " "; } - if (sn.num[0] < 100) { - out << sn.num[0]; + if (num < 100) { + out << num; } else { - out << char(sn.num[0] - 100 + 'A'); + out << char(num - 100 + 'A'); } return out.str(); } @@ -231,9 +238,9 @@ void print_file_trailer(std::ostream& out) { } -void print_table(std::ostream& out, std::vector::const_iterator i, std::vector::const_iterator e, lwg::section_map& section_db, bool link_stable_names = false) { +void print_table(std::ostream& out, std::span issues, lwg::section_map& section_db, bool link_stable_names = false) { #if defined (DEBUG_LOGGING) - std::cout << "\t" << std::distance(i,e) << " items to add to table" << std::endl; + std::cout << "\t" << issues.size() << " items to add to table" << std::endl; #endif out << @@ -250,34 +257,34 @@ R"( )"; lwg::section_tag prev_tag; - for (; i != e; ++i) { + for (auto& i : issues) { out << "\n"; // Number - out << "\n"; // Status - const auto status_idattr = spaces_to_underscores(std::string(lwg::remove_qualifier(i->stat))); - out << "\n"; + const auto status_idattr = spaces_to_underscores(std::string(lwg::remove_qualifier(i.stat))); + out << "\n"; // Section out << "\n"; // Title - out << "\n"; + out << "\n"; // Has Proposed Resolution out << "
      num << "\">" << make_html_anchor(*i) - << "num + out << "" << make_html_anchor(i) + << "(i)" << i->stat << "" << i.stat << ""; - assert(!i->tags.empty()); - out << section_db[i->tags[0]] << " " << i->tags[0]; - if (link_stable_names && i->tags[0] != prev_tag) { - prev_tag = i->tags[0]; + assert(!i.tags.empty()); + out << section_db[i.tags[0]] << " " << i.tags[0]; + if (link_stable_names && i.tags[0] != prev_tag) { + prev_tag = i.tags[0]; out << ""; } out << "" << i->title << "" << i.title << ""; - if (i->has_resolution) { + if (i.has_resolution) { out << "Yes"; } else { @@ -287,14 +294,14 @@ R"( // Priority out << "\n"; // Duplicates out << "\n" << "\n"; } @@ -383,7 +390,7 @@ void print_issue(std::ostream & out, lwg::issue const & iss, lwg::section_map & } template -void print_issues(std::ostream & out, std::vector const & issues, lwg::section_map & section_db, Pred pred) { +void print_issues(std::ostream & out, std::span issues, lwg::section_map & section_db, Pred pred) { issue_set_by_first_tag const all_issues{ issues.begin(), issues.end()} ; issue_set_by_status const issues_by_status{ issues.begin(), issues.end() }; @@ -402,7 +409,7 @@ void print_issues(std::ostream & out, std::vector const & issues, lw } template -void print_resolutions(std::ostream & out, std::vector const & issues, lwg::section_map & section_db, Pred predicate) { +void print_resolutions(std::ostream & out, std::span issues, lwg::section_map & section_db, Pred predicate) { // This construction calls out for filter-iterators // std::multiset pending_issues; std::vector pending_issues; @@ -472,8 +479,8 @@ namespace lwg // A precondition for calling any of these functions is that the list of issues is sorted in numerical order, by issue number. // While nothing disasterous will happen if this precondition is violated, the published issues list will list items // in the wrong order. -void report_generator::make_active(std::vector const & issues, fs::path const & path, std::string const & diff_report) { - assert(std::is_sorted(issues.begin(), issues.end(), order_by_issue_number{})); +void report_generator::make_active(std::span issues, fs::path const & path, std::string const & diff_report) { + assert(std::ranges::is_sorted(issues, {}, &issue::num)); fs::path filename{path / "lwg-active.html"}; std::ofstream out{filename}; @@ -491,8 +498,8 @@ void report_generator::make_active(std::vector const & issues, fs::path c } -void report_generator::make_defect(std::vector const & issues, fs::path const & path, std::string const & diff_report) { - assert(std::is_sorted(issues.begin(), issues.end(), order_by_issue_number{})); +void report_generator::make_defect(std::span issues, fs::path const & path, std::string const & diff_report) { + assert(std::ranges::is_sorted(issues, {}, &issue::num)); fs::path filename{path / "lwg-defects.html"}; std::ofstream out(filename); @@ -509,8 +516,8 @@ void report_generator::make_defect(std::vector const & issues, fs::path c } -void report_generator::make_closed(std::vector const & issues, fs::path const & path, std::string const & diff_report) { - assert(std::is_sorted(issues.begin(), issues.end(), order_by_issue_number{})); +void report_generator::make_closed(std::span issues, fs::path const & path, std::string const & diff_report) { + assert(std::ranges::is_sorted(issues, {}, &issue::num)); fs::path filename{path / "lwg-closed.html"}; std::ofstream out{filename}; @@ -528,9 +535,9 @@ void report_generator::make_closed(std::vector const & issues, fs::path c // Additional non-standard documents, useful for running LWG meetings -void report_generator::make_tentative(std::vector const & issues, fs::path const & path) { +void report_generator::make_tentative(std::span issues, fs::path const & path) { // publish a document listing all tentative issues that may be acted on during a meeting. - assert(std::is_sorted(issues.begin(), issues.end(), order_by_issue_number{})); + assert(std::ranges::is_sorted(issues, {}, &issue::num)); fs::path filename{path / "lwg-tentative.html"}; std::ofstream out{filename}; @@ -548,9 +555,9 @@ void report_generator::make_tentative(std::vector const & issues, fs::pat } -void report_generator::make_unresolved(std::vector const & issues, fs::path const & path) { +void report_generator::make_unresolved(std::span issues, fs::path const & path) { // publish a document listing all non-tentative, non-ready issues that must be reviewed during a meeting. - assert(std::is_sorted(issues.begin(), issues.end(), order_by_issue_number{})); + assert(std::ranges::is_sorted(issues, {}, &issue::num)); fs::path filename{path / "lwg-unresolved.html"}; std::ofstream out{filename}; @@ -567,9 +574,9 @@ void report_generator::make_unresolved(std::vector const & issues, fs::pa print_file_trailer(out); } -void report_generator::make_immediate(std::vector const & issues, fs::path const & path) { +void report_generator::make_immediate(std::span issues, fs::path const & path) { // publish a document listing all non-tentative, non-ready issues that must be reviewed during a meeting. - assert(std::is_sorted(issues.begin(), issues.end(), order_by_issue_number{})); + assert(std::ranges::is_sorted(issues, {}, &issue::num)); fs::path filename{path / "lwg-immediate.html"}; std::ofstream out{filename}; @@ -602,9 +609,9 @@ out << R"(

      C++ Standard Library Issues Resolved Directly In [INSERT CURRENT M print_file_trailer(out); } -void report_generator::make_ready(std::vector const & issues, fs::path const & path) { +void report_generator::make_ready(std::span issues, fs::path const & path) { // publish a document listing all ready issues for a formal vote - assert(std::is_sorted(issues.begin(), issues.end(), order_by_issue_number{})); + assert(std::ranges::is_sorted(issues, {}, &issue::num)); fs::path filename{path / "lwg-ready.html"}; std::ofstream out{filename}; @@ -637,9 +644,9 @@ out << R"(

      C++ Standard Library Issues to be moved in [INSERT CURRENT MEETING print_file_trailer(out); } -void report_generator::make_editors_issues(std::vector const & issues, fs::path const & path) { +void report_generator::make_editors_issues(std::span issues, fs::path const & path) { // publish a single document listing all 'Voting' and 'Immediate' resolutions (only). - assert(std::is_sorted(issues.begin(), issues.end(), order_by_issue_number{})); + assert(std::ranges::is_sorted(issues, {}, &issue::num)); fs::path filename{path / "lwg-issues-for-editor.html"}; std::ofstream out{filename}; @@ -652,8 +659,8 @@ void report_generator::make_editors_issues(std::vector const & issues, fs print_file_trailer(out); } -void report_generator::make_sort_by_num(std::vector& issues, fs::path const & filename) { - sort(issues.begin(), issues.end(), order_by_issue_number{}); +void report_generator::make_sort_by_num(std::span issues, fs::path const & filename) { + std::ranges::sort(issues, {}, &issue::num); std::ofstream out{filename}; if (!out) @@ -669,12 +676,12 @@ R"(

      C++ Standard Library Issues List (Revision )" << lwg_issues_xml.get_revis )"; out << "

      " << build_timestamp << "

      "; - print_table(out, issues.begin(), issues.end(), section_db); + print_table(out, issues, section_db); print_file_trailer(out); } -void report_generator::make_sort_by_priority(std::vector& issues, fs::path const & filename) { +void report_generator::make_sort_by_priority(std::span issues, fs::path const & filename) { auto proj = [this](const auto& i) { return std::tie(i.priority, section_db[i.tags.front()], i.num); }; @@ -695,11 +702,12 @@ sorted by priority.

      )"; out << "

      " << build_timestamp << "

      "; -// print_table(out, issues.begin(), issues.end(), section_db); +// print_table(out, issues, section_db); - for (auto i = issues.cbegin(), e = issues.cend(); i != e;) { - int px = i->priority; - auto j = std::find_if(i, e, [&](issue const & iss){ return iss.priority != px; } ); + while (!issues.empty()) + { + int px = issues.front().priority; + auto end = std::ranges::find_if(issues, std::bind_front(std::ranges::not_equal_to{}, px), &issue::priority); out << "

      "; if (px == 99) { out << "Not Prioritized"; @@ -707,105 +715,89 @@ sorted by priority.

      else { out << "Priority " << px; } - out << " (" << (j-i) << " issues)

      \n"; - print_table(out, i, j, section_db); - i = j; + auto count = end - issues.begin(); + out << " (" << count << " issues)

      \n"; + print_table(out, issues.first(count), section_db); + issues = issues.subspan(count); } print_file_trailer(out); } - -void report_generator::make_sort_by_status(std::vector& issues, fs::path const & filename) { - auto proj = [this](const auto& i) { - return std::make_tuple(lwg::get_status_priority(i.stat), ordered_section(section_db, i), ordered_date(i), i.num); - }; - std::ranges::sort(issues, {}, proj); - +void report_generator::make_sort_by_status_impl(std::span issues, fs::path const & filename, std::string title) { std::ofstream out{filename}; if (!out) throw std::runtime_error{"Failed to open " + filename.string()}; - print_file_header(out, "LWG Index by Status and Section"); + print_file_header(out, "LWG Index by " + title, filename.filename().string(), + "C++ standard library issues list"); out << R"(

      C++ Standard Library Issues List (Revision )" << lwg_issues_xml.get_revision() << R"()

      -

      Index by Status and Section

      +

      Index by )" << title << R"(

      Reference )" << is14882_docno << R"(

      -This document is the Index by Status and Section for the Library Active Issues List, +This document is the Index by )" << title << R"( for the Library Active Issues List, Library Defect Reports and Accepted Issues, and Library Closed Issues List.

      )"; out << "

      " << build_timestamp << "

      "; - for (auto i = issues.cbegin(), e = issues.cend(); i != e;) { - auto const & current_status = i->stat; - auto idattr = current_status; - std::replace(idattr.begin(), idattr.end(), ' ', '_'); - auto j = std::find_if(i, e, [&](issue const & iss){ return iss.stat != current_status; } ); - out << "

      " << current_status << " (" << (j-i) << " issues)

      \n"; - print_table(out, i, j, section_db); - i = j; + while (!issues.empty()) + { + auto const & current_status = issues.front().stat; + auto idattr = spaces_to_underscores(current_status); + auto end = std::ranges::find_if(issues, std::bind_front(std::ranges::not_equal_to{}, current_status), &issue::stat); + auto count = end - issues.begin(); + out << "

      " << current_status << " (" << count << " issues)

      \n"; + print_table(out, issues.first(count), section_db); + issues = issues.subspan(count); } print_file_trailer(out); } -void report_generator::make_sort_by_status_mod_date(std::vector & issues, fs::path const & filename) { +void report_generator::make_sort_by_status(std::span issues, fs::path const & filename) { auto proj = [this](const auto& i) { - return std::make_tuple(lwg::get_status_priority(i.stat), ordered_date(i), ordered_section(section_db, i), i.num); + return std::make_tuple(lwg::get_status_priority(i.stat), ordered_section(section_db, i), ordered_date(i), i.num); }; std::ranges::sort(issues, {}, proj); + make_sort_by_status_impl(issues, filename, "Status and Section"); +} - std::ofstream out{filename}; - if (!out) - throw std::runtime_error{"Failed to open " + filename.string()}; - print_file_header(out, "LWG Index by Status and Date", filename.filename().string(), - "C++ standard library issues list"); - - out << -R"(

      C++ Standard Library Issues List (Revision )" << lwg_issues_xml.get_revision() << R"()

      -

      Index by Status and Date

      -

      Reference )" << is14882_docno << R"(

      -

      -This document is the Index by Status and Date for the Library Active Issues List, -Library Defect Reports and Accepted Issues, and Library Closed Issues List. -

      -)"; - out << "

      " << build_timestamp << "

      "; - - for (auto i = issues.cbegin(), e = issues.cend(); i != e;) { - std::string const & current_status = i->stat; - auto const idattr = spaces_to_underscores(current_status); - auto j = find_if(i, e, [&](issue const & iss){ return iss.stat != current_status; } ); - out << "

      " << current_status << " (" << (j-i) << " issues)

      \n"; - print_table(out, i, j, section_db); - i = j; - } - print_file_trailer(out); +void report_generator::make_sort_by_status_mod_date(std::span issues, fs::path const & filename) { + auto proj = [this](const auto& i) { + return std::make_tuple(lwg::get_status_priority(i.stat), ordered_date(i), ordered_section(section_db, i), i.num); + }; + std::ranges::sort(issues, {}, proj); + make_sort_by_status_impl(issues, filename, "Status and Date"); } -void report_generator::make_sort_by_section(std::vector& issues, fs::path const & filename, bool active_only) { +void report_generator::make_sort_by_section(std::span issues, fs::path const & filename, bool active_only) { auto proj = [](const auto& i) { return std::make_tuple(lwg::get_status_priority(i.stat), ordered_date(i), i.num); }; std::ranges::sort(issues, {}, proj); - auto b = issues.begin(); - auto e = issues.end(); if(active_only) { - b = std::upper_bound(b, e, "Ready", order_by_status{}); - e = find_if(b, e, [](issue const & iss){ return !is_active(iss.stat); }); - } - stable_sort(b, e, order_by_section{section_db}); + auto status_priority = [](const issue& i) { return lwg::get_status_priority(i.stat); }; + // Find the first issue not in Voting, Immediate, or Ready status: + auto first = std::ranges::upper_bound(issues, lwg::get_status_priority("Ready"), {}, status_priority); + // Find the end of the active issues: + auto last = std::ranges::find_if_not(first, issues.end(), is_active, &issue::stat); + // Trim the span to only those active issues: + issues = std::span(first, last); + } + std::ranges::stable_sort(issues, order_by_section{section_db}); std::set mjr_section_open{order_by_major_section{section_db}}; - for (auto const & elem : issues) { - if (is_active_not_ready(elem.stat)) { - mjr_section_open.insert(elem); + if (!active_only) { + for (auto const & elem : issues) { + if (is_active_not_ready(elem.stat)) { + mjr_section_open.insert(elem); + } } } @@ -836,38 +828,37 @@ void report_generator::make_sort_by_section(std::vector& issues, fs::path } out << "

      " << build_timestamp << "

      "; - // Would prefer to use const_iterators from here, but oh well.... - for (auto i = b; i != e;) { - assert(!i->tags.empty()); - std::string current_prefix = section_db[i->tags[0]].prefix; - int current_num = section_db[i->tags[0]].num[0]; - auto j = i; - for (; j != e; ++j) { - if (section_db[j->tags[0]].prefix != current_prefix - || section_db[j->tags[0]].num[0] != current_num) { - break; - } - } - std::string const msn{major_section(section_db[i->tags[0]])}; + auto lookup_section = [this](const issue& i) { + return lookup_major_section(section_db, i); + }; + + while (!issues.empty()) + { + const issue& i = issues.front(); + major_section_key current = lookup_section(i); + auto is_same_section = std::bind_front(std::ranges::equal_to{}, current); + auto j = std::ranges::find_if_not(issues, is_same_section, lookup_section); + auto count = j - issues.begin(); + std::string const msn = to_string(current); auto idattr = spaces_to_underscores(msn); - out << "

      Section " << msn << " (" << (j-i) << " issues)

      \n"; + out << "

      Section " << msn << " (" << count << " issues)

      \n"; if (active_only) { out << "

      (view all issues)

      \n"; } - else if (mjr_section_open.count(*i) > 0) { + else if (mjr_section_open.count(i) > 0) { out << "

      (view only non-Ready open issues)

      \n"; } - print_table(out, i, j, section_db, true); - i = j; + print_table(out, issues.first(count), section_db, true); + issues = issues.subspan(count); } print_file_trailer(out); } // Create individual HTML files for each issue, to make linking easier -void report_generator::make_individual_issues(std::vector const & issues, fs::path const & path) { - assert(std::is_sorted(issues.begin(), issues.end(), order_by_issue_number{})); +void report_generator::make_individual_issues(std::span issues, fs::path const & path) { + assert(std::ranges::is_sorted(issues, {}, &issue::num)); issue_set_by_first_tag const all_issues{ issues.begin(), issues.end()} ; issue_set_by_status const issues_by_status{ issues.begin(), issues.end() }; @@ -884,7 +875,7 @@ void report_generator::make_individual_issues(std::vector const & issues, std::ofstream out{filename}; if (!out) throw std::runtime_error{"Failed to open " + filename.string()}; - print_file_header(out, std::string("Issue ") + num + ": " + lwg::strip_xml_elements(iss.title), + print_file_header(out, "Issue " + num + ": " + lwg::strip_xml_elements(iss.title), // XXX should we use e.g. lwg-active.html#num as the canonical URL for the issue? filename.filename().string(), "C++ library issue. Status: " + iss.stat); diff --git a/src/report_generator.h b/src/report_generator.h index f0c45465de..cadb15543a 100644 --- a/src/report_generator.h +++ b/src/report_generator.h @@ -2,7 +2,7 @@ #define INCLUDE_LWG_REPORT_GENERATOR_H #include -#include +#include #include #include "issues.h" // cannot forward declare the 'section_map' alias, nor the 'LwgIssuesXml' alias @@ -27,41 +27,43 @@ struct report_generator { // A precondition for calling any of these functions is that the list of issues is sorted in numerical order, by issue number. // While nothing disasterous will happen if this precondition is violated, the published issues list will list items // in the wrong order. - void make_active(std::vector const & issues, fs::path const & path, std::string const & diff_report); + void make_active(std::span issues, fs::path const & path, std::string const & diff_report); - void make_defect(std::vector const & issues, fs::path const & path, std::string const & diff_report); + void make_defect(std::span issues, fs::path const & path, std::string const & diff_report); - void make_closed(std::vector const & issues, fs::path const & path, std::string const & diff_report); + void make_closed(std::span issues, fs::path const & path, std::string const & diff_report); // Additional non-standard documents, useful for running LWG meetings - void make_tentative(std::vector const & issues, fs::path const & path); + void make_tentative(std::span issues, fs::path const & path); // publish a document listing all tentative issues that may be acted on during a meeting. - void make_unresolved(std::vector const & issues, fs::path const & path); + void make_unresolved(std::span issues, fs::path const & path); // publish a document listing all non-tentative, non-ready issues that must be reviewed during a meeting. - void make_immediate(std::vector const & issues, fs::path const & path); + void make_immediate(std::span issues, fs::path const & path); // publish a document listing all non-tentative, non-ready issues that must be reviewed during a meeting. - void make_ready(std::vector const & issues, fs::path const & path); + void make_ready(std::span issues, fs::path const & path); // publish a document listing all ready issues for a formal vote - void make_sort_by_num(std::vector& issues, fs::path const & filename); + void make_sort_by_num(std::span issues, fs::path const & filename); - void make_sort_by_priority(std::vector& issues, fs::path const & filename); + void make_sort_by_priority(std::span issues, fs::path const & filename); - void make_sort_by_status(std::vector& issues, fs::path const & filename); + void make_sort_by_status(std::span issues, fs::path const & filename); - void make_sort_by_status_mod_date(std::vector & issues, fs::path const & filename); + void make_sort_by_status_mod_date(std::span issues, fs::path const & filename); - void make_sort_by_section(std::vector& issues, fs::path const & filename, bool active_only = false); + void make_sort_by_section(std::span issues, fs::path const & filename, bool active_only = false); - void make_editors_issues(std::vector const & issues, fs::path const & path); + void make_editors_issues(std::span issues, fs::path const & path); - void make_individual_issues(std::vector const & issues, fs::path const & path); + void make_individual_issues(std::span issues, fs::path const & path); private: + void make_sort_by_status_impl(std::span issues, fs::path const & filename, std::string title); + mailing_info const & lwg_issues_xml; section_map & section_db; }; diff --git a/src/status.cpp b/src/status.cpp index 12badc2b47..91ca6bb05d 100644 --- a/src/status.cpp +++ b/src/status.cpp @@ -5,14 +5,15 @@ #include "status.h" #include +#include #include #include // eases debugging #include namespace { -constexpr char const * LWG_ACTIVE {"lwg-active.html" }; -constexpr char const * LWG_CLOSED {"lwg-closed.html" }; -constexpr char const * LWG_DEFECTS{"lwg-defects.html"}; +constexpr std::string_view LWG_ACTIVE {"lwg-active.html" }; +constexpr std::string_view LWG_CLOSED {"lwg-closed.html" }; +constexpr std::string_view LWG_DEFECTS{"lwg-defects.html"}; } auto lwg::filename_for_status(std::string_view stat) -> std::string_view { @@ -61,7 +62,7 @@ auto lwg::is_active(std::string_view stat) -> bool { } auto lwg::is_active_not_ready(std::string_view stat) -> bool { - return is_active(stat) and stat != "Ready"; + return stat != "Ready" and is_active(stat); } auto lwg::is_defect(std::string_view stat) -> bool { @@ -169,18 +170,12 @@ auto lwg::get_status_priority(std::string_view stat) noexcept -> std::ptrdiff_t }; + auto const i = std::ranges::find(status_priority, stat); #if !defined(DEBUG_SUPPORT) - static auto const first = std::begin(status_priority); - static auto const last = std::end(status_priority); - return std::find_if( first, last, [&](std::string_view str){ return str == stat; } ) - first; -#else // Diagnose when unknown status strings are passed - static auto const first = std::begin(status_priority); - static auto const last = std::end(status_priority); - auto const i = std::find_if( first, last, [&](std::string_view str){ return str == stat; } ); - if(last == i) { + if(std::end(status_priority) == i) { std::cout << "Unknown status: " << stat << std::endl; } - return i - first; #endif + return i - std::begin(status_priority); } diff --git a/src/toc_diff.cpp b/src/toc_diff.cpp index 280df258bc..151a03657d 100644 --- a/src/toc_diff.cpp +++ b/src/toc_diff.cpp @@ -8,13 +8,15 @@ #include #include #include -#include +#include #include +#include +#include namespace fs = std::filesystem; // DEBUG VISUALIZATION TOOL ONLY -void display_issues(std::vector > const & issues) { +void display_issues(std::span> issues) { for( auto const & x : issues) { std::cout << x.first << "\t" << x.second << '\n'; } @@ -24,31 +26,31 @@ void display_issues(std::vector > const & issues) { // PRODUCTION CODE STARTS HERE -auto remove_pending(std::string stat) -> std::string { - typedef std::string::size_type size_type; - if( 0 == stat.find("Pending")) { - stat.erase(size_type{0}, size_type{8}); +auto remove_prefix(std::string_view stat, std::string_view prefix) -> std::string_view { + if (stat.starts_with(prefix)) { + stat.remove_prefix(prefix.size()); } return stat; } -auto remove_tentatively(std::string stat) -> std::string { - typedef std::string::size_type size_type; - if( 0 == stat.find("Tentatively")) { - stat.erase(size_type{0}, size_type{12}); - } - return stat; +auto remove_pending(std::string_view stat) -> std::string_view { + return remove_prefix(stat, "Pending"); +} + + +auto remove_tentatively(std::string_view stat) -> std::string_view { + return remove_prefix(stat, "Tentatively"); } -auto remove_qualifier(std::string const & stat) -> std::string { +auto remove_qualifier(std::string_view stat) -> std::string_view { return remove_tentatively(remove_pending(stat)); } -auto find_file(std::string const & status) -> std::string { - if( 0 == status.find("Tentatively")) { +auto find_file(std::string_view status) -> std::string_view { + if (status.starts_with("Tentatively")) { return "lwg-active.html"; } @@ -76,16 +78,16 @@ auto find_file(std::string const & status) -> std::string { : stat == "Review" ? "lwg-active.html" : stat == "New" ? "lwg-active.html" : stat == "Open" ? "lwg-active.html" - : throw std::runtime_error{"unknown status " + stat}; + : throw std::runtime_error{"unknown status " + std::string(stat)}; } -auto is_active(std::string const & stat) -> bool { +auto is_active(std::string_view stat) -> bool { return find_file(stat) == "lwg-active.html"; } -auto read_issues(std::istream& is) -> std::vector > { +auto read_issues(std::istream& is) -> std::vector> { // parse all issues from the specified stream, 'is'. // Throws 'runtime_error' if *any* parse step fails // @@ -110,7 +112,7 @@ auto read_issues(std::istream& is) -> std::vector > } // Read all issues in table - std::vector > issues; + std::vector> issues; while (true) { i = s.find("

      ", i+4); if (i == std::string::npos) { @@ -166,16 +168,16 @@ struct find_num { struct discover_new_issues { - std::vector > const & old_issues; - std::vector > const & new_issues; + std::vector> const & old_issues; + std::vector> const & new_issues; }; auto operator<<( std::ostream & out, discover_new_issues const & x) -> std::ostream & { - std::vector > const & old_issues = x.old_issues; - std::vector > const & new_issues = x.new_issues; + std::vector> const & old_issues = x.old_issues; + std::vector> const & new_issues = x.new_issues; - std::map > added_issues; + std::map> added_issues; for( auto const & i : new_issues) { auto j = std::lower_bound(old_issues.cbegin(), old_issues.cend(), i.first, find_num{}); if(j == old_issues.end()) { @@ -211,14 +213,14 @@ struct reverse_pair { }; struct discover_changed_issues { - std::vector > const & old_issues; - std::vector > const & new_issues; + std::vector> const & old_issues; + std::vector> const & new_issues; }; auto operator<<( std::ostream & out, discover_changed_issues x) -> std::ostream & { - std::vector > const & old_issues = x.old_issues; - std::vector > const & new_issues = x.new_issues; + std::vector> const & old_issues = x.old_issues; + std::vector> const & new_issues = x.new_issues; std::map, std::vector, reverse_pair> changed_issues; for (auto const & i : new_issues) { @@ -249,37 +251,21 @@ auto operator<<( std::ostream & out, discover_changed_issues x) -> std::ostream } -void count_issues(std::vector > const & issues, unsigned & n_open, unsigned & n_closed) { - n_open = 0; - n_closed = 0; - - for (auto const & elem : issues) { - if (is_active(elem.second)) { - ++n_open; - } - else { - ++n_closed; - } - } +std::pair count_issues(std::span> issues) { + auto n_open = std::ranges::count_if(issues | std::views::elements<1>, is_active); + return { n_open, issues.size() - n_open }; } struct write_summary { - std::vector > const & old_issues; - std::vector > const & new_issues; + std::vector> const & old_issues; + std::vector> const & new_issues; }; auto operator<<( std::ostream & out, write_summary const & x) -> std::ostream & { - std::vector > const & old_issues = x.old_issues; - std::vector > const & new_issues = x.new_issues; - - unsigned n_open_new = 0; - unsigned n_open_old = 0; - unsigned n_closed_new = 0; - unsigned n_closed_old = 0; - count_issues(old_issues, n_open_old, n_closed_old); - count_issues(new_issues, n_open_new, n_closed_new); + auto [n_open_old, n_closed_old] = count_issues(x.old_issues); + auto [n_open_new, n_closed_new] = count_issues(x.new_issues); out << "
    • " << n_open_new << " open issues, "; if (n_open_new >= n_open_old) { @@ -315,8 +301,8 @@ auto operator<<( std::ostream & out, write_summary const & x) -> std::ostream & void print_current_revisions( std::ostream & out - , std::vector > const & old_issues - , std::vector > const & new_issues + , std::vector> const & old_issues + , std::vector> const & new_issues ) { out << "
        \n" << "
      • Summary:
          \n" @@ -330,7 +316,7 @@ void print_current_revisions( std::ostream & out } -auto read_issues(fs::path const & filename) -> std::vector > { +auto read_issues(fs::path const & filename) -> std::vector> { std::ifstream new_html{filename}; if(!new_html.is_open()) { throw std::runtime_error{"Unable to open toc file: " + filename.string()};
    • "; - if (i->priority != 99) { - out << i->priority; + if (i.priority != 99) { + out << i.priority; } out << ""; - print_list(out, i->duplicates, ", "); + print_list(out, i.duplicates, ", "); out << "