diff --git a/src/CommandLine.cxx b/src/CommandLine.cxx
index ac53cf53a..770cc3bc7 100644
--- a/src/CommandLine.cxx
+++ b/src/CommandLine.cxx
@@ -36,7 +36,6 @@
 #include "fs/Traits.hxx"
 #include "fs/FileSystem.hxx"
 #include "fs/StandardDirectory.hxx"
-#include "util/Macros.hxx"
 #include "util/Domain.hxx"
 #include "util/OptionDef.hxx"
 #include "util/OptionParser.hxx"
@@ -385,7 +384,7 @@ ParseCommandLine(int argc, char **argv, struct options &options,
 #ifdef _UNICODE
 		wchar_t buffer[MAX_PATH];
 		auto result = MultiByteToWideChar(CP_ACP, 0, config_file, -1,
-						  buffer, ARRAY_SIZE(buffer));
+						  buffer, std::size(buffer));
 		if (result <= 0)
 			throw MakeLastError("MultiByteToWideChar() failed");
 
diff --git a/src/archive/ArchiveList.cxx b/src/archive/ArchiveList.cxx
index 1a2b228e8..f30732715 100644
--- a/src/archive/ArchiveList.cxx
+++ b/src/archive/ArchiveList.cxx
@@ -24,7 +24,8 @@
 #include "plugins/Bzip2ArchivePlugin.hxx"
 #include "plugins/Iso9660ArchivePlugin.hxx"
 #include "plugins/ZzipArchivePlugin.hxx"
-#include "util/Macros.hxx"
+
+#include <iterator>
 
 #include <assert.h>
 #include <string.h>
@@ -43,7 +44,7 @@ const ArchivePlugin *const archive_plugins[] = {
 };
 
 /** which plugins have been initialized successfully? */
-static bool archive_plugins_enabled[ARRAY_SIZE(archive_plugins) - 1];
+static bool archive_plugins_enabled[std::size(archive_plugins) - 1];
 
 #define archive_plugins_for_each_enabled(plugin) \
 	archive_plugins_for_each(plugin) \
diff --git a/src/command/AllCommands.cxx b/src/command/AllCommands.cxx
index be039f22f..dde17d85c 100644
--- a/src/command/AllCommands.cxx
+++ b/src/command/AllCommands.cxx
@@ -41,7 +41,6 @@
 #include "Instance.hxx"
 #include "client/Client.hxx"
 #include "client/Response.hxx"
-#include "util/Macros.hxx"
 #include "util/Tokenizer.hxx"
 #include "util/StringAPI.hxx"
 
@@ -49,6 +48,8 @@
 #include "StickerCommands.hxx"
 #endif
 
+#include <iterator>
+
 #include <assert.h>
 #include <string.h>
 
@@ -208,7 +209,7 @@ static constexpr struct command commands[] = {
 	{ "volume", PERMISSION_CONTROL, 1, 1, handle_volume },
 };
 
