From e385ff1c3bc82f01180f50174b915069bae33118 Mon Sep 17 00:00:00 2001 From: Kp Date: Sun, 9 Oct 2022 23:15:20 +0000 Subject: [PATCH] Use std::ranges::find_if instead of std::find_if std::ranges::find_if permits use of a sentinel instead of a full iterator, and supports std::ranges::find as an alternative to certain simple uses of std::find_if. Where possible, use the form that takes a range, rather than the form that takes two iterators. Add a declared, but not defined, default constructor for self_return_iterator to satisfy the standard library's concept `semiregular`, which insists that sentinels be default-constructible, even for those functions that never need to do so. Add a defined, but unused, operator++(postfix) for zip_iterator to satisfy a standard library concept for `forward_iterator`. --- SConstruct | 23 +++++++++++++++++++++++ common/arch/sdl/key.cpp | 6 +++--- common/arch/sdl/rbaudio.cpp | 2 +- common/main/d_zip.h | 9 +++++++++ common/main/selfiter.h | 15 +++++++++++++++ common/ui/keypad.cpp | 6 +++--- d2x-rebirth/main/escort.cpp | 9 +++------ similar/editor/group.cpp | 2 +- similar/main/console.cpp | 2 +- similar/main/digiobj.cpp | 2 +- similar/main/dumpmine.cpp | 2 +- similar/main/fuelcen.cpp | 4 +--- similar/main/game.cpp | 2 +- similar/main/gamesave.cpp | 3 +-- similar/main/menu.cpp | 2 +- similar/main/mission.cpp | 10 +++++----- similar/main/morph.cpp | 2 +- similar/main/multi.cpp | 2 +- similar/main/net_udp.cpp | 6 +++--- similar/main/newmenu.cpp | 9 ++++----- similar/main/playsave.cpp | 2 +- similar/main/scores.cpp | 2 +- similar/main/wall.cpp | 25 +++++++++++++++---------- 23 files changed, 96 insertions(+), 51 deletions(-) diff --git a/SConstruct b/SConstruct index 382ce486f..112560b02 100644 --- a/SConstruct +++ b/SConstruct @@ -2678,6 +2678,29 @@ constexpr literal_as_type operator""_literal_as_type(); ''', msg='whether compiler accepts string literal operator templates'): self.successful_flags['CXXFLAGS'].append('-Wno-gnu-string-literal-operator-template') + @_custom_test + def check_have_std_ranges(self,context,_successflags={'CPPDEFINES' : ['_LIBCPP_ENABLE_EXPERIMENTAL']}): + text = ''' +#include +''' + main = ''' + int a[3]{1, 2, 3}; + return std::ranges::find(a, argc) == a; +''' + if self.Compile(context, text=text, main=main, msg='whether C++ compiler provides std::ranges by default'): + return + # As a special case, put this define in the command-line instead of + # allowing it to be moved to `dxxsconf.h`. This define must be in + # effect before the C++ compiler sees a `#include` of any system + # headers that conditionally define experimental functionality. + # Placing the define on the command line ensures that. If the + # preprocessor definition were in `dxxsconf.h`, then some files might + # include the system header before including the definition. + if self.Compile(context, text=text, main=main, msg='whether C++ compiler provides std::ranges with -D_LIBCPP_ENABLE_EXPERIMENTAL', testflags=_successflags): + self.successful_flags['CPPDEFINES'].extend(*_successflags.items()) + return + raise SCons.Errors.StopError("C++ compiler does not support std::ranges.") + __preferred_compiler_options = ( '-fvisibility=hidden', '-Wduplicated-branches', diff --git a/common/arch/sdl/key.cpp b/common/arch/sdl/key.cpp index a0c968675..51166eb0c 100644 --- a/common/arch/sdl/key.cpp +++ b/common/arch/sdl/key.cpp @@ -42,7 +42,7 @@ pressed_keys keyd_pressed; fix64 keyd_time_when_last_pressed; std::array unicode_frame_buffer; -const std::array key_properties = {{ +constexpr std::array key_properties = {{ { "", 255, SDLK_UNKNOWN }, // 0 { "ESC", 255, SDLK_ESCAPE }, { "1", '1', SDLK_1 }, @@ -511,8 +511,8 @@ window_event_result key_handler(const SDL_KeyboardEvent *const kevent) } //===================================================== - auto re = key_properties.rend(); - auto fi = std::find_if(key_properties.rbegin(), re, [event_keysym](const key_props &k) { return k.sym == event_keysym; }); + const auto re = key_properties.rend(); + const auto &&fi = std::ranges::find(key_properties.rbegin(), re, event_keysym, &key_props::sym); if (fi == re) return window_event_result::ignored; unsigned keycode = std::distance(key_properties.begin(), std::next(fi).base()); diff --git a/common/arch/sdl/rbaudio.cpp b/common/arch/sdl/rbaudio.cpp index b563edda6..a4bf3af13 100644 --- a/common/arch/sdl/rbaudio.cpp +++ b/common/arch/sdl/rbaudio.cpp @@ -89,7 +89,7 @@ void RBAInit() if (s_cd && CD_INDRIVE(SDL_CDStatus(s_cd))) { const auto &&r = partial_const_range(s_cd->track, static_cast(s_cd->numtracks)); - if (std::find_if(r.begin(), r.end(), [](const SDL_CDtrack &t){ return t.type == SDL_AUDIO_TRACK; }) != r.end()) + if (std::ranges::find(r, SDL_AUDIO_TRACK, &SDL_CDtrack::type) != r.end()) { initialised = 1; RBAList(); diff --git a/common/main/d_zip.h b/common/main/d_zip.h index 49e384cf8..4c02c9133 100644 --- a/common/main/d_zip.h +++ b/common/main/d_zip.h @@ -163,6 +163,15 @@ public: d_zip::detail::increment_iterator(*this, index_sequence_type()); return *this; } + /* operator++(int) is currently unused, but is required to satisfy + * the concept check on forward iterator. + */ + zip_iterator operator++(int) + { + auto result = *this; + d_zip::detail::increment_iterator(*this, index_sequence_type()); + return result; + } difference_type operator-(const zip_iterator &i) const { return std::get<0>(*this) - std::get<0>(i); diff --git a/common/main/selfiter.h b/common/main/selfiter.h index bfa751c8b..0719d6596 100644 --- a/common/main/selfiter.h +++ b/common/main/selfiter.h @@ -19,6 +19,21 @@ public: using difference_type = std::ptrdiff_t; using pointer = T *; using reference = T &; + /* A default constructor must be declared to satisfy constraints from + * std::ranges algorithms. The declaration must be visible in an unrelated + * context, so it is public here. However, it must not be used in normal + * operation. self_return_iterator is used by valptridx iterators, which + * do not offer default construction. Therefore, the default constructor + * is declared in order to satisfy the library's concept check, but never + * implemented, since even the library's algorithms never default construct + * an instance of self_return_iterator. + * + * std::sentinel_for -> + * std::semiregular -> + * std::default_initializable -> + * requires { T{}; } + */ + self_return_iterator(); self_return_iterator(T &&i) : T(std::move(i)) { diff --git a/common/ui/keypad.cpp b/common/ui/keypad.cpp index 6f4e77803..06c7997df 100644 --- a/common/ui/keypad.cpp +++ b/common/ui/keypad.cpp @@ -72,10 +72,10 @@ typedef PHYSFSX_gets_line_t<100>::line_t keypad_input_line_t; static keypad_input_line_t::const_iterator find_fake_comma(keypad_input_line_t::const_iterator i, keypad_input_line_t::const_iterator e) { - auto is_fake_comma = [](char c) { + const auto is_fake_comma = [](const char c) { return !c || static_cast(c) == 179; }; - return std::find_if(i, e, is_fake_comma); + return std::ranges::find_if(i, e, is_fake_comma); } template @@ -84,7 +84,7 @@ static keypad_input_line_t::const_iterator set_row(keypad_input_line_t::const_it const auto oe = r.end(); auto ob = r.begin(); if (append) - ob = std::find(ob, oe, 0); + ob = std::ranges::find(ob, oe, 0); auto comma0 = find_fake_comma(i, e); if (comma0 == e) /* Start not found */ diff --git a/d2x-rebirth/main/escort.cpp b/d2x-rebirth/main/escort.cpp index ee27bf145..dcca19721 100644 --- a/d2x-rebirth/main/escort.cpp +++ b/d2x-rebirth/main/escort.cpp @@ -687,16 +687,13 @@ static std::pair exi return vcsegptr(s)->special == segment_special::fuelcen; }; const auto &&rb = partial_const_range(bfs_list, length); - const auto i = std::find_if(rb.begin(), rb.end(), predicate); + const auto &&i = std::ranges::find_if(rb, predicate); if (i != rb.end()) return {*i, d_unique_buddy_state::Escort_goal_reachability::reachable}; } { - const auto &rh = vcsegptridx; - const auto &&predicate = [](const shared_segment &s) { - return s.special == segment_special::fuelcen; - }; - const auto i = std::find_if(rh.begin(), rh.end(), predicate); + const auto &&rh = make_range(vcsegptridx); + const auto &&i = std::ranges::find(rh, segment_special::fuelcen, &shared_segment::special); if (i != rh.end()) return {*i, d_unique_buddy_state::Escort_goal_reachability::unreachable}; } diff --git a/similar/editor/group.cpp b/similar/editor/group.cpp index c250762f3..d15b5f5e9 100644 --- a/similar/editor/group.cpp +++ b/similar/editor/group.cpp @@ -564,7 +564,7 @@ static int med_copy_group(const unsigned delta_flag, const vmsegptridx_t base_se auto gb = GroupList[current_group].segments.begin(); auto ge = GroupList[current_group].segments.end(); auto gp = Groupsegp[current_group]; - auto gi = std::find_if(gb, ge, [gp](const segnum_t segnum){ return vcsegptr(segnum) == gp; }); + const auto &&gi = std::ranges::find_if(gb, ge, [gp](const segnum_t segnum) { return vcsegptr(segnum) == gp; }); int gs_index = (gi == ge) ? 0 : std::distance(gb, gi); GroupList[new_current_group] = GroupList[current_group]; diff --git a/similar/main/console.cpp b/similar/main/console.cpp index bb374778b..d86873f13 100644 --- a/similar/main/console.cpp +++ b/similar/main/console.cpp @@ -113,7 +113,7 @@ static void con_scrub_markup(const std::span buffer) { const auto b = buffer.begin(); const auto e = buffer.end(); - const auto i = std::find_if(b, e, [](const char c) { return c == CC_COLOR || c == CC_LSPACING || c == CC_UNDERLINE; }); + const auto &&i = std::ranges::find_if(b, e, [](const char c) { return c == CC_COLOR || c == CC_LSPACING || c == CC_UNDERLINE; }); if (i == e) return; auto p1 = i; diff --git a/similar/main/digiobj.cpp b/similar/main/digiobj.cpp index 6734d27ad..710c1e9d5 100644 --- a/similar/main/digiobj.cpp +++ b/similar/main/digiobj.cpp @@ -113,7 +113,7 @@ static void digi_kill_sound(sound_object &s) static std::pair find_sound_object_flags0(sound_objects_t &SoundObjects) { const auto eso = SoundObjects.end(); - const auto i = std::find_if(SoundObjects.begin(), eso, [](sound_object &so) { return so.flags == 0; }); + const auto &&i = std::ranges::find(SoundObjects.begin(), eso, 0, &sound_object::flags); return {i, eso}; } diff --git a/similar/main/dumpmine.cpp b/similar/main/dumpmine.cpp index d090d170c..be54b4b03 100644 --- a/similar/main/dumpmine.cpp +++ b/similar/main/dumpmine.cpp @@ -616,7 +616,7 @@ static void write_trigger_text(PHYSFS_File *my_file) // Find which wall this trigger is connected to. const auto &&we = vcwallptr.end(); - const auto &&wi = std::find_if(vcwallptr.begin(), we, [i](const wall *const p) { return p->trigger == i; }); + const auto &&wi = std::ranges::find(vcwallptr.begin(), we, i, &wall::trigger); if (wi == we) err_printf(my_file, "Error: Trigger %i is not connected to any wall, so it can never be triggered.", underlying_value(i)); else diff --git a/similar/main/fuelcen.cpp b/similar/main/fuelcen.cpp index 1cff26e7c..3901ba7fe 100644 --- a/similar/main/fuelcen.cpp +++ b/similar/main/fuelcen.cpp @@ -667,9 +667,7 @@ void init_all_matcens(void) station.Enabled = 0; station.Disable_time = 0; // Make sure this fuelcen is pointed at by a matcen. - if (std::find_if(robot_range.begin(), robot_range.end(), [i = i](const matcen_info &mi) { - return mi.fuelcen_num == i; - }) == robot_range.end()) + if (std::ranges::find(robot_range, i, &matcen_info::fuelcen_num) == robot_range.end()) { station.Lives = 0; LevelError("Station %i has type robotmaker, but no robotmaker uses it; ignoring.", underlying_value(i)); diff --git a/similar/main/game.cpp b/similar/main/game.cpp index 4b6695a4c..5bb975937 100644 --- a/similar/main/game.cpp +++ b/similar/main/game.cpp @@ -2256,7 +2256,7 @@ static std::pair(*i); diff --git a/similar/main/mission.cpp b/similar/main/mission.cpp index ecdc8a7db..1f0ea737a 100644 --- a/similar/main/mission.cpp +++ b/similar/main/mission.cpp @@ -879,7 +879,7 @@ static void set_briefing_filename(d_fname &f, const char *const v) auto a = [](char c) { return !c || c == '.'; }; - auto i = std::find_if(v, next(v, f.size() - sizeof(tex)), a); + const auto &&i = std::ranges::find_if(v, next(v, f.size() - sizeof(tex)), a); std::size_t d = std::distance(v, i); set_briefing_filename(f, {v, d}); } @@ -889,7 +889,7 @@ static void record_briefing(d_fname &f, std::array &buf) const auto v = get_value(buf.data()); if (!v) return; - const std::size_t d = std::distance(v, std::find_if(v, buf.end(), null_or_space)); + const std::size_t d = std::distance(v, std::ranges::find_if(v, buf.end(), null_or_space)); if (d >= FILENAME_LEN) return; { @@ -1046,8 +1046,8 @@ static const char *load_mission(const mle *const mission) if (!PHYSFSX_fgets(buf, mfile)) break; auto &line = buf.line(); - auto s = std::find_if(line.begin(), line.end(), null_or_space); - if (i.copy_if(buf.line(), std::distance(line.begin(), s))) + const auto &&s = std::ranges::find_if(line, null_or_space); + if (i.copy_if(line, std::distance(line.begin(), s))) { ++level_names_loaded; } @@ -1092,7 +1092,7 @@ static const char *load_mission(const mle *const mission) auto a = [](char c) { return isspace(static_cast(c)); }; - auto s = std::find_if(lb, t, a); + const auto &&s = std::ranges::find_if(lb, t, a); if (name.copy_if(line, std::distance(lb, s))) { unsigned long ls = strtoul(t + 1, &ip, 10); diff --git a/similar/main/morph.cpp b/similar/main/morph.cpp index 18786fc33..28bd6a134 100644 --- a/similar/main/morph.cpp +++ b/similar/main/morph.cpp @@ -476,7 +476,7 @@ void morph_start(d_level_unique_morph_object_state &LevelUniqueMorphObjectState, auto &mo = *pmo.get(); return mo.obj->type == OBJ_NONE || mo.obj->signature != mo.Morph_sig; }; - const auto moi = std::find_if(mob, moe, mop); + const auto moi = std::ranges::find_if(mob, moe, mop); if (moi == moe) //no free slots return; diff --git a/similar/main/multi.cpp b/similar/main/multi.cpp index 9edfd29fe..3c0dcf005 100644 --- a/similar/main/multi.cpp +++ b/similar/main/multi.cpp @@ -3428,7 +3428,7 @@ texture_index find_goal_texture(const d_level_unique_tmap_info_state &LevelUniqu return (i.flags & tmi_flag); }; const auto begin = r.begin(); - const auto idx = std::distance(begin, std::find_if(begin, r.end(), predicate)); + const auto idx = std::distance(begin, std::ranges::find_if(begin, r.end(), predicate)); return idx; } diff --git a/similar/main/net_udp.cpp b/similar/main/net_udp.cpp index 0dd40a92a..466ce306b 100644 --- a/similar/main/net_udp.cpp +++ b/similar/main/net_udp.cpp @@ -3034,7 +3034,7 @@ static unsigned net_udp_send_request(void) // game, non-zero if there is some problem. auto b = Netgame.players.begin(); auto e = Netgame.players.end(); - auto i = std::find_if(b, e, [](const netplayer_info &ni) { return ni.connected != player_connection_status::disconnected; }); + const auto &&i = std::ranges::find(b, e, player_connection_status::disconnected, &netplayer_info::connected); if (i == e) { Assert(false); @@ -3097,7 +3097,7 @@ static void net_udp_process_game_info_light(const uint8_t *data, uint_fast32_t, menu->num_active_udp_changed = 1; auto r = partial_range(menu->Active_udp_games, menu->num_active_udp_games); - auto i = std::find_if(r.begin(), r.end(), [&recv_game](const UDP_netgame_info_lite &g) { return !d_stricmp(g.game_name.data(), recv_game.game_name.data()) && g.GameID == recv_game.GameID; }); + const auto &&i = std::ranges::find_if(r, [&recv_game](const UDP_netgame_info_lite &g) { return !d_stricmp(g.game_name.data(), recv_game.game_name.data()) && g.GameID == recv_game.GameID; }); if (i == menu->Active_udp_games.end()) { return; @@ -5641,7 +5641,7 @@ void net_udp_noloss_process_queue(fix64 time) { const auto b = UDP_mdata_queue.begin(); const auto e = std::next(b, UDP_mdata_queue_highest); - const auto first_used_entry = std::find_if(b, e, [](const UDP_mdata_store &m) { return m.used; }); + const auto &&first_used_entry = std::ranges::find_if(b, e, [](const UDP_mdata_store &m) { return m.used; }); if (first_used_entry != b) { std::move(first_used_entry, e, b); diff --git a/similar/main/newmenu.cpp b/similar/main/newmenu.cpp index 48989156b..fddcf908b 100644 --- a/similar/main/newmenu.cpp +++ b/similar/main/newmenu.cpp @@ -683,13 +683,12 @@ static void newmenu_scroll(newmenu *const menu, const int amount) return; } const auto &range = menu->items; - const auto predicate = [](const newmenu_item &n) { - return n.type != nm_type::text; - }; - const auto first = std::find_if(range.begin(), range.end(), predicate); + const auto find_value = nm_type::text; + const auto find_projection = &newmenu_item::type; + const auto &&first = std::ranges::find(range, find_value, find_projection); if (first == range.end()) return; - const auto rlast = std::find_if(range.rbegin(), std::reverse_iterator(first), predicate).base(); + const auto &&rlast = std::ranges::find(range.rbegin(), std::reverse_iterator(first), find_value, find_projection).base(); /* `first == rlast` should not happen, since that would mean that * there are no elements in `range` for which `predicate` is true. * If there are no such elements, then `first == range.end()` should diff --git a/similar/main/playsave.cpp b/similar/main/playsave.cpp index 81e401b7c..0dc6a08a6 100644 --- a/similar/main/playsave.cpp +++ b/similar/main/playsave.cpp @@ -1286,7 +1286,7 @@ static std::array::pointer, 2> find_hli_entry(cons const auto &&a = [p = &*mission_filename](const hli &h) { return !d_stricmp(h.Shortname.data(), p); }; - const auto i = std::find_if(r.begin(), r.end(), a); + const auto &&i = std::ranges::find_if(r, a); return {{&*i, r.end()}}; } } diff --git a/similar/main/scores.cpp b/similar/main/scores.cpp index 1393c6fb7..2952aeca3 100644 --- a/similar/main/scores.cpp +++ b/similar/main/scores.cpp @@ -290,7 +290,7 @@ void scores_maybe_add_player() const auto end_score_stats = std::end(scores.stats); /* Find the position at which the player's score should be placed. */ - const auto iter_position = std::find_if(begin_score_stats, end_score_stats, predicate); + const auto &&iter_position = std::ranges::find_if(begin_score_stats, end_score_stats, predicate); const auto position = std::distance(begin_score_stats, iter_position); /* If iter_position == end_score_stats, then the player's score does * not beat any of the existing high scores. Include a special case diff --git a/similar/main/wall.cpp b/similar/main/wall.cpp index c4f6059e2..b4bb522d1 100644 --- a/similar/main/wall.cpp +++ b/similar/main/wall.cpp @@ -436,8 +436,9 @@ void wall_open_door(const vmsegptridx_t seg, const sidenum_t side) auto &vmactdoorptr = ActiveDoors.vmptr; if (w->state == wall_state::closing) { //closing, so reuse door const auto &&r = make_range(vmactdoorptr); - const auto &&i = std::find_if(r.begin(), r.end(), find_active_door_predicate(wall_num)); - if (i == r.end()) // likely in demo playback or multiplayer + const auto &&re = r.end(); + const auto &&i = std::ranges::find_if(r.begin(), re, find_active_door_predicate(wall_num)); + if (i == re) // likely in demo playback or multiplayer { const auto c = ActiveDoors.get_count(); ActiveDoors.set_count(c + 1); @@ -548,8 +549,9 @@ void start_wall_cloak(const vmsegptridx_t seg, const sidenum_t side) if (w->state == wall_state::decloaking) { //decloaking, so reuse door const auto &&r = make_range(CloakingWalls.vmptr); - const auto i = std::find_if(r.begin(), r.end(), find_cloaked_wall_predicate(w)); - if (i == r.end()) + const auto &&re = r.end(); + const auto &&i = std::ranges::find_if(r.begin(), re, find_cloaked_wall_predicate(w)); + if (i == re) { d_debugbreak(); return; @@ -624,8 +626,9 @@ void start_wall_decloak(const vmsegptridx_t seg, const sidenum_t side) auto &CloakingWalls = LevelUniqueWallSubsystemState.CloakingWalls; if (w->state == wall_state::cloaking) { //cloaking, so reuse door const auto &&r = make_range(CloakingWalls.vmptr); - const auto i = std::find_if(r.begin(), r.end(), find_cloaked_wall_predicate(w)); - if (i == r.end()) + const auto &&re = r.end(); + const auto &&i = std::ranges::find_if(r.begin(), re, find_cloaked_wall_predicate(w)); + if (i == re) { d_debugbreak(); return; @@ -796,8 +799,9 @@ void wall_close_door(wall_array &Walls, const vmsegptridx_t seg, const sidenum_t if (w->state == wall_state::opening) { //reuse door const auto &&r = make_range(vmactdoorptr); - const auto &&i = std::find_if(r.begin(), r.end(), find_active_door_predicate(wall_num)); - if (i == r.end()) + const auto &&re = r.end(); + const auto &&i = std::ranges::find_if(r.begin(), re, find_active_door_predicate(wall_num)); + if (i == re) { d_debugbreak(); return; @@ -1457,9 +1461,10 @@ void d_level_unique_stuck_object_state::add_stuck_object(fvcwallptr &vcwallptr, void d_level_unique_stuck_object_state::remove_stuck_object(const vcobjidx_t obj) { auto &&pr = partial_range(Stuck_objects, Num_stuck_objects); + auto &&pre = pr.end(); const auto predicate = [obj](const stuckobj &so) { return so.objnum == obj; }; - const auto i = std::find_if(pr.begin(), pr.end(), predicate); - if (i == pr.end()) + const auto &&i = std::ranges::find_if(pr.begin(), pre, predicate); + if (i == pre) /* Objects enter this function if they are able to become stuck, * without regard to whether they actually are stuck. If the * object terminated without being stuck in a wall, then it will