[vlc-commits] [Git][videolan/vlc][master] 5 commits: include: vlc_list.hpp: add C++ wrapper for vlc_list

Steve Lhomme (@robUx4) gitlab at videolan.org
Sat Feb 10 08:51:31 UTC 2024



Steve Lhomme pushed to branch master at VideoLAN / VLC


Commits:
1398b183 by Alexandre Janniaux at 2024-02-10T08:25:07+00:00
include: vlc_list.hpp: add C++ wrapper for vlc_list

vlc_list.h provides struct vlc_list which is mostly designed to be used
in C code to provide doubly-linked chained list, which is already
exposed in C++ in an unintrusive way through std::list<> and work is
still done in the standard[^1] to try to submit intrusive list support.

However, some part of the public libvlccore API is exposing objects with
vlc_list members, which enforce their usage in the C++ code without more
standard alternative option.

This MR adds a C++ wrapper around the vlc_list structure to be able to
loop through vlc_list without the C macros. It has some limitations to
avoid exploding the implementation and type complexity, in particular
when it comes to the list<T>::reverse_list type which doesn't support
operations (push_front, push_back, erase). However, the base list<T>
type will support those using the same iterators.

[^1]: https://www.open-std.org/JTC1/SC22/WG21/docs/papers/2016/p0406r1.html

Refs #28507

Co-authored-by: Pierre Lamot <pierre at videolabs.io>

- - - - -
dd459dba by Pierre Lamot at 2024-02-10T08:25:07+00:00
include: vlc_list.hpp: add insertion/removal functions

Add some methods to replace their C counterpart when using a vlc_list,
so that elements can be added and removed. Those methods are only
supported on a vlc::list<T> wrapper, but they are not supported neither
on the vlc::const_list<T> wrapper (which is expected) nor on the
vlc::list::reverse_list wrapper from ::as_reverse() function, because it
would complexify the design.

The current workaround that was chosen is that vlc::list<T> is able to
use iterators from the reversed list, and can be swapped with the
reverse list object where needed.

The limitation will be lifted with C++20 and concepts.

Co-authored-by: Alexandre Janniaux <ajanni at videolabs.io>

- - - - -
833fcc11 by Alexandre Janniaux at 2024-02-10T08:25:07+00:00
src: test: add test for vlc::list<> wrappers

The tests will check the correct behaviour of the iterators as well as
the type validity for the templates once they are instantiated.

They also check whether removal of the current element during iteration
is working correctly both on the normal list wrapper and the reversed
version. This is important given how tricky std::reverse_iterator can
be.

Co-authored-by: Pierre Lamot <pierre at videolabs.io>

- - - - -
1697a21d by Alexandre Janniaux at 2024-02-10T08:25:07+00:00
doc: Doxyfile.in: inline inherited members

INLINE_INHERITED_MEMB = yes will make it so inherited members will be
visible in the documentation of derived classes.

- - - - -
ef35d253 by Alexandre Janniaux at 2024-02-10T08:25:07+00:00
qt: info_panels: use C++ variant for vlc_list

The C++ wrapper for vlc_list is type-safe and doesn't depend on GCC
compiler extension or vlc_fixups.

Refs #28507

- - - - -


5 changed files:

- doc/Doxyfile.in
- + include/vlc_list.hpp
- modules/gui/qt/dialogs/mediainfo/info_panels.cpp
- src/Makefile.am
- + src/test/list_cpp.cpp


Changes:

=====================================
doc/Doxyfile.in
=====================================
@@ -133,7 +133,7 @@ ALWAYS_DETAILED_SEC    = NO
 # operators of the base classes will not be shown.
 # The default value is: NO.
 
-INLINE_INHERITED_MEMB  = NO
+INLINE_INHERITED_MEMB  = YES
 
 # If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path
 # before files name in the file list and in the header files. If set to NO the


