From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from simark.ca by simark.ca with LMTP id Gov5CbVxsGi7Ww4AWB0awg (envelope-from ) for ; Thu, 28 Aug 2025 11:11:49 -0400 Received: by simark.ca (Postfix, from userid 112) id 0F2171E043; Thu, 28 Aug 2025 11:11:49 -0400 (EDT) X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-25) on simark.ca X-Spam-Level: X-Spam-Status: No, score=-0.7 required=5.0 tests=ARC_SIGNED,ARC_VALID,BAYES_00, MAILING_LIST_MULTI,RCVD_IN_DNSWL_LOW, RCVD_IN_VALIDITY_CERTIFIED_BLOCKED,RCVD_IN_VALIDITY_RPBL_BLOCKED, RCVD_IN_VALIDITY_SAFE_BLOCKED autolearn=no autolearn_force=no version=4.0.1 Received: from server2.sourceware.org (server2.sourceware.org [8.43.85.97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange x25519 server-signature ECDSA (prime256v1) server-digest SHA256) (No client certificate requested) by simark.ca (Postfix) with ESMTPS id E84131E043 for ; Thu, 28 Aug 2025 11:11:46 -0400 (EDT) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 2A4F43839E33 for ; Thu, 28 Aug 2025 15:11:46 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 2A4F43839E33 Received: from simark.ca (simark.ca [158.69.221.121]) by sourceware.org (Postfix) with ESMTPS id 7D893385840D for ; Thu, 28 Aug 2025 15:11:02 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 7D893385840D Authentication-Results: sourceware.org; dmarc=fail (p=none dis=none) header.from=efficios.com Authentication-Results: sourceware.org; spf=fail smtp.mailfrom=efficios.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 7D893385840D Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=158.69.221.121 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1756393862; cv=none; b=ImnBwLcjGiHspBKSDBuxhwqHVcX9zMZrieRhh/al3+aiSUZ6makLqZ3WH92FloXMm3v23p6K84tJ/5JjzMdWQq0ejyW+CyXk9DI9EY1TZ3N9XmaHivXv2Tdliukcu9xRRsrZ2X5ymGTjBpcLQ/qvJw0a1sfJudVHAmNGBow2RZM= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1756393862; c=relaxed/simple; bh=qpY6Hr739zcoNkRpoEEniOF0Dbfgh8rSZX2XJu8R5hY=; h=From:To:Subject:Date:Message-ID:MIME-Version; b=YyLGdUM0zJQaERo9K75wnuoZn4LwooQc5dzC0AmSmtVGXPnRh1kjW53pt8lZjawTN+sqL+6yPgUg/aOQcI/y6GCRBJirqNh1yElta/3m3OeqXuIIR/6y+cIQOdc1QU/++fpJc02z1ILQjzG+O41BG2ROmPwzepdsZpM+2HFiFAI= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 7D893385840D Received: by simark.ca (Postfix) id 2C1641E047; Thu, 28 Aug 2025 11:11:02 -0400 (EDT) From: Simon Marchi To: gdb-patches@sourceware.org Cc: Simon Marchi Subject: [PATCH 2/3] gdbsupport: make filtered_iterator work with pointers Date: Thu, 28 Aug 2025 11:10:50 -0400 Message-ID: <20250828151100.84594-2-simon.marchi@efficios.com> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20250828151100.84594-1-simon.marchi@efficios.com> References: <20250828151100.84594-1-simon.marchi@efficios.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gdb-patches-bounces~public-inbox=simark.ca@sourceware.org It's currently not possible to use filtered_iterator with a pointer as the base iterator type. This patch makes it possible. The indended usage is: Foo array[12]; Foo *begin = array; Foo *end = array + ARRAY_SIZE (array); filtered_iterator (begin, end); Here are the things that needed changing: - Give filtered_iterator a constructor where the caller provides already constructed begin and end iterators. filtered_iterator currently assumes that default-constructing a BaseIterator will produce a valid "end" iterator. This is not the case if BaseIterator is a pointer. The caller needs to pass in the end of the array / region to iterate on as the end. - Typedefs of member types like wouldn't work: typedef typename BaseIterator::value_type value_type; The compiler would complain that it's not possible to apply `::` to type `BaseIterator` (aka `Foo *`). Use std::iterator_traits to fix it [1]. - Similarly, the compiler would complain about the use of `BaseIterator::operator*` in the return type of `filtered_iterator::operator*`. Fix this by using `decltype(auto)` as the return type. This lets the compiler deduce the return type from the return statement. Unlike `auto`, `decltype(auto)` perfectly preserves the "cvref-ness" of the deduced return type. If the return expression yields a `Foo &`, then the function will return a `Foo &` (which is what we want), whereas it would return a `Foo` if we used just `auto`. Improve the filtered_iterator unit tests to run the same tests but with pointers as iterators. Because the filtered_iterator objects are initialized differently in the two scenarios, I chose to copy the existing code and adapt it. It would probably be possible to add a layer of abstraction to avoid code duplication, but it would end up more complicated and messy. If we ever add a third scenario, we can revisit that. [1] https://en.cppreference.com/w/cpp/iterator/iterator_traits.html Change-Id: Id962ffbcd960a705a82bc5eb4808b4fe118a2761 --- gdb/unittests/filtered_iterator-selftests.c | 52 +++++++++++++++++++++ gdbsupport/filtered-iterator.h | 26 ++++++----- 2 files changed, 67 insertions(+), 11 deletions(-) diff --git a/gdb/unittests/filtered_iterator-selftests.c b/gdb/unittests/filtered_iterator-selftests.c index 49c95cb2bf94..c04cae4963e0 100644 --- a/gdb/unittests/filtered_iterator-selftests.c +++ b/gdb/unittests/filtered_iterator-selftests.c @@ -125,6 +125,26 @@ test_filtered_iterator () SELF_CHECK (even_ints == expected_even_ints); } +/* Same as the above, but using pointers as the iterator base type. */ + +static void +test_filtered_iterator_ptr () +{ + int array[] = { 4, 4, 5, 6, 7, 8, 9 }; + std::vector even_ints; + const std::vector expected_even_ints { 4, 4, 6, 8 }; + + filtered_iterator iter + (array, array + ARRAY_SIZE (array)); + filtered_iterator end + (array + ARRAY_SIZE (array), array + ARRAY_SIZE (array)); + + for (; iter != end; ++iter) + even_ints.push_back (*iter); + + SELF_CHECK (even_ints == expected_even_ints); +} + /* Test operator== and operator!=. */ static void @@ -152,6 +172,34 @@ test_filtered_iterator_eq () SELF_CHECK (!(iter1 != iter2)); } + +/* Same as the above, but using pointers as the iterator base type. */ + +static void +test_filtered_iterator_eq_ptr () +{ + int array[] = { 4, 4, 5, 6, 7, 8, 9 }; + + filtered_iterator iter1 + (array, array + ARRAY_SIZE(array)); + filtered_iterator iter2 + (array, array + ARRAY_SIZE(array)); + + /* They start equal. */ + SELF_CHECK (iter1 == iter2); + SELF_CHECK (!(iter1 != iter2)); + + /* Advance 1, now they aren't equal (despite pointing to equal values). */ + ++iter1; + SELF_CHECK (!(iter1 == iter2)); + SELF_CHECK (iter1 != iter2); + + /* Advance 2, now they are equal again. */ + ++iter2; + SELF_CHECK (iter1 == iter2); + SELF_CHECK (!(iter1 != iter2)); +} + } /* namespace selftests */ INIT_GDB_FILE (filtered_iterator_selftests) @@ -160,4 +208,8 @@ INIT_GDB_FILE (filtered_iterator_selftests) selftests::test_filtered_iterator); selftests::register_test ("filtered_iterator_eq", selftests::test_filtered_iterator_eq); + selftests::register_test ("filtered_iterator_ptr", + selftests::test_filtered_iterator_ptr); + selftests::register_test ("filtered_iterator_eq_ptr", + selftests::test_filtered_iterator_eq_ptr); } diff --git a/gdbsupport/filtered-iterator.h b/gdbsupport/filtered-iterator.h index e824d6115a8a..38b4bb1236be 100644 --- a/gdbsupport/filtered-iterator.h +++ b/gdbsupport/filtered-iterator.h @@ -19,8 +19,6 @@ #ifndef GDBSUPPORT_FILTERED_ITERATOR_H #define GDBSUPPORT_FILTERED_ITERATOR_H -#include - /* A filtered iterator. This wraps BaseIterator and automatically skips elements that FilterFunc filters out. Requires that default-constructing a BaseIterator creates a valid one-past-end @@ -30,12 +28,16 @@ template class filtered_iterator { public: - typedef filtered_iterator self_type; - typedef typename BaseIterator::value_type value_type; - typedef typename BaseIterator::reference reference; - typedef typename BaseIterator::pointer pointer; - typedef typename BaseIterator::iterator_category iterator_category; - typedef typename BaseIterator::difference_type difference_type; + using self_type = filtered_iterator; + using value_type = typename std::iterator_traits::value_type; + using reference = typename std::iterator_traits::reference; + using pointer = typename std::iterator_traits::pointer; + using iterator_category = + typename std::iterator_traits::iterator_category; + ; + using difference_type = + typename std::iterator_traits::difference_type; + ; /* Construct by forwarding all arguments to the underlying iterator. */ @@ -44,6 +46,10 @@ class filtered_iterator : m_it (std::forward (args)...) { skip_filtered (); } + filtered_iterator (BaseIterator begin, BaseIterator end) + : m_it (begin), m_end (end) + { skip_filtered (); } + /* Create a one-past-end iterator. */ filtered_iterator () = default; @@ -56,9 +62,7 @@ class filtered_iterator : filtered_iterator (static_cast (other)) {} - typename std::invoke_result::type - operator* () const + decltype(auto) operator* () const { return *m_it; } self_type &operator++ () -- 2.51.0