123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828 |
- #ifndef INCLUDES_MYSQL_SQL_LIST_H
- #define INCLUDES_MYSQL_SQL_LIST_H
- /* Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License, version 2.0,
- as published by the Free Software Foundation.
- This program is also distributed with certain software (including
- but not limited to OpenSSL) that is licensed under separate terms,
- as designated in a particular file or component or in included license
- documentation. The authors of MySQL hereby grant you an additional
- permission to link the program and your derivative works with the
- separately licensed software that they have included with MySQL.
- 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 General Public License, version 2.0, for more details.
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
- #include <stddef.h>
- #include <sys/types.h>
- #include <algorithm>
- #include <iterator>
- #include <type_traits>
- #include "my_alloc.h"
- #include "my_compiler.h"
- #include "my_dbug.h"
- #include "my_sharedlib.h"
- #include "sql/thr_malloc.h"
- /**
- Simple intrusive linked list.
- @remark Similar in nature to base_list, but intrusive. It keeps a
- a pointer to the first element in the list and a indirect
- reference to the last element.
- */
- template <typename T>
- class SQL_I_List {
- public:
- uint elements;
- /** The first element in the list. */
- T *first;
- /** A reference to the next element in the list. */
- T **next;
- SQL_I_List() { empty(); }
- SQL_I_List(const SQL_I_List &tmp)
- : elements(tmp.elements),
- first(tmp.first),
- next(elements ? tmp.next : &first) {}
- SQL_I_List(SQL_I_List &&) = default;
- inline void empty() {
- elements = 0;
- first = NULL;
- next = &first;
- }
- inline void link_in_list(T *element, T **next_ptr) {
- elements++;
- (*next) = element;
- next = next_ptr;
- *next = NULL;
- }
- inline void save_and_clear(SQL_I_List<T> *save) {
- *save = *this;
- empty();
- }
- inline void push_front(SQL_I_List<T> *save) {
- /* link current list last */
- *save->next = first;
- first = save->first;
- elements += save->elements;
- }
- inline void push_back(SQL_I_List<T> *save) {
- if (save->first) {
- *next = save->first;
- next = save->next;
- elements += save->elements;
- }
- }
- inline uint size() const { return elements; }
- SQL_I_List &operator=(SQL_I_List &) = default;
- SQL_I_List &operator=(SQL_I_List &&) = default;
- };
- /*
- Basic single linked list
- Used for item and item_buffs.
- All list ends with a pointer to the 'end_of_list' element, which
- data pointer is a null pointer and the next pointer points to itself.
- This makes it very fast to traverse lists as we don't have to
- test for a specialend condition for list that can't contain a null
- pointer.
- */
- /**
- list_node - a node of a single-linked list.
- @note We never call a destructor for instances of this class.
- */
- struct list_node {
- list_node *next;
- void *info;
- list_node(void *info_par, list_node *next_par)
- : next(next_par), info(info_par) {}
- list_node() /* For end_of_list */
- {
- info = 0;
- next = this;
- }
- };
- extern MYSQL_PLUGIN_IMPORT list_node end_of_list;
- class base_list {
- protected:
- list_node *first, **last;
- public:
- uint elements;
- bool operator==(const base_list &rhs) const {
- return elements == rhs.elements && first == rhs.first && last == rhs.last;
- }
- inline void empty() {
- elements = 0;
- first = &end_of_list;
- last = &first;
- }
- inline base_list() { empty(); }
- /**
- This is a shallow copy constructor that implicitly passes the ownership
- from the source list to the new instance. The old instance is not
- updated, so both objects end up sharing the same nodes. If one of
- the instances then adds or removes a node, the other becomes out of
- sync ('last' pointer), while still operational. Some old code uses and
- relies on this behaviour. This logic is quite tricky: please do not use
- it in any new code.
- */
- base_list(const base_list &tmp)
- : first(tmp.first),
- last(tmp.elements ? tmp.last : &first),
- elements(tmp.elements) {}
- base_list &operator=(const base_list &tmp) {
- elements = tmp.elements;
- first = tmp.first;
- last = elements ? tmp.last : &first;
- return *this;
- }
- /**
- Construct a deep copy of the argument in memory root mem_root.
- The elements themselves are copied by pointer.
- */
- base_list(const base_list &rhs, MEM_ROOT *mem_root);
- inline bool push_back(void *info) {
- if (((*last) = new (*THR_MALLOC) list_node(info, &end_of_list))) {
- last = &(*last)->next;
- elements++;
- return 0;
- }
- return 1;
- }
- inline bool push_back(void *info, MEM_ROOT *mem_root) {
- if (((*last) = new (mem_root) list_node(info, &end_of_list))) {
- last = &(*last)->next;
- elements++;
- return 0;
- }
- return 1;
- }
- inline bool push_front(void *info) {
- list_node *node = new (*THR_MALLOC) list_node(info, first);
- if (node) {
- if (last == &first) last = &node->next;
- first = node;
- elements++;
- return 0;
- }
- return 1;
- }
- inline bool push_front(void *info, MEM_ROOT *mem_root) {
- list_node *node = new (mem_root) list_node(info, first);
- if (node) {
- if (last == &first) last = &node->next;
- first = node;
- elements++;
- return false;
- }
- return true;
- }
- void remove(list_node **prev) {
- list_node *node = (*prev)->next;
- if (!--elements)
- last = &first;
- else if (last == &(*prev)->next)
- last = prev;
- destroy(*prev);
- *prev = node;
- }
- inline void concat(base_list *list) {
- if (!list->is_empty()) {
- *last = list->first;
- last = list->last;
- elements += list->elements;
- }
- }
- inline void *pop(void) {
- if (first == &end_of_list) return 0;
- list_node *tmp = first;
- first = first->next;
- if (!--elements) last = &first;
- return tmp->info;
- }
- inline void disjoin(base_list *list) {
- list_node **prev = &first;
- list_node *node = first;
- list_node *list_first = list->first;
- elements = 0;
- while (node && node != list_first) {
- prev = &node->next;
- node = node->next;
- elements++;
- }
- *prev = *last;
- last = prev;
- }
- inline void prepend(base_list *list) {
- if (!list->is_empty()) {
- *list->last = first;
- if (is_empty()) last = list->last;
- first = list->first;
- elements += list->elements;
- }
- }
- /**
- Swap two lists.
- */
- inline void swap(base_list &rhs) {
- std::swap(first, rhs.first);
- std::swap(last, rhs.last);
- std::swap(elements, rhs.elements);
- }
- inline list_node *last_node() { return *last; }
- inline list_node *first_node() { return first; }
- inline void *head() { return first->info; }
- inline const void *head() const { return first->info; }
- inline void **head_ref() { return first != &end_of_list ? &first->info : 0; }
- inline bool is_empty() const { return first == &end_of_list; }
- inline list_node *last_ref() { return &end_of_list; }
- inline uint size() const { return elements; }
- friend class base_list_iterator;
- friend class error_list;
- friend class error_list_iterator;
- #ifdef LIST_EXTRA_DEBUG
- /*
- Check list invariants and print results into trace. Invariants are:
- - (*last) points to end_of_list
- - There are no NULLs in the list.
- - base_list::elements is the number of elements in the list.
- SYNOPSIS
- check_list()
- name Name to print to trace file
- RETURN
- 1 The list is Ok.
- 0 List invariants are not met.
- */
- bool check_list(const char *name) {
- base_list *list = this;
- list_node *node = first;
- uint cnt = 0;
- while (node->next != &end_of_list) {
- if (!node->info) {
- DBUG_PRINT("list_invariants",
- ("%s: error: NULL element in the list", name));
- return false;
- }
- node = node->next;
- cnt++;
- }
- if (last != &(node->next)) {
- DBUG_PRINT("list_invariants", ("%s: error: wrong last pointer", name));
- return false;
- }
- if (cnt + 1 != elements) {
- DBUG_PRINT("list_invariants", ("%s: error: wrong element count", name));
- return false;
- }
- DBUG_PRINT("list_invariants", ("%s: list is ok", name));
- return true;
- }
- #endif // LIST_EXTRA_DEBUG
- protected:
- void after(void *info, list_node *node) {
- list_node *new_node = new (*THR_MALLOC) list_node(info, node->next);
- node->next = new_node;
- elements++;
- if (last == &(node->next)) last = &new_node->next;
- }
- bool after(void *info, list_node *node, MEM_ROOT *mem_root) {
- list_node *new_node = new (mem_root) list_node(info, node->next);
- if (!new_node) return true; // OOM
- node->next = new_node;
- elements++;
- if (last == &(node->next)) last = &new_node->next;
- return false;
- }
- };
- class base_list_iterator {
- protected:
- base_list *list;
- list_node **el, **prev, *current;
- void sublist(base_list &ls, uint elm) {
- ls.first = *el;
- ls.last = list->last;
- ls.elements = elm;
- }
- public:
- base_list_iterator() : list(0), el(0), prev(0), current(0) {}
- base_list_iterator(base_list &list_par) { init(list_par); }
- inline void init(base_list &list_par) {
- list = &list_par;
- el = &list_par.first;
- prev = 0;
- current = 0;
- }
- inline void *next(void) {
- prev = el;
- current = *el;
- el = ¤t->next;
- return current->info;
- }
- inline void *next_fast(void) {
- list_node *tmp;
- tmp = *el;
- el = &tmp->next;
- return tmp->info;
- }
- inline void rewind(void) { el = &list->first; }
- inline void *replace(void *element) { // Return old element
- void *tmp = current->info;
- DBUG_ASSERT(current->info != 0);
- current->info = element;
- return tmp;
- }
- void *replace(base_list &new_list) {
- void *ret_value = current->info;
- if (!new_list.is_empty()) {
- *new_list.last = current->next;
- current->info = new_list.first->info;
- current->next = new_list.first->next;
- if ((list->last == ¤t->next) && (new_list.elements > 1))
- list->last = new_list.last;
- list->elements += new_list.elements - 1;
- }
- return ret_value; // return old element
- }
- inline void remove(void) // Remove current
- {
- list->remove(prev);
- el = prev;
- current = 0; // Safeguard
- }
- void after(void *element) // Insert element after current
- {
- list->after(element, current);
- current = current->next;
- el = ¤t->next;
- }
- bool after(void *a, MEM_ROOT *mem_root) {
- if (list->after(a, current, mem_root)) return true;
- current = current->next;
- el = ¤t->next;
- return false;
- }
- inline void **ref(void) // Get reference pointer
- {
- return ¤t->info;
- }
- inline bool is_last(void) { return el == list->last; }
- inline bool is_before_first() const { return current == NULL; }
- bool prepend(void *a, MEM_ROOT *mem_root) {
- if (list->push_front(a, mem_root)) return true;
- el = &list->first;
- prev = el;
- el = &(*el)->next;
- return false;
- }
- friend class error_list_iterator;
- };
- template <class T>
- class List_STL_Iterator;
- template <class T>
- class List : public base_list {
- public:
- List() : base_list() {}
- inline List(const List<T> &tmp) : base_list(tmp) {}
- List &operator=(const List &tmp) {
- return static_cast<List &>(base_list::operator=(tmp));
- }
- inline List(const List<T> &tmp, MEM_ROOT *mem_root)
- : base_list(tmp, mem_root) {}
- /*
- Typecasting to (void *) it's necessary if we want to declare List<T> with
- constant T parameter (like List<const char>), since the untyped storage
- is "void *", and assignment of const pointer to "void *" is a syntax error.
- */
- inline bool push_back(T *a) { return base_list::push_back((void *)a); }
- inline bool push_back(T *a, MEM_ROOT *mem_root) {
- return base_list::push_back((void *)a, mem_root);
- }
- inline bool push_front(T *a) { return base_list::push_front((void *)a); }
- inline bool push_front(T *a, MEM_ROOT *mem_root) {
- return base_list::push_front((void *)a, mem_root);
- }
- inline T *head() { return static_cast<T *>(base_list::head()); }
- inline const T *head() const {
- return static_cast<const T *>(base_list::head());
- }
- inline T **head_ref() { return (T **)base_list::head_ref(); }
- inline T *pop() { return (T *)base_list::pop(); }
- inline void concat(List<T> *list) { base_list::concat(list); }
- inline void disjoin(List<T> *list) { base_list::disjoin(list); }
- inline void prepend(List<T> *list) { base_list::prepend(list); }
- void delete_elements(void) {
- list_node *element, *next;
- for (element = first; element != &end_of_list; element = next) {
- next = element->next;
- delete (T *)element->info;
- }
- empty();
- }
- void destroy_elements(void) {
- list_node *element, *next;
- for (element = first; element != &end_of_list; element = next) {
- next = element->next;
- destroy((T *)element->info);
- }
- empty();
- }
- T *operator[](uint index) const {
- DBUG_ASSERT(index < elements);
- list_node *current = first;
- for (uint i = 0; i < index; ++i) current = current->next;
- return static_cast<T *>(current->info);
- }
- void replace(uint index, T *new_value) {
- DBUG_ASSERT(index < elements);
- list_node *current = first;
- for (uint i = 0; i < index; ++i) current = current->next;
- current->info = new_value;
- }
- bool swap_elts(uint index1, uint index2) {
- if (index1 == index2) return false;
- if (index1 >= elements || index2 >= elements) return true; // error
- if (index2 < index1) std::swap(index1, index2);
- list_node *current1 = first;
- for (uint i = 0; i < index1; ++i) current1 = current1->next;
- list_node *current2 = current1;
- for (uint i = 0; i < index2 - index1; ++i) current2 = current2->next;
- std::swap(current1->info, current2->info);
- return false;
- }
- /**
- @brief
- Sort the list
- @param cmp node comparison function
- @details
- The function sorts list nodes by an exchange sort algorithm.
- The order of list nodes isn't changed, values of info fields are
- swapped instead. Due to this, list iterators that are initialized before
- sort could be safely used after sort, i.e they wouldn't cause a crash.
- As this isn't an effective algorithm the list to be sorted is supposed to
- be short.
- */
- template <typename Node_cmp_func>
- void sort(Node_cmp_func cmp) {
- if (elements < 2) return;
- for (list_node *n1 = first; n1 && n1 != &end_of_list; n1 = n1->next) {
- for (list_node *n2 = n1->next; n2 && n2 != &end_of_list; n2 = n2->next) {
- if (cmp(static_cast<T *>(n1->info), static_cast<T *>(n2->info)) > 0) {
- void *tmp = n1->info;
- n1->info = n2->info;
- n2->info = tmp;
- }
- }
- }
- }
- // For C++11 range-based for loops.
- using iterator = List_STL_Iterator<T>;
- iterator begin() { return iterator(first); }
- iterator end() {
- // If the list overlaps another list, last isn't actually
- // the last element, and if so, we'd give a different result from
- // List_iterator_fast.
- DBUG_ASSERT((*last)->next == &end_of_list);
- return iterator(*last);
- }
- using const_iterator = List_STL_Iterator<const T>;
- const_iterator begin() const { return const_iterator(first); }
- const_iterator end() const {
- DBUG_ASSERT((*last)->next == &end_of_list);
- return const_iterator(*last);
- }
- const_iterator cbegin() const { return const_iterator(first); }
- const_iterator cend() const {
- DBUG_ASSERT((*last)->next == &end_of_list);
- return const_iterator(*last);
- }
- };
- template <class T>
- class List_iterator : public base_list_iterator {
- public:
- List_iterator(List<T> &a) : base_list_iterator(a) {}
- List_iterator() : base_list_iterator() {}
- inline void init(List<T> &a) { base_list_iterator::init(a); }
- inline T *operator++(int) { return (T *)base_list_iterator::next(); }
- inline T *replace(T *a) { return (T *)base_list_iterator::replace(a); }
- inline T *replace(List<T> &a) { return (T *)base_list_iterator::replace(a); }
- inline void rewind(void) { base_list_iterator::rewind(); }
- inline void remove() { base_list_iterator::remove(); }
- inline void after(T *a) { base_list_iterator::after(a); }
- inline bool after(T *a, MEM_ROOT *mem_root) {
- return base_list_iterator::after(a, mem_root);
- }
- inline T **ref(void) { return (T **)base_list_iterator::ref(); }
- };
- template <class T>
- class List_iterator_fast : public base_list_iterator {
- protected:
- inline T *replace(T *) { return (T *)0; }
- inline T *replace(List<T> &) { return (T *)0; }
- inline void remove(void) {}
- inline void after(T *) {}
- inline T **ref(void) { return (T **)0; }
- public:
- inline List_iterator_fast(List<T> &a) : base_list_iterator(a) {}
- inline List_iterator_fast() : base_list_iterator() {}
- inline void init(List<T> &a) { base_list_iterator::init(a); }
- inline T *operator++(int) { return (T *)base_list_iterator::next_fast(); }
- inline void rewind(void) { base_list_iterator::rewind(); }
- void sublist(List<T> &list_arg, uint el_arg) {
- base_list_iterator::sublist(list_arg, el_arg);
- }
- };
- /*
- Like List_iterator<T>, but with an STL-compatible interface
- (ForwardIterator), so that you can use it in range-based for loops.
- Prefer this to List_iterator<T> wherever possible, but also prefer
- std::vector<T> or std::list<T> to List<T> wherever possible.
- */
- template <class T>
- class List_STL_Iterator {
- public:
- explicit List_STL_Iterator(list_node *node) : m_current(node) {}
- // Iterator (required for InputIterator).
- T &operator*() const { return *static_cast<T *>(m_current->info); }
- List_STL_Iterator &operator++() {
- m_current = m_current->next;
- return *this;
- }
- using difference_type = ptrdiff_t;
- using value_type = T; // NOTE: std::remove_cv_t<T> from C++20.
- using pointer = T *;
- using reference = T &;
- using iterator_category = std::forward_iterator_tag;
- // EqualityComparable (required for InputIterator).
- bool operator==(const List_STL_Iterator &other) const {
- return m_current == other.m_current;
- }
- // InputIterator (required for ForwardIterator).
- bool operator!=(const List_STL_Iterator &other) const {
- return !(*this == other);
- }
- T *operator->() const { return static_cast<T *>(m_current->info); }
- // DefaultConstructible (required for ForwardIterator).
- List_STL_Iterator() {}
- // ForwardIterator.
- List_STL_Iterator operator++(int) {
- List_STL_Iterator copy = *this;
- m_current = m_current->next;
- return copy;
- }
- private:
- list_node *m_current;
- };
- template <typename T>
- class base_ilist;
- template <typename T>
- class base_ilist_iterator;
- /*
- A simple intrusive list.
- NOTE: this inherently unsafe, since we rely on <T> to have
- the same layout as ilink<T> (see base_ilist::sentinel).
- Please consider using a different strategy for linking objects.
- */
- template <typename T>
- class ilink {
- T **prev, *next;
- public:
- ilink() : prev(NULL), next(NULL) {}
- void unlink() {
- /* Extra tests because element doesn't have to be linked */
- if (prev) *prev = next;
- if (next) next->prev = prev;
- prev = NULL;
- next = NULL;
- }
- friend class base_ilist<T>;
- friend class base_ilist_iterator<T>;
- };
- /* Needed to be able to have an I_List of char* strings in mysqld.cc. */
- class i_string : public ilink<i_string> {
- public:
- const char *ptr;
- i_string() : ptr(0) {}
- i_string(const char *s) : ptr(s) {}
- };
- /* needed for linked list of two strings for replicate-rewrite-db */
- class i_string_pair : public ilink<i_string_pair> {
- public:
- const char *key;
- const char *val;
- i_string_pair() : key(0), val(0) {}
- i_string_pair(const char *key_arg, const char *val_arg)
- : key(key_arg), val(val_arg) {}
- };
- template <class T>
- class I_List_iterator;
- template <typename T>
- class base_ilist {
- T *first;
- ilink<T> sentinel;
- static_assert(!std::is_polymorphic<T>::value,
- "Do not use this for classes with virtual members");
- public:
- // The sentinel is not a T, but at least it is a POD
- void empty() SUPPRESS_UBSAN {
- first = static_cast<T *>(&sentinel);
- sentinel.prev = &first;
- }
- base_ilist() { empty(); }
- // The sentinel is not a T, but at least it is a POD
- bool is_empty() const SUPPRESS_UBSAN {
- return first == static_cast<const T *>(&sentinel);
- }
- /// Pushes new element in front of list.
- void push_front(T *a) {
- first->prev = &a->next;
- a->next = first;
- a->prev = &first;
- first = a;
- }
- /// Pushes new element to the end of the list, i.e. in front of the sentinel.
- void push_back(T *a) {
- *sentinel.prev = a;
- a->next = static_cast<T *>(&sentinel);
- a->prev = sentinel.prev;
- sentinel.prev = &a->next;
- }
- // Unlink first element, and return it.
- T *get() {
- if (is_empty()) return NULL;
- T *first_link = first;
- first_link->unlink();
- return first_link;
- }
- T *head() { return is_empty() ? NULL : first; }
- /**
- Moves list elements to new owner, and empties current owner (i.e. this).
- @param[in,out] new_owner The new owner of the list elements.
- Should be empty in input.
- */
- void move_elements_to(base_ilist *new_owner) {
- DBUG_ASSERT(new_owner->is_empty());
- new_owner->first = first;
- new_owner->sentinel = sentinel;
- empty();
- }
- friend class base_ilist_iterator<T>;
- private:
- /*
- We don't want to allow copying of this class, as that would give us
- two list heads containing the same elements.
- So we declare, but don't define copy CTOR and assignment operator.
- */
- base_ilist(const base_ilist &);
- void operator=(const base_ilist &);
- };
- template <typename T>
- class base_ilist_iterator {
- base_ilist<T> *list;
- T **el, *current;
- public:
- base_ilist_iterator(base_ilist<T> &list_par)
- : list(&list_par), el(&list_par.first), current(NULL) {}
- // The sentinel is not a T, but at least it is a POD
- T *next(void) SUPPRESS_UBSAN {
- /* This is coded to allow push_back() while iterating */
- current = *el;
- if (current == static_cast<T *>(&list->sentinel)) return NULL;
- el = ¤t->next;
- return current;
- }
- };
- template <class T>
- class I_List : private base_ilist<T> {
- public:
- using base_ilist<T>::empty;
- using base_ilist<T>::is_empty;
- using base_ilist<T>::get;
- using base_ilist<T>::push_front;
- using base_ilist<T>::push_back;
- using base_ilist<T>::head;
- void move_elements_to(I_List<T> *new_owner) {
- base_ilist<T>::move_elements_to(new_owner);
- }
- friend class I_List_iterator<T>;
- };
- template <class T>
- class I_List_iterator : public base_ilist_iterator<T> {
- public:
- I_List_iterator(I_List<T> &a) : base_ilist_iterator<T>(a) {}
- inline T *operator++(int) { return base_ilist_iterator<T>::next(); }
- };
- void free_list(I_List<i_string_pair> *list);
- void free_list(I_List<i_string> *list);
- template <class T>
- List<T> *List_merge(T *head, List<T> *tail) {
- tail->push_front(head);
- return tail;
- }
- #endif // INCLUDES_MYSQL_SQL_LIST_H
|