[gtkmm] Add Gtk::BitsetConstIter and tests/bitset_iterator
- From: Kjell Ahlstedt <kjellahl src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtkmm] Add Gtk::BitsetConstIter and tests/bitset_iterator
- Date: Sun, 5 Jul 2020 12:52:03 +0000 (UTC)
commit 2edb9e9b95a47180ea69fdf4fcfc756187ed6d86
Author: Kjell Ahlstedt <kjellahlstedt gmail com>
Date: Sun Jul 5 14:48:46 2020 +0200
Add Gtk::BitsetConstIter and tests/bitset_iterator
gtk/gtkmm/bitsetconstiter.cc | 201 ++++++++++++++++++++++++++++++++++++++++++
gtk/gtkmm/bitsetconstiter.h | 92 +++++++++++++++++++
gtk/gtkmm/filelist.am | 2 +
gtk/gtkmm/meson.build | 1 +
gtk/src/bitset.ccg | 15 ++++
gtk/src/bitset.hg | 13 ++-
tests/Makefile.am | 5 +-
tests/bitset_iterator/main.cc | 105 ++++++++++++++++++++++
tests/meson.build | 1 +
9 files changed, 433 insertions(+), 2 deletions(-)
---
diff --git a/gtk/gtkmm/bitsetconstiter.cc b/gtk/gtkmm/bitsetconstiter.cc
new file mode 100644
index 00000000..50a997e3
--- /dev/null
+++ b/gtk/gtkmm/bitsetconstiter.cc
@@ -0,0 +1,201 @@
+/* Copyright (C) 2020 The gtkmm Development Team
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <gtkmm/bitsetconstiter.h>
+
+namespace Gtk
+{
+
+BitsetConstIter::BitsetConstIter(const GtkBitset* bitset, bool is_end)
+: gobject_(std::make_unique<GtkBitsetIter>()),
+ bitset_(bitset),
+ is_end_(is_end)
+{
+ if (is_end_)
+ {
+ // Initialize gobject_.get() to an invalid iterator.
+ if (gtk_bitset_iter_init_last(gobject_.get(), bitset_, nullptr))
+ gtk_bitset_iter_next(gobject_.get(), nullptr);
+ }
+ else
+ // Point to the first element in the bitset.
+ if (!gtk_bitset_iter_init_first(gobject_.get(), bitset_, nullptr))
+ // The bitset is empty.
+ is_end_ = true;
+}
+
+BitsetConstIter::BitsetConstIter(const BitsetConstIter& other)
+: gobject_(other.gobject_ ? std::make_unique<GtkBitsetIter>() : nullptr),
+ bitset_(other.bitset_),
+ is_end_(other.is_end_)
+{
+ if (other.gobject_)
+ {
+ if (gtk_bitset_iter_is_valid(other.gobject_.get()))
+ gtk_bitset_iter_init_at(gobject_.get(), bitset_,
+ gtk_bitset_iter_get_value(other.gobject_.get()), nullptr);
+ else
+ // other.gobject_.get() exists, but is invalid.
+ // Initialize gobject_.get() to an invalid iterator.
+ if (gtk_bitset_iter_init_last(gobject_.get(), bitset_, nullptr))
+ gtk_bitset_iter_next(gobject_.get(), nullptr);
+ }
+}
+
+BitsetConstIter& BitsetConstIter::operator=(const BitsetConstIter& other)
+{
+ if (&other == this)
+ return *this;
+
+ gobject_ = other.gobject_ ? std::make_unique<GtkBitsetIter>() : nullptr;
+ bitset_ = other.bitset_;
+ is_end_ = other.is_end_;
+
+ if (other.gobject_)
+ {
+ if (gtk_bitset_iter_is_valid(other.gobject_.get()))
+ gtk_bitset_iter_init_at(gobject_.get(), bitset_,
+ gtk_bitset_iter_get_value(other.gobject_.get()), nullptr);
+ else
+ // other.gobject_.get() exists, but is invalid.
+ // Initialize gobject_.get() to an invalid iterator.
+ if (gtk_bitset_iter_init_last(gobject_.get(), bitset_, nullptr))
+ gtk_bitset_iter_next(gobject_.get(), nullptr);
+ }
+ return *this;
+}
+
+BitsetConstIter::BitsetConstIter(BitsetConstIter&& other) noexcept
+: gobject_(std::move(other.gobject_)),
+ bitset_(other.bitset_),
+ is_end_(other.is_end_)
+{
+ other.bitset_ = nullptr;
+ other.is_end_ = true;
+}
+
+BitsetConstIter& BitsetConstIter::operator=(BitsetConstIter&& other) noexcept
+{
+ gobject_ = std::move(other.gobject_);
+ bitset_ = other.bitset_;
+ is_end_ = other.is_end_;
+ other.bitset_ = nullptr;
+ other.is_end_ = true;
+ return *this;
+}
+
+BitsetConstIter& BitsetConstIter::operator++()
+{
+ if (!(gobject_ && bitset_ && !is_end_))
+ return *this;
+
+ if (!gtk_bitset_iter_next(gobject_.get(), nullptr))
+ is_end_ = true;
+
+ return *this;
+}
+
+BitsetConstIter BitsetConstIter::operator++(int)
+{
+ if (!(gobject_ && bitset_ && !is_end_))
+ return *this;
+
+ BitsetConstIter orig(*this);
+
+ if (!gtk_bitset_iter_next(gobject_.get(), nullptr))
+ is_end_ = true;
+
+ return orig;
+}
+
+BitsetConstIter& BitsetConstIter::operator--()
+{
+ if (!(gobject_ && bitset_))
+ return *this;
+
+ if (is_end_)
+ {
+ if (gtk_bitset_iter_init_last(gobject_.get(), bitset_, nullptr))
+ is_end_ = false;
+ }
+ else
+ gtk_bitset_iter_previous(gobject_.get(), nullptr);
+
+ return *this;
+}
+
+BitsetConstIter BitsetConstIter::operator--(int)
+{
+ if (!(gobject_ && bitset_))
+ return *this;
+
+ BitsetConstIter orig(*this);
+
+ if (is_end_)
+ {
+ if (gtk_bitset_iter_init_last(gobject_.get(), bitset_, nullptr))
+ is_end_ = false;
+ }
+ else
+ gtk_bitset_iter_previous(gobject_.get(), nullptr);
+
+ return orig;
+}
+
+BitsetConstIter::reference BitsetConstIter::operator*() const
+{
+ if (!(gobject_ && bitset_ && !is_end_))
+ return 0;
+
+ return gtk_bitset_iter_get_value(gobject_.get());
+}
+
+BitsetConstIter::operator bool() const noexcept
+{
+ if (!(gobject_ && bitset_ && !is_end_))
+ return false;
+
+ return gtk_bitset_iter_is_valid(gobject_.get());
+}
+
+bool BitsetConstIter::equal(const BitsetConstIter& other) const noexcept
+{
+ // By far the most common test for equality or unequality between
+ // iterators is a comparison where one iterator is an end iterator.
+ // First test if the iterators are unequal because one is an end iterator
+ // and the other is not.
+ if (is_end_ != other.is_end_ || bitset_ != other.bitset_)
+ return false;
+
+ if (!gobject_ && !other.gobject_)
+ return true;
+
+ if (!gobject_ || !other.gobject_)
+ return false;
+
+ if (gtk_bitset_iter_is_valid(gobject_.get()) !=
+ gtk_bitset_iter_is_valid(other.gobject_.get()))
+ return false;
+
+ // There is no function that checks the equality of two GtkBitsetIters.
+ // The iterators iterate over a set where no two elements have the
+ // same value. If two iterators point to elements with the same value,
+ // the iterators are equal.
+ return gtk_bitset_iter_get_value(gobject_.get()) ==
+ gtk_bitset_iter_get_value(other.gobject_.get());
+}
+
+} // namespace Gtk
diff --git a/gtk/gtkmm/bitsetconstiter.h b/gtk/gtkmm/bitsetconstiter.h
new file mode 100644
index 00000000..e15e49b0
--- /dev/null
+++ b/gtk/gtkmm/bitsetconstiter.h
@@ -0,0 +1,92 @@
+#ifndef _GTKMM_BITSETCONSTITER_H
+#define _GTKMM_BITSETCONSTITER_H
+
+/* Copyright (C) 2020 The gtkmm Development Team
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <gtkmmconfig.h>
+#include <iterator>
+#include <memory>
+#include <gtk/gtk.h>
+
+namespace Gtk
+{
+
+/** Alias: Gtk::Bitset::const_iterator.
+ *
+ * A %BitsetConstIter is a reference to a specific element in a specific bitset.
+ *
+ * This is a const_iterator. There is no way to modify the bitset via an iterator.
+ * For modifying the bitset, use methods in Gtk::Bitset.
+ *
+ * @see Bitset
+ *
+ * @newin{3,98}
+ */
+class GTKMM_API BitsetConstIter
+{
+public:
+ using iterator_category = std::bidirectional_iterator_tag;
+ using value_type = guint;
+ using difference_type = int;
+ using reference = value_type;
+ using pointer = void;
+
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+ BitsetConstIter() {}
+ BitsetConstIter(const GtkBitset* bitset, bool is_end);
+#endif // DOXYGEN_SHOULD_SKIP_THIS
+
+ BitsetConstIter(const BitsetConstIter& other);
+ BitsetConstIter& operator=(const BitsetConstIter& other);
+ BitsetConstIter(BitsetConstIter&& other) noexcept;
+ BitsetConstIter& operator=(BitsetConstIter&& other) noexcept;
+
+ BitsetConstIter& operator++();
+ BitsetConstIter operator++(int);
+ BitsetConstIter& operator--();
+ BitsetConstIter operator--(int);
+ reference operator*() const;
+
+ /** Discovers whether the iterator is valid, and not equal to end().
+ * For instance,
+ * @code
+ * if (bitset_iter)
+ * do_something();
+ * @endcode
+ */
+ explicit operator bool() const noexcept;
+
+ bool equal(const BitsetConstIter& other) const noexcept;
+
+private:
+ std::unique_ptr<GtkBitsetIter> gobject_;
+ // BitsetConstIter does not own a reference to the GtkBitset.
+ const GtkBitset* bitset_ = nullptr;
+ bool is_end_ = true;
+};
+
+/** @relates Gtk::BitsetConstIter */
+inline bool operator==(const BitsetConstIter& lhs, const BitsetConstIter& rhs)
+{ return lhs.equal(rhs); }
+
+/** @relates Gtk::BitsetConstIter */
+inline bool operator!=(const BitsetConstIter& lhs, const BitsetConstIter& rhs)
+{ return !lhs.equal(rhs); }
+
+} // namespace Gtk
+
+#endif /* _GTKMM_BITSETCONSTITER_H */
diff --git a/gtk/gtkmm/filelist.am b/gtk/gtkmm/filelist.am
index bf171ba8..45f27b59 100644
--- a/gtk/gtkmm/filelist.am
+++ b/gtk/gtkmm/filelist.am
@@ -6,6 +6,7 @@ gtkmm_files_built_h = $(gtkmm_files_used_hg:.hg=.h)
gtkmm_files_extra_any_cc = \
accelerator.cc \
accelkey.cc \
+ bitsetconstiter.cc \
cellrenderer_generation.cc \
listviewtext.cc \
object.cc \
@@ -18,6 +19,7 @@ gtkmm_files_extra_deprecated_cc =
gtkmm_files_extra_any_h = \
accelerator.h \
accelkey.h \
+ bitsetconstiter.h \
cellrenderer_generation.h \
listviewtext.h \
object.h \
diff --git a/gtk/gtkmm/meson.build b/gtk/gtkmm/meson.build
index f5291dbb..887ccb3f 100644
--- a/gtk/gtkmm/meson.build
+++ b/gtk/gtkmm/meson.build
@@ -255,6 +255,7 @@ gtkmm_deprecated_hg_ccg_basenames = []
gtkmm_extra_any_h_cc_basenames = [
'accelerator',
'accelkey',
+ 'bitsetconstiter',
'cellrenderer_generation',
'listviewtext',
'object',
diff --git a/gtk/src/bitset.ccg b/gtk/src/bitset.ccg
index 57b87fdc..729a8e71 100644
--- a/gtk/src/bitset.ccg
+++ b/gtk/src/bitset.ccg
@@ -15,3 +15,18 @@
*/
#include <gtk/gtk.h>
+
+namespace Gtk
+{
+
+Bitset::const_iterator Bitset::begin() const
+{
+ return BitsetConstIter(gobj(), false);
+}
+
+Bitset::const_iterator Bitset::end() const
+{
+ return BitsetConstIter(gobj(), true);
+}
+
+} // namespace Gtk
diff --git a/gtk/src/bitset.hg b/gtk/src/bitset.hg
index 8c6e7d44..cdabc887 100644
--- a/gtk/src/bitset.hg
+++ b/gtk/src/bitset.hg
@@ -19,6 +19,7 @@ _CONFIGINCLUDE(gtkmmconfig.h)
_DEFS(gtkmm,gtk)
#include <glibmm/refptr.h>
+#include <gtkmm/bitsetconstiter.h>
extern "C" typedef struct _GtkBitset GtkBitset;
@@ -38,10 +39,13 @@ namespace Gtk
* in the set. %Gtk::Bitset also contains various functions to query metadata about
* the bitset, such as the minimum or maximum values or its size.
*
+ * The fastest way to iterate values in a bitset is Gtk::BitsetConstIter which
+ * allows quick iteration of all the values in a bitset.
+ *
* The main use case for %Gtk::Bitset is implementing complex selections for
* Gtk::SelectionModel.
*
- * @see SelectionModel
+ * @see SelectionModel, BitsetConstIter
*
* @newin{3,98}
*/
@@ -53,6 +57,13 @@ class GTKMM_API Bitset final
_IGNORE(gtk_bitset_ref, gtk_bitset_unref)
public:
+ using const_iterator = BitsetConstIter;
+
+ const_iterator begin() const;
+ const_iterator end() const;
+ const_iterator cbegin() const { return begin(); }
+ const_iterator cend() const { return end(); }
+
_WRAP_METHOD(static Glib::RefPtr<Bitset> create(), gtk_bitset_new_empty)
_WRAP_METHOD(bool contains(guint value) const, gtk_bitset_contains)
diff --git a/tests/Makefile.am b/tests/Makefile.am
index e15d3111..effb39ef 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -18,6 +18,7 @@
AUTOMAKE_OPTIONS = subdir-objects
check_PROGRAMS = \
+ bitset_iterator/test \
builder/test \
child_widget/test \
child_widget2/test \
@@ -33,10 +34,12 @@ check_PROGRAMS = \
tree_model_iterator/test \
wrap_existing/test
-TESTS = object_move/test \
+TESTS = bitset_iterator/test \
+ object_move/test \
gdk_rgba/test \
test_validate_docs_xhtml.sh
+bitset_iterator_test_SOURCES = bitset_iterator/main.cc
builder_test_SOURCES = builder/main.cc
child_widget_test_SOURCES = child_widget/main.cc \
child_widget/testwindow.cc \
diff --git a/tests/bitset_iterator/main.cc b/tests/bitset_iterator/main.cc
new file mode 100644
index 00000000..12149980
--- /dev/null
+++ b/tests/bitset_iterator/main.cc
@@ -0,0 +1,105 @@
+#include <gtkmm.h>
+#include <iostream>
+#include <set>
+
+namespace
+{
+
+bool success = true;
+
+void test_traversal1()
+{
+ auto bitset = Gtk::Bitset::create();
+ bitset->add_range(200, 5); // Add 200, 201, 202, 203, 204
+ bitset->add(42);
+
+ // A std::set with the same elements as bitset.
+ std::set<guint> stdset{ 42, 200, 201, 202, 203, 204 };
+
+ for (auto i : *bitset)
+ {
+ if (!stdset.erase(i))
+ {
+ success = false;
+ std::cerr << "test_traversal1(): Element " << i << " not in stdset\n";
+ }
+ }
+ if (!stdset.empty())
+ {
+ success = false;
+ std::cerr << "test_traversal1(): stdset.size() = " << stdset.size() << " (not empty)\n";
+ }
+}
+
+void test_traversal2()
+{
+ auto bitset = Gtk::Bitset::create();
+ bitset->add_range(200, 5); // Add 200, 201, 202, 203, 204
+ bitset->add(42);
+
+ // A std::set with the same elements as bitset.
+ std::set<guint> stdset{ 42, 200, 201, 202, 203, 204 };
+
+ const auto end = bitset->end();
+ for (auto it = bitset->begin(); it != end; ++it)
+ {
+ if (!stdset.erase(*it))
+ {
+ success = false;
+ std::cerr << "test_traversal2(): Element " << *it << " not in stdset\n";
+ }
+ }
+ if (!stdset.empty())
+ {
+ success = false;
+ std::cerr << "test_traversal2(): stdset.size() = " << stdset.size() << " (not empty)\n";
+ }
+}
+
+void test_copy()
+{
+ auto bitset = Gtk::Bitset::create();
+ bitset->add_range(200, 5); // Add 200, 201, 202, 203, 204
+ bitset->add(42);
+
+ auto iter1 = bitset->end();
+ if (iter1)
+ {
+ success = false;
+ std::cerr << "test_copy(): iter1 is valid\n";
+ }
+ --iter1; // Shall point to the last element.
+ if (!iter1)
+ {
+ success = false;
+ std::cerr << "test_copy(): iter1 is not valid\n";
+ }
+ auto iter2 = iter1;
+ if (!iter1 || !iter2)
+ {
+ success = false;
+ std::cerr << "test_copy(): (bool)iter1 = " << static_cast<bool>(iter1)
+ << " (bool)iter2 = " << static_cast<bool>(iter2) << "\n";
+ }
+ auto iter3 = std::move(iter1);
+ if (iter1 || !iter3)
+ {
+ success = false;
+ std::cerr << "test_copy(): (bool)iter1 = " << static_cast<bool>(iter1)
+ << " (bool)iter3 = " << static_cast<bool>(iter3) << "\n";
+ }
+}
+
+} // anonymous namespace
+
+int main(int /* argc */, char** /* argv */)
+{
+ gtk_init();
+ Gtk::Main::init_gtkmm_internals();
+
+ test_traversal1();
+ test_traversal2();
+ test_copy();
+
+ return success ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/tests/meson.build b/tests/meson.build
index 23d44cf5..5bb08722 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -5,6 +5,7 @@
test_programs = [
# [[dir-name], exe-name, [sources], GUI, execute]
+ [['bitset_iterator'], 'test', ['main.cc'], false, true],
[['builder'], 'test', ['main.cc'], true, false],
[['child_widget'], 'test', ['main.cc', 'testwindow.cc'], true, false],
[['child_widget2'], 'test', ['main.cc'], true, false],
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]