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`.
This commit is contained in:
Kp 2022-10-09 23:15:20 +00:00
parent f754b2d33a
commit e385ff1c3b
23 changed files with 96 additions and 51 deletions

View File

@ -2678,6 +2678,29 @@ constexpr literal_as_type<T, v...> 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 <algorithm>
'''
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',

View File

@ -42,7 +42,7 @@ pressed_keys keyd_pressed;
fix64 keyd_time_when_last_pressed;
std::array<unsigned char, KEY_BUFFER_SIZE> unicode_frame_buffer;
const std::array<key_props, 256> key_properties = {{
constexpr std::array<key_props, 256> 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());

View File

@ -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<unsigned>(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();

View File

@ -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);

View File

@ -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))
{

View File

@ -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<uint8_t>(c) == 179;
};
return std::find_if(i, e, is_fake_comma);
return std::ranges::find_if(i, e, is_fake_comma);
}
template <bool append, char eor>
@ -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 */

View File

@ -687,16 +687,13 @@ static std::pair<icsegidx_t, d_unique_buddy_state::Escort_goal_reachability> 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};
}

View File

@ -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];

View File

@ -113,7 +113,7 @@ static void con_scrub_markup(const std::span<char> 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;

View File

@ -113,7 +113,7 @@ static void digi_kill_sound(sound_object &s)
static std::pair<sound_objects_t::iterator, sound_objects_t::iterator> 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};
}

View File

@ -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

View File

@ -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));

View File

@ -2256,7 +2256,7 @@ static std::pair<d_flickering_light_state::Flickering_light_array_t::iterator, d
return f.segnum == segnum && f.sidenum == sidenum; //found it!
};
const auto &&pe = pr.end();
return {std::find_if(pr.begin(), pe, predicate), pe};
return {std::ranges::find_if(pr.begin(), pe, predicate), pe};
}
static void update_flicker(d_flickering_light_state &fls, const vmsegidx_t segnum, const sidenum_t sidenum, const fix timer)

View File

@ -1197,9 +1197,8 @@ static int load_game_data(
for (auto iter = Triggers.vmptridx.begin(); iter != Triggers.vmptridx.end();)
{
const auto i = (*iter).get_unchecked_index();
auto a = [i](const wall &w) { return w.trigger == i; };
// Find which wall this trigger is connected to.
auto w = std::find_if(wr.begin(), wr.end(), a);
const auto &&w = std::ranges::find(wr, i, &wall::trigger);
if (w == wr.end())
{
remove_trigger_num(Triggers, Walls.vmptr, i);

View File

@ -1264,7 +1264,7 @@ void screen_resolution_menu::check_apply_preset_resolution() const
return 0;
return ni.value;
};
const auto i = std::find_if(r.begin(), r.end(), predicate);
const auto &&i = std::ranges::find_if(r, predicate);
if (i == r.end())
return;
const auto requested_mode = std::get<0>(*i);

View File

@ -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<char, PATH_MAX> &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<unsigned>(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);

View File

@ -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;

View File

@ -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;
}

View File

@ -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);

View File

@ -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<newmenu_item *>(first), predicate).base();
const auto &&rlast = std::ranges::find(range.rbegin(), std::reverse_iterator<newmenu_item *>(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

View File

@ -1286,7 +1286,7 @@ static std::array<std::array<hli, MAX_MISSIONS>::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()}};
}
}

View File

@ -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

View File

@ -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