-static constexpr unsigned num_commands = ARRAY_SIZE(commands);
+static constexpr unsigned num_commands = std::size(commands);
 
 static bool
 command_available(gcc_unused const Partition &partition,
diff --git a/src/config/Templates.cxx b/src/config/Templates.cxx
index 0641b03bb..01205c3d0 100644
--- a/src/config/Templates.cxx
+++ b/src/config/Templates.cxx
@@ -19,7 +19,8 @@
 
 #include "Templates.hxx"
 #include "Option.hxx"
-#include "util/Macros.hxx"
+
+#include <iterator>
 
 #include <string.h>
 
@@ -77,7 +78,7 @@ const ConfigTemplate config_param_templates[] = {
 };
 
 static constexpr unsigned n_config_param_templates =
-	ARRAY_SIZE(config_param_templates);
+	std::size(config_param_templates);
 
 static_assert(n_config_param_templates == unsigned(ConfigOption::MAX),
 	      "Wrong number of config_param_templates");
@@ -95,7 +96,7 @@ const ConfigTemplate config_block_templates[] = {
 };
 
 static constexpr unsigned n_config_block_templates =
-	ARRAY_SIZE(config_block_templates);
+	std::size(config_block_templates);
 
 static_assert(n_config_block_templates == unsigned(ConfigBlockOption::MAX),
 	      "Wrong number of config_block_templates");
diff --git a/src/decoder/DecoderList.cxx b/src/decoder/DecoderList.cxx
index cf6d47958..b01f6381e 100644
--- a/src/decoder/DecoderList.cxx
+++ b/src/decoder/DecoderList.cxx
@@ -46,9 +46,10 @@
 #include "plugins/MpcdecDecoderPlugin.hxx"
 #include "plugins/FluidsynthDecoderPlugin.hxx"
 #include "plugins/SidplayDecoderPlugin.hxx"
-#include "util/Macros.hxx"
 #include "util/RuntimeError.hxx"
 
+#include <iterator>
+
 #include <string.h>
 
 const struct DecoderPlugin *const decoder_plugins[] = {
@@ -117,7 +118,7 @@ const struct DecoderPlugin *const decoder_plugins[] = {
 };
 
 static constexpr unsigned num_decoder_plugins =
-	ARRAY_SIZE(decoder_plugins) - 1;
+	std::size(decoder_plugins) - 1;
 
 /** which plugins have been initialized successfully? */
 bool decoder_plugins_enabled[num_decoder_plugins];
diff --git a/src/decoder/plugins/AdPlugDecoderPlugin.cxx b/src/decoder/plugins/AdPlugDecoderPlugin.cxx
index eecdbafe2..3188ca91c 100644
--- a/src/decoder/plugins/AdPlugDecoderPlugin.cxx
+++ b/src/decoder/plugins/AdPlugDecoderPlugin.cxx
@@ -23,7 +23,6 @@
 #include "CheckAudioFormat.hxx"
 #include "fs/Path.hxx"
 #include "util/Domain.hxx"
-#include "util/Macros.hxx"
 #include "util/StringView.hxx"
 #include "Log.hxx"
 
@@ -71,7 +70,7 @@ adplug_file_decode(DecoderClient &client, Path path_fs)
 			break;
 
 		int16_t buffer[2048];
-		constexpr unsigned frames_per_buffer = ARRAY_SIZE(buffer) / 2;
+		constexpr unsigned frames_per_buffer = std::size(buffer) / 2;
 		opl.update(buffer, frames_per_buffer);
 		cmd = client.SubmitData(nullptr,
 					buffer, sizeof(buffer),
diff --git a/src/decoder/plugins/FluidsynthDecoderPlugin.cxx b/src/decoder/plugins/FluidsynthDecoderPlugin.cxx
index 06982317b..fd4590dd7 100644
--- a/src/decoder/plugins/FluidsynthDecoderPlugin.cxx
+++ b/src/decoder/plugins/FluidsynthDecoderPlugin.cxx
@@ -22,7 +22,6 @@
 #include "CheckAudioFormat.hxx"
 #include "fs/Path.hxx"
 #include "util/Domain.hxx"
-#include "util/Macros.hxx"
 #include "Log.hxx"
 
 #include <fluidsynth.h>
@@ -169,7 +168,7 @@ fluidsynth_file_decode(DecoderClient &client, Path path_fs)
 	DecoderCommand cmd;
 	while (fluid_player_get_status(player) == FLUID_PLAYER_PLAYING) {
 		int16_t buffer[2048];
-		const unsigned max_frames = ARRAY_SIZE(buffer) / 2;
+		const unsigned max_frames = std::size(buffer) / 2;
 
 		/* read samples from fluidsynth and send them to the
 		   MPD core */
diff --git a/src/decoder/plugins/MpcdecDecoderPlugin.cxx b/src/decoder/plugins/MpcdecDecoderPlugin.cxx
index 42f965bd9..0521fa8fa 100644
--- a/src/decoder/plugins/MpcdecDecoderPlugin.cxx
+++ b/src/decoder/plugins/MpcdecDecoderPlugin.cxx
@@ -24,13 +24,14 @@
 #include "pcm/Traits.hxx"
 #include "tag/Handler.hxx"
 #include "util/Domain.hxx"
-#include "util/Macros.hxx"
 #include "util/Clamp.hxx"
 #include "util/ScopeExit.hxx"
 #include "Log.hxx"
 
 #include <mpc/mpcdec.h>
 
+#include <iterator>
+
 #include <math.h>
 
 struct mpc_decoder_data {
@@ -216,7 +217,7 @@ mpcdec_decode(DecoderClient &client, InputStream &is)
 		mpc_uint32_t ret = frame.samples;
 		ret *= info.channels;
 
-		MpcdecSampleTraits::value_type chunk[ARRAY_SIZE(sample_buffer)];
+		MpcdecSampleTraits::value_type chunk[std::size(sample_buffer)];
 		mpc_to_mpd_buffer(chunk, sample_buffer, ret);
 
 		long bit_rate = unsigned(frame.bits) * audio_format.sample_rate
diff --git a/src/decoder/plugins/SidplayDecoderPlugin.cxx b/src/decoder/plugins/SidplayDecoderPlugin.cxx
index ac9a8835a..d5f8c0b8b 100644
--- a/src/decoder/plugins/SidplayDecoderPlugin.cxx
+++ b/src/decoder/plugins/SidplayDecoderPlugin.cxx
@@ -29,7 +29,6 @@
 #include "fs/io/FileReader.hxx"
 #include "util/RuntimeError.hxx"
 #endif
-#include "util/Macros.hxx"
 #include "util/StringFormat.hxx"
 #include "util/StringView.hxx"
 #include "util/Domain.hxx"
@@ -52,6 +51,8 @@
 #include <sidplay/utils/SidDatabase.h>
 #endif
 
+#include <iterator>
+
 #include <string.h>
 #include <stdio.h>
 
@@ -392,7 +393,7 @@ sidplay_file_decode(DecoderClient &client, Path path_fs)
 	do {
 		short buffer[4096];
 
-		const auto result = player.play(buffer, ARRAY_SIZE(buffer));
+		const auto result = player.play(buffer, std::size(buffer));
 		if (result <= 0)
 			break;
 
@@ -421,7 +422,7 @@ sidplay_file_decode(DecoderClient &client, Path path_fs)
 
 			/* ignore data until target time is reached */
 			while (data_time < target_time &&
-			       player.play(buffer, ARRAY_SIZE(buffer)) > 0)
+			       player.play(buffer, std::size(buffer)) > 0)
 				data_time = player.time();
 
 			client.CommandFinished();
diff --git a/src/decoder/plugins/VorbisDecoderPlugin.cxx b/src/decoder/plugins/VorbisDecoderPlugin.cxx
index 7ee7cbd73..dfb38c2b5 100644
--- a/src/decoder/plugins/VorbisDecoderPlugin.cxx
+++ b/src/decoder/plugins/VorbisDecoderPlugin.cxx
@@ -29,7 +29,6 @@
 #include "input/Reader.hxx"
 #include "OggCodec.hxx"
 #include "pcm/Interleave.hxx"
-#include "util/Macros.hxx"
 #include "util/ScopeExit.hxx"
 #include "CheckAudioFormat.hxx"
 #include "tag/Handler.hxx"
@@ -41,6 +40,7 @@
 #include <tremor/ivorbiscodec.h>
 #endif /* HAVE_TREMOR */
 
+#include <iterator>
 #include <stdexcept>
 
 class VorbisDecoder final : public OggDecoder {
@@ -209,7 +209,7 @@ VorbisDecoder::SubmitSomePcm()
 
 	out_sample_t buffer[4096];
 	const unsigned channels = audio_format.channels;
-	size_t max_frames = ARRAY_SIZE(buffer) / channels;
+	size_t max_frames = std::size(buffer) / channels;
 	size_t n_frames = std::min(size_t(result), max_frames);
 
 #ifdef HAVE_TREMOR
diff --git a/src/decoder/plugins/WavpackDecoderPlugin.cxx b/src/decoder/plugins/WavpackDecoderPlugin.cxx
index 4f5657220..080bc09f3 100644
--- a/src/decoder/plugins/WavpackDecoderPlugin.cxx
+++ b/src/decoder/plugins/WavpackDecoderPlugin.cxx
@@ -24,7 +24,6 @@
 #include "CheckAudioFormat.hxx"
 #include "tag/Handler.hxx"
 #include "fs/Path.hxx"
-#include "util/Macros.hxx"
 #include "util/Alloc.hxx"
 #include "util/ScopeExit.hxx"
 #include "util/RuntimeError.hxx"
@@ -32,6 +31,7 @@
 #include <wavpack/wavpack.h>
 
 #include <algorithm>
+#include <iterator>
 #include <memory>
 
 #include <cstdlib>
@@ -235,7 +235,7 @@ wavpack_decode(DecoderClient &client, WavpackContext *wpc, bool can_seek)
 
 	/* wavpack gives us all kind of samples in a 32-bit space */
 	int32_t chunk[1024];
-	const uint32_t samples_requested = ARRAY_SIZE(chunk) /
+	const uint32_t samples_requested = std::size(chunk) /
 		audio_format.channels;
 
 	DecoderCommand cmd = client.GetCommand();
diff --git a/src/fs/NarrowPath.hxx b/src/fs/NarrowPath.hxx
index 7375ea278..b8cca2d45 100644
--- a/src/fs/NarrowPath.hxx
+++ b/src/fs/NarrowPath.hxx
@@ -21,7 +21,6 @@
 #define MPD_FS_NARROW_PATH_HXX
 
 #include "Path.hxx"
-#include "util/Macros.hxx"
 
 #ifdef _UNICODE
 #include "lib/icu/Win32.hxx"
diff --git a/src/input/Registry.cxx b/src/input/Registry.cxx
index 1696c936f..c5414ea96 100644
--- a/src/input/Registry.cxx
+++ b/src/input/Registry.cxx
@@ -19,7 +19,6 @@
 
 #include "Registry.hxx"
 #include "InputPlugin.hxx"
-#include "util/Macros.hxx"
 #include "plugins/TidalInputPlugin.hxx"
 #include "plugins/QobuzInputPlugin.hxx"
 #include "config.h"
@@ -83,7 +82,7 @@ const InputPlugin *const input_plugins[] = {
 	nullptr
 };
 
-bool input_plugins_enabled[ARRAY_SIZE(input_plugins) - 1];
+bool input_plugins_enabled[std::size(input_plugins) - 1];
 
 bool
 HasRemoteTagScanner(const char *uri) noexcept
diff --git a/src/lib/icu/Converter.cxx b/src/lib/icu/Converter.cxx
index 3ceb8960f..3e464a3af 100644
--- a/src/lib/icu/Converter.cxx
+++ b/src/lib/icu/Converter.cxx
@@ -18,13 +18,13 @@
  */
 
 #include "Converter.hxx"
-#include "util/Macros.hxx"
 #include "util/AllocatedString.hxx"
 #include "util/AllocatedArray.hxx"
 #include "util/ConstBuffer.hxx"
 #include "util/FormatString.hxx"
 #include "config.h"
 
+#include <iterator>
 #include <stdexcept>
 
 #include <string.h>
@@ -115,7 +115,7 @@ IcuConverter::ToUTF8(const char *s) const
 
 	UErrorCode code = U_ZERO_ERROR;
 
-	ucnv_toUnicode(converter, &target, buffer + ARRAY_SIZE(buffer),
+	ucnv_toUnicode(converter, &target, buffer + std::size(buffer),
 		       &source, source + strlen(source),
 		       nullptr, true, &code);
 	if (code != U_ZERO_ERROR)
@@ -144,7 +144,7 @@ IcuConverter::FromUTF8(const char *s) const
 	const UChar *source = u.begin();
 	UErrorCode code = U_ZERO_ERROR;
 
-	ucnv_fromUnicode(converter, &target, buffer + ARRAY_SIZE(buffer),
+	ucnv_fromUnicode(converter, &target, buffer + std::size(buffer),
 			 &source, u.end(),
 			 nullptr, true, &code);
 
diff --git a/src/net/SocketError.cxx b/src/net/SocketError.cxx
index 4c887bcba..3a0e77980 100644
--- a/src/net/SocketError.cxx
+++ b/src/net/SocketError.cxx
@@ -18,7 +18,8 @@
  */
 
 #include "SocketError.hxx"
-#include "util/Macros.hxx"
+
+#include <iterator>
 
 #include <string.h>
 
@@ -27,7 +28,7 @@
 SocketErrorMessage::SocketErrorMessage(socket_error_t code) noexcept
 {
 #ifdef _UNICODE
-	wchar_t buffer[ARRAY_SIZE(msg)];
+	wchar_t buffer[std::size(msg)];
 #else
 	auto *buffer = msg;
 #endif
@@ -36,7 +37,7 @@ SocketErrorMessage::SocketErrorMessage(socket_error_t code) noexcept
 				     FORMAT_MESSAGE_IGNORE_INSERTS |
 				     FORMAT_MESSAGE_MAX_WIDTH_MASK,
 				     nullptr, code, 0,
-				     buffer, ARRAY_SIZE(msg), nullptr);
+				     buffer, std::size(msg), nullptr);
 	if (nbytes == 0) {
 		strcpy(msg, "Unknown error");
 		return;
@@ -44,7 +45,7 @@ SocketErrorMessage::SocketErrorMessage(socket_error_t code) noexcept
 
 #ifdef _UNICODE
 	auto length = WideCharToMultiByte(CP_UTF8, 0, buffer, -1,
-					  msg, ARRAY_SIZE(msg),
+					  msg, std::size(msg),
 					  nullptr, nullptr);
 	if (length <= 0) {
 		strcpy(msg, "WideCharToMultiByte() error");
diff --git a/src/output/plugins/OssOutputPlugin.cxx b/src/output/plugins/OssOutputPlugin.cxx
index 5bbb3983d..6449ceaf1 100644
--- a/src/output/plugins/OssOutputPlugin.cxx
+++ b/src/output/plugins/OssOutputPlugin.cxx
@@ -24,10 +24,10 @@
 #include "system/Error.hxx"
 #include "util/ConstBuffer.hxx"
 #include "util/Domain.hxx"
-#include "util/Macros.hxx"
 #include "util/ByteOrder.hxx"
 #include "Log.hxx"
 
+#include <iterator>
 #include <stdexcept>
 
 #include <sys/stat.h>
@@ -168,7 +168,7 @@ static const char *const default_devices[] = { "/dev/sound/dsp", "/dev/dsp" };
 static bool
 oss_output_test_default_device() noexcept
 {
-	for (int i = ARRAY_SIZE(default_devices); --i >= 0; ) {
+	for (int i = std::size(default_devices); --i >= 0; ) {
 		UniqueFileDescriptor fd;
 		if (fd.Open(default_devices[i], O_WRONLY, 0))
 			return true;
@@ -184,16 +184,16 @@ oss_output_test_default_device() noexcept
 static OssOutput *
 oss_open_default()
 {
-	int err[ARRAY_SIZE(default_devices)];
-	enum oss_stat ret[ARRAY_SIZE(default_devices)];
+	int err[std::size(default_devices)];
+	enum oss_stat ret[std::size(default_devices)];
 
-	for (int i = ARRAY_SIZE(default_devices); --i >= 0; ) {
+	for (int i = std::size(default_devices); --i >= 0; ) {
 		ret[i] = oss_stat_device(default_devices[i], &err[i]);
 		if (ret[i] == OSS_STAT_NO_ERROR)
 			return new OssOutput(default_devices[i]);
 	}
 
-	for (int i = ARRAY_SIZE(default_devices); --i >= 0; ) {
+	for (int i = std::size(default_devices); --i >= 0; ) {
 		const char *dev = default_devices[i];
 		switch(ret[i]) {
 		case OSS_STAT_NO_ERROR:
diff --git a/src/output/plugins/WinmmOutputPlugin.cxx b/src/output/plugins/WinmmOutputPlugin.cxx
index 71ce551b4..b38754a09 100644
--- a/src/output/plugins/WinmmOutputPlugin.cxx
+++ b/src/output/plugins/WinmmOutputPlugin.cxx
@@ -23,10 +23,10 @@
 #include "mixer/MixerList.hxx"
 #include "fs/AllocatedPath.hxx"
 #include "util/RuntimeError.hxx"
-#include "util/Macros.hxx"
 #include "util/StringCompare.hxx"
 
 #include <array>
+#include <iterator>
 
 #include <stdlib.h>
 #include <string.h>
@@ -86,7 +86,7 @@ MakeWaveOutError(MMRESULT result, const char *prefix)
 {
 	char buffer[256];
 	if (waveOutGetErrorTextA(result, buffer,
-				 ARRAY_SIZE(buffer)) == MMSYSERR_NOERROR)
+				 std::size(buffer)) == MMSYSERR_NOERROR)
 		return FormatRuntimeError("%s: %s", prefix, buffer);
 	else
 		return std::runtime_error(prefix);
diff --git a/src/output/plugins/httpd/IcyMetaDataServer.cxx b/src/output/plugins/httpd/IcyMetaDataServer.cxx
index c75b6fbc9..c2661b98e 100644
--- a/src/output/plugins/httpd/IcyMetaDataServer.cxx
+++ b/src/output/plugins/httpd/IcyMetaDataServer.cxx
@@ -22,7 +22,8 @@
 #include "util/FormatString.hxx"
 #include "util/AllocatedString.hxx"
 #include "util/TruncateString.hxx"
-#include "util/Macros.hxx"
+
+#include <iterator>
 
 #include <string.h>
 
@@ -97,7 +98,7 @@ icy_server_metadata_page(const Tag &tag, const TagType *types) noexcept
 
 	// Length + Metadata - "StreamTitle='';StreamUrl='';" = 4081 - 28
 	char stream_title[(1 + 255 - 28) * 16];
-	char *p = stream_title, *const end = stream_title + ARRAY_SIZE(stream_title);
+	char *p = stream_title, *const end = stream_title + std::size(stream_title);
 	stream_title[0] =  '\0';
 
 	while (p < end && item <= last_item) {
diff --git a/src/output/plugins/sles/SlesOutputPlugin.cxx b/src/output/plugins/sles/SlesOutputPlugin.cxx
index c10fe36f3..03b589e16 100644
--- a/src/output/plugins/sles/SlesOutputPlugin.cxx
+++ b/src/output/plugins/sles/SlesOutputPlugin.cxx
@@ -25,7 +25,6 @@
 #include "../../OutputAPI.hxx"
 #include "thread/Mutex.hxx"
 #include "thread/Cond.hxx"
-#include "util/Macros.hxx"
 #include "util/Domain.hxx"
 #include "util/ByteOrder.hxx"
 #include "Log.hxx"
@@ -33,6 +32,7 @@
 #include <SLES/OpenSLES.h>
 #include <SLES/OpenSLES_Android.h>
 
+#include <iterator>
 #include <stdexcept>
 
 class SlesOutput final : AudioOutput  {
@@ -212,7 +212,7 @@ SlesOutput::Open(AudioFormat &audio_format)
 	};
 
 	result = engine.CreateAudioPlayer(&_object, &audioSrc, &audioSnk,
-					  ARRAY_SIZE(ids2), ids2, req2);
+					  std::size(ids2), ids2, req2);
 	if (result != SL_RESULT_SUCCESS) {
 		mix_object.Destroy();
 		engine_object.Destroy();
diff --git a/src/playlist/PlaylistRegistry.cxx b/src/playlist/PlaylistRegistry.cxx
index 1866229bc..f24b28201 100644
--- a/src/playlist/PlaylistRegistry.cxx
+++ b/src/playlist/PlaylistRegistry.cxx
@@ -36,10 +36,11 @@
 #include "util/UriUtil.hxx"
 #include "util/StringUtil.hxx"
 #include "util/StringView.hxx"
-#include "util/Macros.hxx"
 #include "config/Data.hxx"
 #include "config/Block.hxx"
 
+#include <iterator>
+
 #include <assert.h>
 
 const struct playlist_plugin *const playlist_plugins[] = {
@@ -65,7 +66,7 @@ const struct playlist_plugin *const playlist_plugins[] = {
 };
 
 static constexpr unsigned n_playlist_plugins =
-	ARRAY_SIZE(playlist_plugins) - 1;
+	std::size(playlist_plugins) - 1;
 
 /** which plugins have been initialized successfully? */
 static bool playlist_plugins_enabled[n_playlist_plugins];
diff --git a/src/sticker/Database.cxx b/src/sticker/Database.cxx
index c90cbdcf7..29df8ca8e 100644
--- a/src/sticker/Database.cxx
+++ b/src/sticker/Database.cxx
@@ -22,10 +22,11 @@
 #include "lib/sqlite/Util.hxx"
 #include "fs/Path.hxx"
 #include "Idle.hxx"
-#include "util/Macros.hxx"
 #include "util/StringCompare.hxx"
 #include "util/ScopeExit.hxx"
 
+#include <iterator>
+
 #include <assert.h>
 
 using namespace Sqlite;
@@ -98,7 +99,7 @@ StickerDatabase::StickerDatabase(Path path)
 
 	/* prepare the statements we're going to use */
 
-	for (unsigned i = 0; i < ARRAY_SIZE(sticker_sql); ++i) {
+	for (unsigned i = 0; i < std::size(sticker_sql); ++i) {
 		assert(sticker_sql[i] != nullptr);
 
 		stmt[i] = Prepare(db, sticker_sql[i]);
@@ -109,7 +110,7 @@ StickerDatabase::~StickerDatabase() noexcept
 {
 	assert(db != nullptr);
 
-	for (unsigned i = 0; i < ARRAY_SIZE(stmt); ++i) {
+	for (unsigned i = 0; i < std::size(stmt); ++i) {
 		assert(stmt[i] != nullptr);
 
 		sqlite3_finalize(stmt[i]);
diff --git a/src/util/Macros.hxx b/src/util/Macros.hxx
deleted file mode 100644
index 31be1cf76..000000000
--- a/src/util/Macros.hxx
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2010-2013 Max Kellermann <max.kellermann@gmail.com>
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * - Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the
- * distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
- * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
- * OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef MACROS_HPP
-#define MACROS_HPP
-
-#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
-
-#endif
diff --git a/src/util/NumberParser.cxx b/src/util/NumberParser.cxx
index 4bb47b310..995e9f9ca 100644
--- a/src/util/NumberParser.cxx
+++ b/src/util/NumberParser.cxx
@@ -30,15 +30,15 @@
 
 #include "NumberParser.hxx"
 #include "StringView.hxx"
-#include "Macros.hxx"
 
 #include <algorithm>
+#include <iterator>
 
 int64_t
 ParseInt64(StringView s, const char **endptr_r, int base) noexcept
 {
 	char buffer[32];
-	*std::copy_n(s.data, std::min(s.size, ARRAY_SIZE(buffer) - 1),
+	*std::copy_n(s.data, std::min(s.size, std::size(buffer) - 1),
 		     buffer) = 0;
 
 	char *endptr;
diff --git a/test/TestSplitString.cxx b/test/TestSplitString.cxx
index 6ba87ee1b..868aea573 100644
--- a/test/TestSplitString.cxx
+++ b/test/TestSplitString.cxx
@@ -3,10 +3,10 @@
  */
 
 #include "util/SplitString.hxx"
-#include "util/Macros.hxx"
 
 #include <gtest/gtest.h>
 
+#include <iterator>
 
 TEST(SplitString, Basic)
 {
@@ -14,12 +14,12 @@ TEST(SplitString, Basic)
 	const char *const output[] = { "foo", "bar" };
 	size_t i = 0;
 	for (auto p : SplitString(input, '.')) {
-		EXPECT_LT(i, ARRAY_SIZE(output));
+		EXPECT_LT(i, std::size(output));
 		EXPECT_EQ(p, output[i]);
 		++i;
 	}
 
-	EXPECT_EQ(ARRAY_SIZE(output), i);
+	EXPECT_EQ(std::size(output), i);
 }
 
 TEST(SplitString, Strip)
@@ -28,12 +28,12 @@ TEST(SplitString, Strip)
 	const char *const output[] = { "foo", "bar\r\n2" };
 	size_t i = 0;
 	for (auto p : SplitString(input, '.')) {
-		EXPECT_LT(i, ARRAY_SIZE(output));
+		EXPECT_LT(i, std::size(output));
 		EXPECT_EQ(p, output[i]);
 		++i;
 	}
 
-	EXPECT_EQ(ARRAY_SIZE(output), i);
+	EXPECT_EQ(std::size(output), i);
 }
 
 TEST(SplitString, NoStrip)
@@ -42,12 +42,12 @@ TEST(SplitString, NoStrip)
 	const char *const output[] = { " foo\t", "\r\nbar\r\n2" };
 	size_t i = 0;
 	for (auto p : SplitString(input, '.', false)) {
-		EXPECT_LT(i, ARRAY_SIZE(output));
+		EXPECT_LT(i, std::size(output));
 		EXPECT_EQ(p, output[i]);
 		++i;
 	}
 
-	EXPECT_EQ(ARRAY_SIZE(output), i);
+	EXPECT_EQ(std::size(output), i);
 }
 
 TEST(SplitString, Empty)
diff --git a/test/test_byte_reverse.cxx b/test/test_byte_reverse.cxx
index 144ebde6c..8535a89df 100644
--- a/test/test_byte_reverse.cxx
+++ b/test/test_byte_reverse.cxx
@@ -18,7 +18,6 @@
  */
 
 #include "util/ByteReverse.hxx"
-#include "util/Macros.hxx"
 #include "util/Compiler.h"
 
 #include <gtest/gtest.h>
@@ -30,10 +29,10 @@ TEST(ByteReverse, A)
 {
 	alignas(uint16_t) static const char src[] = "123456";
 	static const char result[] = "214365";
-	alignas(uint16_t)static uint8_t dest[ARRAY_SIZE(src)];
+	alignas(uint16_t)static uint8_t dest[std::size(src)];
 
 	reverse_bytes(dest, (const uint8_t *)src,
-		      (const uint8_t *)(src + ARRAY_SIZE(src) - 1), 2);
+		      (const uint8_t *)(src + std::size(src) - 1), 2);
 	EXPECT_STREQ(result, (const char *)dest);
 }
 
@@ -41,10 +40,10 @@ TEST(ByteReverse, B)
 {
 	static const char src[] = "123456";
 	static const char result[] = "321654";
-	static uint8_t dest[ARRAY_SIZE(src)];
+	static uint8_t dest[std::size(src)];
 
 	reverse_bytes(dest, (const uint8_t *)src,
-		      (const uint8_t *)(src + ARRAY_SIZE(src) - 1), 3);
+		      (const uint8_t *)(src + std::size(src) - 1), 3);
 	EXPECT_STREQ(result, (const char *)dest);
 }
 
@@ -52,10 +51,10 @@ TEST(ByteReverse, C)
 {
 	alignas(uint32_t) static const char src[] = "12345678";
 	static const char result[] = "43218765";
-	alignas(uint32_t) static uint8_t dest[ARRAY_SIZE(src)];
+	alignas(uint32_t) static uint8_t dest[std::size(src)];
 
 	reverse_bytes(dest, (const uint8_t *)src,
-		      (const uint8_t *)(src + ARRAY_SIZE(src) - 1), 4);
+		      (const uint8_t *)(src + std::size(src) - 1), 4);
 	EXPECT_STREQ(result, (const char *)dest);
 }
 
@@ -63,9 +62,9 @@ TEST(ByteReverse, D)
 {
 	static const char src[] = "1234567890";
 	static const char result[] = "5432109876";
-	static uint8_t dest[ARRAY_SIZE(src)];
+	static uint8_t dest[std::size(src)];
 
 	reverse_bytes(dest, (const uint8_t *)src,
-		      (const uint8_t *)(src + ARRAY_SIZE(src) - 1), 5);
+		      (const uint8_t *)(src + std::size(src) - 1), 5);
 	EXPECT_STREQ(result, (const char *)dest);
 }
diff --git a/test/test_pcm_interleave.cxx b/test/test_pcm_interleave.cxx
index 8d52b884b..12f0ac4b2 100644
--- a/test/test_pcm_interleave.cxx
+++ b/test/test_pcm_interleave.cxx
@@ -18,7 +18,6 @@
  */
 
 #include "pcm/Interleave.hxx"
-#include "util/Macros.hxx"
 
 #include <gtest/gtest.h>
 
@@ -33,15 +32,15 @@ TestInterleaveN()
 	static constexpr T src3[] = { 3, 6, 9 };
 	static constexpr const T *src_all[] = { src1, src2, src3 };
 
-	static constexpr size_t n_frames = ARRAY_SIZE(src1);
-	static constexpr unsigned channels = ARRAY_SIZE(src_all);
+	static constexpr size_t n_frames = std::size(src1);
+	static constexpr unsigned channels = std::size(src_all);
 
 	static const ConstBuffer<const void *> src((const void *const*)src_all,
 						   channels);
 
 	static constexpr T poison = T(0xdeadbeef);
 	T dest[n_frames * channels + 1];
-	std::fill_n(dest, ARRAY_SIZE(dest), poison);
+	std::fill_n(dest, std::size(dest), poison);
 
 	PcmInterleave(dest, src, n_frames, sizeof(T));
 
@@ -75,15 +74,15 @@ TEST(PcmTest, Interleave24)
 	static constexpr T src3[] = { 13, 14, 15, 16, 17, 18 };
 	static constexpr const T *src_all[] = { src1, src2, src3 };
 
-	static constexpr size_t n_frames = ARRAY_SIZE(src1) / 3;
-	static constexpr unsigned channels = ARRAY_SIZE(src_all);
+	static constexpr size_t n_frames = std::size(src1) / 3;
+	static constexpr unsigned channels = std::size(src_all);
 
 	static const ConstBuffer<const void *> src((const void *const*)src_all,
 						   channels);
 
 	static constexpr T poison = 0xff;
 	T dest[n_frames * channels * 3 + 1];
-	std::fill_n(dest, ARRAY_SIZE(dest), poison);
+	std::fill_n(dest, std::size(dest), poison);
 
 	PcmInterleave(dest, src, n_frames, 3);
 
diff --git a/test/test_queue_priority.cxx b/test/test_queue_priority.cxx
index 1d65193b3..b6eaf800a 100644
--- a/test/test_queue_priority.cxx
+++ b/test/test_queue_priority.cxx
@@ -1,9 +1,10 @@
 #include "queue/Queue.hxx"
 #include "song/DetachedSong.hxx"
-#include "util/Macros.hxx"
 
 #include <gtest/gtest.h>
 
+#include <iterator>
+
 Tag::Tag(const Tag &) noexcept {}
 void Tag::Clear() noexcept {}
 
@@ -46,10 +47,10 @@ TEST(QueuePriority, Priority)
 
 	Queue queue(32);
 
-	for (unsigned i = 0; i < ARRAY_SIZE(songs); ++i)
+	for (unsigned i = 0; i < std::size(songs); ++i)
 		queue.Append(DetachedSong(songs[i]), 0);
 
-	EXPECT_EQ(unsigned(ARRAY_SIZE(songs)), queue.GetLength());
+	EXPECT_EQ(unsigned(std::size(songs)), queue.GetLength());
 
 	/* priority=10 for 4 items */
 
@@ -67,7 +68,7 @@ TEST(QueuePriority, Priority)
 		assert(queue.PositionToOrder(i) < 4);
 	}
 
-	for (unsigned i = 8; i < ARRAY_SIZE(songs); ++i) {
+	for (unsigned i = 8; i < std::size(songs); ++i) {
 		assert(queue.PositionToOrder(i) >= 4);
 	}