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
This commit is contained in:
Jacob Erlbeck 2015-05-11 14:13:47 +02:00
parent 67c385046d
commit dfef28de88
9 changed files with 251 additions and 4 deletions

1
.gitignore vendored
View File

@ -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

View File

@ -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

133
src/cxx_linuxlist.h Normal file
View File

@ -0,0 +1,133 @@
/* cxx_linuxlist.h
*
* Copyright (C) 2015 by Sysmocom s.f.m.c. GmbH
* Author: Jacob Erlbeck <jerlbeck@sysmocom.de>
*
* 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 <osmocom/core/linuxlist.h>
}
template <typename T>
struct LListHead {
typedef T entry_t;
/* This must match the declaration of struct llist_head */
LListHead<T> *next;
LListHead<T> *prev;
LListHead() : m_back(0) { INIT_LLIST_HEAD(this); }
LListHead(T* entry) : m_back(entry) {
next = (LListHead<T> *)LLIST_POISON1;
prev = (LListHead<T> *)LLIST_POISON2;
}
T *entry() {return m_back;}
const T *entry() const {return m_back;}
llist_head &llist() {
return *static_cast<llist_head *>(static_cast<void *>(this));
}
const llist_head &llist() const {
return *static_cast<llist_head *>(static_cast<void *>(this));
}
private:
T *const m_back;
};
/* Define a family of casting functions */
template <typename T>
llist_head &llist(LListHead<T> &l)
{
return l->llist();
}
template <typename T>
const llist_head &llist(const LListHead<T> &l)
{
return l->llist();
}
template <typename T>
llist_head *llptr(LListHead<T> *l)
{
return &(l->llist());
}
template <typename T>
const llist_head *llptr(const LListHead<T> *l)
{
return &(l->llist());
}
/* Define type-safe wrapper for the existing linux_list.h functions */
template <typename T>
inline void llist_add(LListHead<T> *new_, LListHead<T> *head)
{
llist_add(llptr(new_), llptr(head));
}
template <typename T>
inline void llist_add_tail(LListHead<T> *new_, LListHead<T> *head)
{
llist_add_tail(llptr(new_), llptr(head));
}
template <typename T>
inline void llist_del(LListHead<T> *entry)
{
llist_del(llptr(entry));
}
template <typename T>
inline void llist_del_init(LListHead<T> *entry)
{
llist_del_init(llptr(entry));
}
template <typename T>
inline void llist_move(LListHead<T> *list, LListHead<T> *head)
{
llist_move(llptr(list), llptr(head));
}
template <typename T>
inline void llist_move_tail(LListHead<T> *list, LListHead<T> *head)
{
llist_move_tail(llptr(list), llptr(head));
}
template <typename T>
inline int llist_empty(const LListHead<T> *head)
{
return llist_empty(llptr(head));
}
template <typename T>
inline void llist_splice(LListHead<T> *list, LListHead<T> *head)
{
llist_splice(llptr(list), llptr(head));
}
template <typename T>
inline void llist_splice_init(LListHead<T> *list, LListHead<T> *head)
{
llist_splice_init(llptr(list), llptr(head));
}

View File

@ -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

87
tests/llist/LListTest.cpp Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
*
*/
#include "cxx_linuxlist.h"
extern "C" {
#include <osmocom/core/application.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/utils.h>
}
#include <errno.h>
struct TestElem {
const char *str;
LListHead<TestElem> list;
TestElem(const char *s) : str(s), list(this) {};
};
static void test_linux_list()
{
LListHead<TestElem> 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;
}

View File

10
tests/llist/LListTest.ok Normal file
View File

@ -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 ===

View File

@ -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},
};

View File

@ -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