From dfef28de887eba43747bca52584f8310450e243a Mon Sep 17 00:00:00 2001 From: Jacob Erlbeck Date: Mon, 11 May 2015 14:13:47 +0200 Subject: [PATCH] llist: Add a C++ wrapper for linux_list This commit adds the LListHead class which is a wrapper around the linuxlist. It adds an additional member to refer to the container, since the container_of macro doesn't work properly with C++ classes. All functions and macros from linuxlist.h are support except for the entry macros (e.g. llist_entry, llist_for_each_entry, ...). To access the container (entry), an entry() method is provided instead: llist_for_each(pos, &elems) { pos->entry()->do_something(); } Sponsored-by: On-Waves ehf --- .gitignore | 1 + src/Makefile.am | 3 +- src/cxx_linuxlist.h | 133 ++++++++++++++++++++++++++++++++++++++ tests/Makefile.am | 13 +++- tests/llist/LListTest.cpp | 87 +++++++++++++++++++++++++ tests/llist/LListTest.err | 0 tests/llist/LListTest.ok | 10 +++ tests/ms/MsTest.cpp | 1 - tests/testsuite.at | 7 ++ 9 files changed, 251 insertions(+), 4 deletions(-) create mode 100644 src/cxx_linuxlist.h create mode 100644 tests/llist/LListTest.cpp create mode 100644 tests/llist/LListTest.err create mode 100644 tests/llist/LListTest.ok diff --git a/.gitignore b/.gitignore index 4ae71da..b093692 100644 --- a/.gitignore +++ b/.gitignore @@ -37,6 +37,7 @@ tests/rlcmac/RLCMACTest tests/tbf/TbfTest tests/types/TypesTest tests/ms/MsTest +tests/llist/LListTest tests/emu/pcu_emu tests/testsuite tests/testsuite.log diff --git a/src/Makefile.am b/src/Makefile.am index 8a422f6..0398028 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -95,7 +95,8 @@ noinst_HEADERS = \ rlc.h \ decoding.h \ llc.h \ - pcu_utils.h + pcu_utils.h \ + cxx_linuxlist.h osmo_pcu_SOURCES = pcu_main.cpp diff --git a/src/cxx_linuxlist.h b/src/cxx_linuxlist.h new file mode 100644 index 0000000..d9b28c4 --- /dev/null +++ b/src/cxx_linuxlist.h @@ -0,0 +1,133 @@ +/* cxx_linuxlist.h + * + * Copyright (C) 2015 by Sysmocom s.f.m.c. GmbH + * Author: Jacob Erlbeck + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * 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 General Public License 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#pragma once + +extern "C" { + #include +} + +template +struct LListHead { + typedef T entry_t; + + /* This must match the declaration of struct llist_head */ + LListHead *next; + LListHead *prev; + + LListHead() : m_back(0) { INIT_LLIST_HEAD(this); } + LListHead(T* entry) : m_back(entry) { + next = (LListHead *)LLIST_POISON1; + prev = (LListHead *)LLIST_POISON2; + } + + T *entry() {return m_back;} + const T *entry() const {return m_back;} + + llist_head &llist() { + return *static_cast(static_cast(this)); + } + const llist_head &llist() const { + return *static_cast(static_cast(this)); + } + +private: + T *const m_back; +}; + +/* Define a family of casting functions */ +template +llist_head &llist(LListHead &l) +{ + return l->llist(); +} + +template +const llist_head &llist(const LListHead &l) +{ + return l->llist(); +} + +template +llist_head *llptr(LListHead *l) +{ + return &(l->llist()); +} + +template +const llist_head *llptr(const LListHead *l) +{ + return &(l->llist()); +} + +/* Define type-safe wrapper for the existing linux_list.h functions */ +template +inline void llist_add(LListHead *new_, LListHead *head) +{ + llist_add(llptr(new_), llptr(head)); +} + +template +inline void llist_add_tail(LListHead *new_, LListHead *head) +{ + llist_add_tail(llptr(new_), llptr(head)); +} + +template +inline void llist_del(LListHead *entry) +{ + llist_del(llptr(entry)); +} + +template +inline void llist_del_init(LListHead *entry) +{ + llist_del_init(llptr(entry)); +} + +template +inline void llist_move(LListHead *list, LListHead *head) +{ + llist_move(llptr(list), llptr(head)); +} + +template +inline void llist_move_tail(LListHead *list, LListHead *head) +{ + llist_move_tail(llptr(list), llptr(head)); +} + +template +inline int llist_empty(const LListHead *head) +{ + return llist_empty(llptr(head)); +} + +template +inline void llist_splice(LListHead *list, LListHead *head) +{ + llist_splice(llptr(list), llptr(head)); +} + +template +inline void llist_splice_init(LListHead *list, LListHead *head) +{ + llist_splice_init(llptr(list), llptr(head)); +} diff --git a/tests/Makefile.am b/tests/Makefile.am index 7b2d32d..b822e46 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,6 +1,6 @@ AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGB_CFLAGS) $(LIBOSMOGSM_CFLAGS) -I$(top_srcdir)/src/ -check_PROGRAMS = rlcmac/RLCMACTest alloc/AllocTest tbf/TbfTest types/TypesTest ms/MsTest +check_PROGRAMS = rlcmac/RLCMACTest alloc/AllocTest tbf/TbfTest types/TypesTest ms/MsTest llist/LListTest noinst_PROGRAMS = emu/pcu_emu rlcmac_RLCMACTest_SOURCES = rlcmac/RLCMACTest.cpp @@ -54,6 +54,14 @@ ms_MsTest_LDADD = \ ms_MsTest_LDFLAGS = \ -Wl,-u,bssgp_prim_cb +llist_LListTest_SOURCES = llist/LListTest.cpp +llist_LListTest_LDADD = \ + $(top_builddir)/src/libgprs.la \ + $(LIBOSMOGB_LIBS) \ + $(LIBOSMOGSM_LIBS) \ + $(LIBOSMOCORE_LIBS) \ + $(COMMON_LA) + # The `:;' works around a Bash 3.2 bug when the output is not writeable. $(srcdir)/package.m4: $(top_srcdir)/configure.ac :;{ \ @@ -78,7 +86,8 @@ EXTRA_DIST = \ alloc/AllocTest.ok alloc/AllocTest.err \ tbf/TbfTest.ok tbf/TbfTest.err \ types/TypesTest.ok types/TypesTest.err \ - ms/MsTest.ok ms/MsTest.err + ms/MsTest.ok ms/MsTest.err \ + llist/LListTest.ok llist/LListTest.err DISTCLEANFILES = atconfig diff --git a/tests/llist/LListTest.cpp b/tests/llist/LListTest.cpp new file mode 100644 index 0000000..04f741c --- /dev/null +++ b/tests/llist/LListTest.cpp @@ -0,0 +1,87 @@ +/* + * LListTest.cpp + * + * Copyright (C) 2015 by Sysmocom s.f.m.c. GmbH + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +#include "cxx_linuxlist.h" + +extern "C" { +#include +#include +#include +} + +#include + + +struct TestElem { + const char *str; + LListHead list; + + TestElem(const char *s) : str(s), list(this) {}; +}; + +static void test_linux_list() +{ + LListHead elems, *pos, *tmp; + TestElem elem1("number one"); + TestElem elem2("number two"); + TestElem elem3("number three"); + int count = 0; + + printf("=== start %s ===\n", __func__); + + llist_add_tail(&elem1.list, &elems); + llist_add_tail(&elem2.list, &elems); + llist_add_tail(&elem3.list, &elems); + + llist_for_each(pos, &elems) { + count += 1; + printf(" %i -> %s\n", count, pos->entry()->str); + } + OSMO_ASSERT(count == 3); + + count = 0; + llist_for_each_safe(pos, tmp, &elems) { + count += 1; + if (count == 2) + llist_del(pos); + + printf(" %i -> %s\n", count, pos->entry()->str); + } + OSMO_ASSERT(count == 3); + + count = 0; + llist_for_each(pos, &elems) { + count += 1; + OSMO_ASSERT(pos != &elem2.list); + printf(" %i -> %s\n", count, pos->entry()->str); + } + OSMO_ASSERT(count == 2); + + printf("=== end %s ===\n", __func__); +} + +int main(int argc, char **argv) +{ + test_linux_list(); + + return EXIT_SUCCESS; +} diff --git a/tests/llist/LListTest.err b/tests/llist/LListTest.err new file mode 100644 index 0000000..e69de29 diff --git a/tests/llist/LListTest.ok b/tests/llist/LListTest.ok new file mode 100644 index 0000000..375e9a6 --- /dev/null +++ b/tests/llist/LListTest.ok @@ -0,0 +1,10 @@ +=== start test_linux_list === + 1 -> number one + 2 -> number two + 3 -> number three + 1 -> number one + 2 -> number two + 3 -> number three + 1 -> number one + 2 -> number three +=== end test_linux_list === diff --git a/tests/ms/MsTest.cpp b/tests/ms/MsTest.cpp index 59c92b3..03b1c18 100644 --- a/tests/ms/MsTest.cpp +++ b/tests/ms/MsTest.cpp @@ -231,7 +231,6 @@ static void test_ms_replace_tbf() printf("=== end %s ===\n", __func__); } - static const struct log_info_cat default_categories[] = { {"DPCU", "", "GPRS Packet Control Unit (PCU)", LOGL_INFO, 1}, }; diff --git a/tests/testsuite.at b/tests/testsuite.at index f1f4032..1cadcd2 100644 --- a/tests/testsuite.at +++ b/tests/testsuite.at @@ -36,3 +36,10 @@ cat $abs_srcdir/ms/MsTest.ok > expout cat $abs_srcdir/ms/MsTest.err > experr AT_CHECK([$OSMO_QEMU $abs_top_builddir/tests/ms/MsTest], [0], [expout], [experr]) AT_CLEANUP + +AT_SETUP([llist]) +AT_KEYWORDS([llist]) +cat $abs_srcdir/llist/LListTest.ok > expout +cat $abs_srcdir/llist/LListTest.err > experr +AT_CHECK([$OSMO_QEMU $abs_top_builddir/tests/llist/LListTest], [0], [expout], [experr]) +AT_CLEANUP