=====================================
include/vlc_list.hpp
=====================================
@@ -0,0 +1,461 @@
+/******************************************************************************
+ * vlc_list.hpp: C++ wrappers on top of vlc_list
+ ******************************************************************************
+ * Copyright © 2024 Videolabs
+ *
+ * Authors: Alexandre Janniaux <ajanni at videolabs.io>
+ *          Pierre Lamot <pierre at videolabs.io>
+ *
+ * This program 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 program 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 program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifndef VLC_LIST_HPP
+#define VLC_LIST_HPP 1
+
+#include <vlc_list.h>
+
+#include <iterator>
+#include <type_traits>
+
+namespace vlc
+{
+
+/**
+ * \defgroup cpp_list Linked lists (C++ wrappers)
+ * \ingroup cext
+ * @{
+ * \file
+ * This provides convenience helpers for using the C linked lists extension
+ * from C++ files.
+ *
+ * A list wrapper should be used on an existing list:
+ *
+ * \code
+ * struct item {
+ *     // ...
+ *     vlc_list node;
+ * }
+ *
+ * struct vlc_list c_list;
+ * vlc_list_init(&c_list);
+ * // ...
+ * auto list = vlc::from(&list, &item::node);
+ * \endcode
+ *
+ * Using `vlc::from` will automatically select the correct variant for
+ * the list depending on the constness of the `vlc_list` list, and it
+ * can allow only iteration if the list is const.
+ *
+ * Iteration includes standard iterators and for range-based loops, as well
+ * as reversed list.
+ *
+ * \code
+ * for (auto it = list.begin(); it != list.end(); ++it)
+ * {
+ *     // (*it) is of type `struct item`
+ * }
+ *
+ * for (auto &elem : vlc::from(&c_list, &item::node))
+ * {
+ *     // `elem` type is `struct item`
+ *     // ...
+ * }
+ *
+ * for (auto &elem : vlc::from(&c_list, &item::node).as_reverse())
+ * {
+ *     // `elem` type is `struct item`
+ *     // ...
+ * }
+ * \endcode
+ *
+ */
+
+/**
+ * Compare two vlc_list node and check whether they represent the same element.
+ * If the element is not in a list itself, or is not a list itself, then the
+ * result is undefined.
+ *
+ * \param a some node belonging to a vlc_list
+ * \param b some node belonging to a vlc_list
+ * \return true if they represent the same element or the same list,
+ *         false otherwise
+ **/
+bool operator==(const ::vlc_list& a, const ::vlc_list& b)
+{
+    return a.prev == b.prev && a.next == b.next;
+}
+
+/**
+ * Compare two vlc_list node and check whether they representthe same element.
+ * If the element is not in a list itself, or is not a list itself, then the
+ * result is undefined.
+ *
+ * \param a some node belonging to a vlc_list
+ * \param b some node belonging to a vlc_list
+ * \return false if they represent the same element or the same list,
+ *         true otherwise
+ **/
+bool operator!=(const ::vlc_list& a, const ::vlc_list& b)
+{
+    return !(a == b);
+}
+
+/**
+ * Base class for iterators on the vlc::list's vlc_list wrapper.
+ *
+ * The base class c
+ *
+ * \tparam NodeType the type of each node from the list
+ * \tparam ListType either vlc_list or const vlc_list
+ */
+template <typename NodeType, typename ListType>
+class list_iterator_base {
+protected:
+    ListType* _current, *_next, *_prev;
+    vlc_list NodeType::* _node_ptr;
+
+    constexpr std::ptrdiff_t offset() const {
+        return reinterpret_cast<std::ptrdiff_t>(
+            &(reinterpret_cast<NodeType const volatile*>(NULL)->*_node_ptr)
+        );
+    }
+
+    static constexpr bool is_const = std::is_const<ListType>::value;
+
+public:
+    using iterator_category = std::bidirectional_iterator_tag;
+    using value_type = std::conditional_t<is_const, const NodeType, NodeType>;
+    using pointer = std::conditional_t<is_const, const NodeType*, NodeType*>;
+    using reference = std::conditional_t<is_const, const NodeType&, NodeType&>;
+    using difference_type = std::ptrdiff_t;
+
+    using iterator_type = list_iterator_base<NodeType, ListType>;
+
+    list_iterator_base(ListType& list, vlc_list NodeType::* node_ptr)
+        : _current{&list}, _next{list.next}, _prev{list.prev}, _node_ptr{node_ptr} {}
+
+    reference operator*() const
+    {
+        using char_pointer = std::conditional_t<is_const, const char*, char*>;
+        return *reinterpret_cast<pointer>(
+                reinterpret_cast<char_pointer>(this->_current) - this->offset());
+    }
+
+    pointer operator->() const
+    {
+        return &operator*();
+    }
+
+    iterator_type operator++()
+    {
+        _prev = _next->prev;
+        _current = _next;
+        _next = _next->next;
+        return *this;
+    }
+
+    iterator_type operator++(int)
+    {
+        return iterator_type {*_next, _node_ptr};
+    }
+
+    iterator_type& operator--()
+    {
+        _next = _prev->next;
+        _current = _prev;
+        _prev = _prev->prev;
+        return *this;
+    }
+
+    iterator_type operator--(int)
+    {
+        return iterator_type {*_prev, _node_ptr};
+    }
+
+    friend bool operator==(const iterator_type& a, const iterator_type& b)
+    {
+        return a._current == b._current;
+    }
+
+    friend bool operator!=(const iterator_type& a, const iterator_type& b)
+    {
+        return a._current != b._current;
+    }
+};
+
+/**
+ * Iterator on vlc_list with mutable capabilities.
+ */
+template <typename NodeType>
+using list_iterator = list_iterator_base<NodeType, vlc_list>;
+
+/**
+ * Iterator on vlc_list with immutable capabilities.
+ */
+template <typename NodeType>
+using list_const_iterator = list_iterator_base<NodeType, const vlc_list>;
+
+template <typename NodeType, typename ListType>
+class list_reverse_iterator
+    : public std::reverse_iterator<list_iterator_base<NodeType, ListType>>
+{
+    using iterator = std::reverse_iterator<list_iterator_base<NodeType, ListType>>;
+    using inner_iterator = typename iterator::iterator_type;
+public:
+    list_reverse_iterator(ListType &list, vlc_list NodeType::* node_ptr)
+        : iterator {++inner_iterator{list, node_ptr}} {}
+
+    list_reverse_iterator(iterator other)
+        : iterator{other.base()} {}
+};
+
+/**
+ * Wrapper around any type matching with vlc_list, exposing C++ iterator operations.
+ *
+ * Users should use the vlc::list and vlc::const_list types instead,
+ * and initialize them from the vlc::from function.
+ *
+ * \code
+ * struct item {
+ *     // ...
+ *     vlc_list node;
+ * }
+ *
+ * struct vlc_list c_list;
+ * vlc_list_init(&c_list);
+ * // ...
+ * auto list = vlc::from(&list, &item::node);
+ * \endcode
+ *
+ * \tparam NodeType      the type of each node from the list
+ * \tparam ListType      either vlc_list or const vlc_list
+ * \tparam Iterator      the iterator type returned by vlc::list_base::begin()
+ *                       and vlc::list_base::end()
+ * \tparam ConstIterator the iterator type returned by vlc::list_base::cbegin()
+ *                       and vlc::list_base::cend()
+ */
+template <
+    typename NodeType,
+    typename ListType,
+    typename Iterator,
+    typename ConstIterator
+>
+class list_base
+{
+    /* We use some kind of offsetof, which will only be valid on
+     * standard layout types since non-standard might have a variable
+     * layout and still be pointer-compatible. */
+    static_assert(std::is_standard_layout<NodeType>::value,
+                  "list can only iterate standard layout types");
+
+protected:
+    ListType& _list;
+    vlc_list NodeType::* _node_ptr;
+
+    static bool constexpr is_reverse = !std::is_same<
+        Iterator, list_reverse_iterator<NodeType, ListType>>::value;
+
+public:
+    list_base(ListType &list, vlc_list NodeType::* node_ptr)
+        : _list{list}, _node_ptr{node_ptr} {}
+
+    using list_type = list_base<NodeType, ListType, Iterator, ConstIterator>;
+
+    using iterator = Iterator;
+    using const_iterator = ConstIterator;
+    using reverse_iterator = std::conditional_t<is_reverse,
+        list_reverse_iterator<NodeType, ListType>,
+        list_iterator_base<NodeType, ListType>>;
+    using const_reverse_iterator = std::conditional_t<is_reverse,
+        list_reverse_iterator<NodeType, const ListType>,
+        list_iterator_base<NodeType, const ListType>>;
+
+    using reverse_list = list_base<NodeType, ListType,
+                                   reverse_iterator, const_reverse_iterator>;
+
+    reverse_list as_reverse()
+    {
+        return reverse_list{_list, _node_ptr};
+    }
+
+    iterator begin() const
+    {
+        return ++iterator{_list, _node_ptr};
+    }
+
+    iterator end() const
+    {
+        return iterator{_list, _node_ptr};
+    }
+
+    const_iterator cbegin() const
+    {
+        return ++const_iterator{_list, _node_ptr};
+    }
+
+    const_iterator cend() const
+    {
+        return const_iterator{_list, _node_ptr};
+    }
+
+    reverse_iterator rbegin()
+    {
+        return ++reverse_iterator{_list, _node_ptr};
+    }
+
+    reverse_iterator rend()
+    {
+        return reverse_iterator{_list, _node_ptr};
+    }
+
+    const_reverse_iterator crbegin() const
+    {
+        return ++const_reverse_iterator{_list, _node_ptr};
+    }
+
+    const_reverse_iterator crend() const
+    {
+        return const_reverse_iterator{_list, _node_ptr};
+    }
+
+    friend bool operator==(const list_type& a, const list_type& b)
+    {
+        return a._list == b._list;
+    }
+
+    friend bool operator!=(const list_type& a, const list_type& b)
+    {
+        return a._list != b._list;
+    }
+
+    bool empty() const
+    {
+        return vlc_list_is_empty(_list);
+    }
+};
+
+/**
+ * Public type-safe wrapper around const vlc_list, providing const iterator
+ * and iteration functions.
+ *
+ * It is advised to use ::vlc::list::from() to get the correct
+ * wrapper directly in an inferenced way.
+ *
+ * \tparam NodeType the type of each node from the list
+ **/
+template <typename NodeType>
+struct const_list : public list_base<
+    NodeType,
+    const vlc_list,
+    list_const_iterator<NodeType>,
+    list_const_iterator<NodeType>
+>{
+public:
+    using iterator = ::vlc::list_const_iterator<NodeType>;
+    using const_iterator = ::vlc::list_const_iterator<NodeType>;
+
+    const_list(const vlc_list &l, vlc_list NodeType::* node_ptr)
+        : list_base<
+            NodeType, const vlc_list, iterator, const_iterator
+        >(l, node_ptr) {};
+};
+
+/**
+ * Public type-safe wrapper around mutable vlc_list, providing iterators,
+ * iteration functions and mutation on the list itself.
+ *
+ * \tparam NodeType the type of each node from the list
+ **/
+template <typename NodeType>
+struct list : public list_base<
+    NodeType,
+    vlc_list,
+    list_iterator<NodeType>,
+    list_const_iterator<NodeType>
+>{
+
+public:
+    using iterator = ::vlc::list_iterator<NodeType>;
+    using const_iterator = ::vlc::list_const_iterator<NodeType>;
+
+    /**
+     * Construct a ::vlc::list from an existing vlc_list.
+     *
+     * It is advised to use ::vlc::list::from() to get the correct
+     * wrapper directly in an inferenced way.
+     **/
+    list(vlc_list &l, vlc_list NodeType::* node_ptr)
+        : list_base<
+            NodeType, vlc_list, iterator, const_iterator
+        >(l, node_ptr) {};
+
+    template <typename IteratorType>
+    IteratorType erase(IteratorType it)
+    {
+        vlc_list_remove(&((*it).*(this->_node_ptr)));
+        return it;
+    }
+
+    void push_front(NodeType &item)
+    {
+        struct vlc_list *node = &(item.*(this->_node_ptr));
+        vlc_list_prepend(node, &this->_list);
+    }
+
+    void push_back(NodeType &item)
+    {
+        struct vlc_list *node = &(item.*(this->_node_ptr));
+        vlc_list_append(node, &this->_list);
+    }
+};
+
+/**
+ * Construct a vlc::list (mutable list) object from a mutable
+ * vlc_list reference
+ *
+ * \tparam NodeType      the type of each node from the list
+ *
+ * \param list the vlc_list object to wrap around
+ * \param node_ptr a pointer to the intrusive vlc_list member from the
+ *                 type being stored in the list.
+ * \return a vlc::list instance
+ * */
+template <typename NodeType>
+::vlc::list<NodeType> from(vlc_list &list, vlc_list NodeType::* node_ptr)
+{
+    return ::vlc::list<NodeType>{list, node_ptr};
+}
+
+/**
+ * Construct a vlc::const_list (immutable list) object from a const
+ * vlc_list reference
+ *
+ * \tparam NodeType      the type of each node from the list
+ *
+ * \param list the vlc_list object to wrap around
+ * \param node_ptr a pointer to the intrusive vlc_list member from the
+ *                 type being stored in the list.
+ * \return a vlc::const_list instance which cannot modify the vlc_list
+ * */
+template <typename NodeType>
+::vlc::const_list<NodeType> from(const vlc_list &list, vlc_list NodeType::* node_ptr)
+{
+    return ::vlc::const_list<NodeType>{list, node_ptr};
+}
+
+/** @} */
+}
+
+#endif /* VLC_LIST_HPP */


=====================================
modules/gui/qt/dialogs/mediainfo/info_panels.cpp
=====================================
@@ -37,6 +37,7 @@
 #include <vlc_url.h>
 #include <vlc_meta.h>
 #include <vlc_input_item.h>
+#include <vlc_list.hpp>
 
 #include <QTreeWidget>
 #include <QTableWidget>
@@ -507,25 +508,19 @@ void InfoPanel::update( input_item_t *p_item)
 
     vlc_mutex_locker locker( &p_item->lock );
 
-    info_category_t *cat;
-    vlc_list_foreach(cat, &p_item->categories, node)
+    for (auto &cat : vlc::from(p_item->categories, &info_category_t::node))
     {
-        if (info_category_IsHidden(cat))
+        if (info_category_IsHidden(&cat))
             continue;
 
-        struct vlc_list *const head = &cat->infos;
-
         current_item = new QTreeWidgetItem();
-        current_item->setText( 0, qfu(cat->psz_name) );
+        current_item->setText(0, qfu(cat.psz_name));
         InfoTree->addTopLevelItem( current_item );
 
-        for (info_t *info = vlc_list_first_entry_or_null(head, info_t, node);
-             info != NULL;
-             info = vlc_list_next_entry_or_null(head, info, info_t, node))
+        for (auto &info : vlc::from(cat.infos, &info_t::node))
         {
             child_item = new QTreeWidgetItem ();
-            child_item->setText( 0, qfu(info->psz_name) + ": "
-                                    + qfu(info->psz_value));
+            child_item->setText(0, qfu(info.psz_name) + ": " + qfu(info.psz_value));
             current_item->addChild(child_item);
         }
         current_item->setExpanded( true );


=====================================
src/Makefile.am
=====================================
@@ -126,6 +126,7 @@ pluginsinclude_HEADERS.h = \
 
 pluginsinclude_HEADERS.hpp = \
 	../include/vlc_cxx_helpers.hpp \
+	../include/vlc_list.hpp \
 	$(NULL)
 
 pluginsinclude_HEADERS = \
@@ -636,7 +637,8 @@ check_PROGRAMS = \
 	test_media_source \
 	test_extensions \
 	test_thread \
-	test_diffutil
+	test_diffutil \
+	test_cpp_list
 
 TESTS = $(check_PROGRAMS) check_symbols
 
@@ -685,6 +687,7 @@ test_media_source_SOURCES = media_source/test.c \
 	media_source/media_source.c \
 	media_source/media_tree.c
 test_thread_SOURCES = test/thread.c
+test_cpp_list_SOURCES = test/list_cpp.cpp
 
 LDADD = libvlccore.la \
 	../compat/libcompat.la
@@ -757,6 +760,7 @@ $(checkheaders.hpp): CHECKCOMPILE=$(LTCXXCOMPILE)
 # Some exceptions for headers depending on other libraries
 checkheader_vlc_gcrypt.h.check: CHECK_CFLAGS = "-Dgcry_check_version(x)="
 checkheader_vlc_xlib.h.check: CHECK_CFLAGS = $(X_CFLAGS)
+checkheader_vlc_list.hpp.check: CHECK_CFLAGS = "-Drestrict="
 
 SKIPPED_CHECKHEADERS =
 if !HAVE_X


=====================================
src/test/list_cpp.cpp
=====================================
@@ -0,0 +1,305 @@
+/*****************************************************************************
+ * list.cpp: tests for vlc_list in C++
+ *****************************************************************************
+ * Copyright (C) 2024 VideoLabs
+ *
+ * Authors: Alexandre Janniaux <ajanni at videolabs.io>
+ *          Pierre Lamot <pierre at videolabs.io>
+ *
+ * This program 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 program 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 program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vlc_common.h>
+#include <vlc_list.hpp>
+
+#include <cassert>
+
+struct item {
+    int index = 0;
+    struct vlc_list node;
+};
+
+struct test_list {
+    struct vlc_list list;
+
+    test_list()
+    {
+        vlc_list_init(&list);
+    }
+};
+
+int main()
+{
+    {
+        test_list o;
+        auto l = vlc::from(o.list, &item::node);
+        assert(l.begin() == l.end());
+        assert(l.cbegin() == l.cend());
+        assert(l.rbegin() == l.rend());
+        assert(l.crbegin() == l.crend());
+    }
+
+    {
+        test_list o;
+        item t1; t1.index = 5;
+        vlc_list_append(&t1.node, &o.list);
+
+        auto l = vlc::from(o.list, &item::node);
+        assert(l.begin() != l.end());
+        assert(l.cbegin() != l.cend());
+        assert(l.rbegin() != l.rend());
+        assert(l.crbegin() != l.crend());
+
+        assert(l.begin()->index == 5);
+        assert(l.rbegin()->index == 5);
+        assert(l.cbegin()->index == 5);
+        assert(l.crbegin()->index == 5);
+
+        item t2; t2.index = 10;
+        vlc_list_append(&t2.node, &o.list);
+
+        assert(l.begin()->index == 5);
+        assert(l.cbegin()->index == 5);
+        assert(l.rbegin()->index == 10);
+        assert(l.crbegin()->index == 10);
+
+        assert(l.as_reverse().begin()->index == 10);
+        assert(l.as_reverse().cbegin()->index == 10);
+        assert(l.as_reverse().rbegin()->index == 5);
+        assert(l.as_reverse().crbegin()->index == 5);
+
+
+    }
+
+    /* Check that the type system will handle the const variant. */
+    {
+        const test_list l;
+
+        /* Note: the type must be enforced here. */
+        vlc::const_list<item> t = vlc::from(l.list, &item::node);
+        (void)t;
+    }
+
+    {
+        test_list l;
+        item t1, t2;
+        vlc_list_append(&t1.node, &l.list);
+        vlc_list_append(&t2.node, &l.list);
+
+
+        {
+            int i = 1;
+            for (auto &t : vlc::from(l.list, &item::node))
+            {
+                fprintf(stderr, "Checking mutable list, got %d, expects %d\n", t.index, i);
+                t.index = i++;
+            }
+            assert(t1.index == 1 && t2.index == 2);
+        }
+
+        {
+            int i = 1;
+            const test_list& lc = l;
+            for (const auto &t : vlc::from(lc.list, &item::node))
+            {
+                fprintf(stderr, "Checking const list, got %d, expects %d\n", t.index, i);
+                assert(i++ == t.index);
+            }
+            assert(i == 3);
+        }
+
+        {
+            /* const reverse version */
+            int i = 2;
+            const test_list& lc = l;
+            for (const auto &t : vlc::from(lc.list, &item::node).as_reverse())
+            {
+                fprintf(stderr, "Checking reverse mutable list, got %d, expects %d\n", t.index, i);
+                assert(i-- == t.index);
+            }
+            assert(i == 0);
+        }
+
+        {
+            /* mutable reverse version */
+            int i = 2;
+            test_list& lc = l;
+            for (auto &t : vlc::from(lc.list, &item::node).as_reverse())
+            {
+                fprintf(stderr, "Checking reverse const list, got %d, expects %d\n", t.index, i);
+                assert(i-- == t.index);
+            }
+            assert(i == 0);
+        }
+    }
+
+    {
+        test_list l;
+        item t1, t2;
+        vlc_list_append(&t1.node, &l.list);
+        vlc_list_append(&t2.node, &l.list);
+        t1.index = 1;
+        t2.index = 2;
+
+        int index = 0;
+        test_list& lc = l;
+        for (auto &t : vlc::from(lc.list, &item::node))
+        {
+            if (t.index == 1)
+                vlc_list_remove(&t.node);
+            else
+                index = t.index;
+        }
+        assert(index == 2);
+        assert(vlc_list_is_first(&t2.node, &l.list));
+        assert(vlc_list_is_last(&t2.node, &l.list));
+    }
+
+    {
+        test_list l;
+        item t1, t2;
+        vlc_list_append(&t1.node, &l.list);
+        vlc_list_append(&t2.node, &l.list);
+        t1.index = 1;
+        t2.index = 2;
+
+        int index = 0;
+        test_list& lc = l;
+        for (auto &t : vlc::from(lc.list, &item::node).as_reverse())
+        {
+            if (t.index == 2)
+                vlc_list_remove(&t.node);
+            else
+                index = t.index;
+        }
+        assert(index == 1);
+        assert(vlc_list_is_first(&t1.node, &l.list));
+        assert(vlc_list_is_last(&t1.node, &l.list));
+    }
+
+    {
+        /* Check that removal are working correctly when the current item is
+         * being removed during iteration. */
+        test_list l;
+        item t1, t2;
+        vlc_list_append(&t1.node, &l.list);
+        vlc_list_append(&t2.node, &l.list);
+        t1.index = 1;
+        t2.index = 2;
+
+        auto list = vlc::from(l.list, &item::node);
+        auto it = list.begin();
+        assert((*it).index == 1);
+
+        vlc_list_remove(&(*it).node);
+        ++it;
+        assert(it != list.end());
+        assert(it == list.begin());
+        assert((*it).index == 2);
+        --it;
+        assert(it == list.end());
+    }
+
+    {
+        test_list l;
+        auto lw = vlc::from(l.list, &item::node);
+
+        item t1; t1.index = 1;
+        lw.push_front(t1);
+
+        item t2; t2.index = 2;
+        lw.push_front(t2);
+
+        item t3; t3.index = 3;
+        lw.push_front(t3);
+
+        int i = 3;
+        for (auto &t : lw)
+        {
+            assert(t.index == i--);
+        }
+        assert(i == 0);
+
+        auto it = lw.begin();
+        ++it;
+        assert((*it).index == 2);
+        lw.erase(it);
+        assert((*++lw.begin()).index == 1);
+    }
+
+    {
+        test_list l;
+        auto lw = vlc::from(l.list, &item::node);
+
+        item t1; t1.index = 1;
+        lw.push_front(t1);
+
+        item t2; t2.index = 2;
+        lw.push_front(t2);
+
+        item t3; t3.index = 3;
+        lw.push_front(t3);
+
+        int i = 1;
+        for (auto &t : lw.as_reverse())
+        {
+            assert(t.index == i++);
+        }
+        assert(i == 4);
+
+        auto it = lw.begin();
+        ++it;
+        assert((*it).index == 2);
+        lw.erase(it);
+        assert((*++lw.begin()).index == 1);
+    }
+
+    {
+        test_list l;
+        auto lw = vlc::from(l.list, &item::node);
+
+        item t1; t1.index = 1;
+        lw.push_front(t1);
+
+        item t2; t2.index = 2;
+        lw.push_front(t2);
+
+        item t3; t3.index = 3;
+        lw.push_front(t3);
+
+        auto lr = vlc::from(l.list, &item::node).as_reverse();
+        {
+            auto it = lw.erase(lr.begin());
+            fprintf(stderr, "Removing as_reverse().begin() in [ 1, 2, 3 ] and "
+                    "checking as_reverse().begin(), got %d, expects %d, removed %d\n",
+                    lr.begin()->index, 2, it->index);
+        }
+        assert(lr.begin()->index == 2);
+
+        {
+            auto it = lw.erase(lr.rbegin());
+            fprintf(stderr, "Removing as_reverse().rbegin() in [ 1, 2 ] and "
+                    "checking as_reverse().rbegin(), got %d, expects %d, removed %d\n",
+                    lr.rbegin()->index, 2, it->index);
+        }
+        assert(lr.rbegin()->index == 2);
+    }
+
+    return 0;
+}



View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/fd1444e89b52e41d6e0189e607bd0b0cc1656407...ef35d253cbac5ab9b902807761d807ac6a4c1348

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/fd1444e89b52e41d6e0189e607bd0b0cc1656407...ef35d253cbac5ab9b902807761d807ac6a4c1348
You're receiving this email because of your account on code.videolan.org.


VideoLAN code repository instance


More information about the vlc-commits mailing list