C++ circular dependency with intrusive linked list - c++

I've implemented this intrusive linked list:
template <class Entry>
struct LinkedListNode {
Entry *next;
Entry *prev;
};
template <class Entry, LinkedListNode<Entry> Entry::*NodeMember>
class LinkedList {
public:
void init ();
bool isEmpty () const;
Entry * first () const;
Entry * last () const;
Entry * next (Entry *e) const;
Entry * prev (Entry *e) const;
void prepend (Entry *e);
void append (Entry *e);
void insertBefore (Entry *e, Entry *target);
void insertAfter (Entry *e, Entry *target);
void remove (Entry *e);
public:
Entry *m_first;
Entry *m_last;
};
...
template <class Entry, LinkedListNode<Entry> Entry::*NodeMember>
inline Entry * LinkedList<Entry, NodeMember>::next (Entry *e) const
{
return (e->*NodeMember).next;
}
...
It can be used like this:
struct MyEntry {
int value;
LinkedListNode<MyEntry> list_node;
};
LinkedList<MyEntry, &MyEntry::list_node> list;
list.init();
MyEntry entry1, entry2;
entry1.value = 3;
list.append(&entry1);
entry2.value = 5;
list.prepend(&entry2);
It works all right, until you need two objects which contain lists of one another:
struct MyEntry2;
struct MyEntry1 {
int value;
LinkedListNode<MyEntry1> node;
LinkedList<MyEntry2, &MyEntry2::node> list;
};
struct MyEntry2 {
int value;
LinkedListNode<MyEntry2> node;
LinkedList<MyEntry1, &MyEntry1::node> list;
};
Each MyEntry1 holds a list of MyEntry2's, and each MyEntry2 can only appear in the list of one MyEntry1; and the converse. However, this doesn't compile, because the member pointer &MyEntry2::node is taken before MyEntry2 is defined:
prog.cpp:33:27: error: incomplete type 'MyEntry2' used in nested name specifier
prog.cpp:33:41: error: template argument 2 is invalid
There isn't really any practical semantic to this problematic layout, it is only a theoretical problem I've found which may limit the usability of the generic linked list.
Is there any way around this which doesn't make the list considerably more impractical?
EDIT: the layout of all data structures here is completely defined. This is because the data members of LinkedList do not depend on the problematic NodeMember template parameter; only the functions do. The problem seems to be that the language is demanding that &MyEntry2::node be known even though it does not really need to be known at the time.
EDIT: it must be possible to use this generic list to add a structure into two or more lists; this is the purpose of the NodeMember template parameter - it specifies which LinkedListNode within the entry is to be used.

Here is an implementation using inheritance that does not suffer from
your problem.
template <typename Entry>
struct LinkedListNode {
Entry *next;
Entry *prev;
};
template <class Entry>
class LinkedList {
public:
void init ();
bool isEmpty () const;
Entry * first () const;
Entry * last () const;
Entry* next (Entry* e) const {
return e->next;
}
Entry * prev (Entry *e) const;
void prepend (Entry *e);
void append (Entry *e);
void insertBefore (Entry *e, Entry *target);
void insertAfter (Entry *e, Entry *target);
void remove (Entry *e);
public:
LinkedListNode<Entry> *m_first;
LinkedListNode<Entry> *m_last;
};
struct MyEntry2;
struct MyEntry1 : public LinkedListNode<MyEntry1> {
int value;
LinkedList<MyEntry2> list;
};
struct MyEntry2 : public LinkedListNode<MyEntry2> {
int value;
LinkedList<MyEntry1> list;
};
Here is a solution where the LinkedList has a functor as second
template argument. We use an accessor functor with a templated
operator() to remove code duplication and to delay look-up of the
name. Note: The accessor should actually be a member and treated with an
empty base optimization.
template <class Entry>
struct LinkedListNode {
Entry *next;
Entry *prev;
};
template <class Entry, typename Func>
class LinkedList {
public:
void init ();
bool isEmpty () const;
Entry * first () const;
Entry * last () const;
Entry * next (Entry *e) const {
Func f;
return f(e).next();
}
Entry * prev (Entry *e) const;
void prepend (Entry *e);
void append (Entry *e);
void insertBefore (Entry *e, Entry *target);
void insertAfter (Entry *e, Entry *target);
void remove (Entry *e);
public:
Entry *m_first;
Entry *m_last;
};
struct MyEntry2;
struct node_m_access {
template <typename T>
LinkedListNode<T> operator()(T* t) const {
return t->node;
}
};
struct MyEntry1 {
int value;
LinkedListNode<MyEntry1> node;
LinkedList<MyEntry2, node_m_access> list;
};
struct MyEntry2 {
int value;
LinkedListNode<MyEntry2> node;
LinkedList<MyEntry1, node_m_access> list;
};

This problem is equivalent to trying to do:
struct MyEntry2;
struct MyEntry1 {
MyEntry2 a;
};
struct MyEntry2 {
MyEntry1 b;
};
In the above case, the compiler needs to know the size of the MyEntry2 struct when generating MyEntry1. In your case, the compiler needs to know the offset of node in MyEntry2 while generating MyEntry1.
I'm not experienced in template-foo but I would guess that instead of making Entry a class, you want to use a pointer to a class.

Here's a small modification of pmr's accessor solution to reduce the amount of boilerplate. The trick is to first provide an incomplete "struct" declaration of the accessors, instantiate the LinkedList's with these, and later complete the accessors by inheriting from a template accessor class.
template <class Entry>
struct LinkedListNode {
Entry *next;
Entry *prev;
};
template <class Entry, class Accessor>
class LinkedList {
public:
void init ();
bool isEmpty () const;
Entry * first () const;
Entry * last () const;
Entry * next (Entry *e) const {
return Accessor::access(e).next;
}
Entry * prev (Entry *e) const;
void prepend (Entry *e);
void append (Entry *e);
void insertBefore (Entry *e, Entry *target);
void insertAfter (Entry *e, Entry *target);
void remove (Entry *e);
public:
Entry *m_first;
Entry *m_last;
};
template <class Entry, LinkedListNode<Entry> Entry::*NodeMember>
struct LinkedListAccessor {
static LinkedListNode<Entry> & access (Entry *e)
{
return e->*NodeMember;
}
};
struct MyEntry2;
struct Accessor1;
struct Accessor2;
struct MyEntry1 {
int value;
LinkedListNode<MyEntry1> node;
LinkedList<MyEntry2, Accessor2> list;
};
struct MyEntry2 {
int value;
LinkedListNode<MyEntry2> node;
LinkedList<MyEntry1, Accessor1> list;
};
struct Accessor1 : LinkedListAccessor<MyEntry1, &MyEntry1::node> {};
struct Accessor2 : LinkedListAccessor<MyEntry2, &MyEntry2::node> {};
With this, a convenience class can even be made for when there is no problem with the circular dependency:
template <class Entry, LinkedListNode<Entry> Entry::*NodeMember>
class SimpleLinkedList
: public LinkedList<Entry, LinkedListAccessor<Entry, NodeMember> >
{};

Related

BST with function ptr, How to add in extra argument?

is there a way to pass in an extra argument to my function pointer in BST? I am trying to use BST inOrder to get value from a map<string, int>. this BST will be storing the key of the map.
The map will act as a database that uses date + time as the key. each BST will be created to store the date+time of each year and saved into another map (bstMap) which holds all bst. bstMap will use the year as key.
BST inOrder with function ptr.
#ifndef BST_H
#define BST_H
#include<iostream>
using namespace std;
template <class T>
class Node.
{
public:
T m_key;
Node<T> *m_left;
Node<T> *m_right;
};
template <class T>
class BST
{
typedef void(*funcPtr)(T &);
public:
BST();
void Insert(T key);
void Delete();
void InOrder(void(*funcPtr)(T &)) const;
void PreOrder(void(*funcPtr)(T &)) const;
bool Search(T key);
T MaxValue();
bool IsEmpty() const {return m_root == nullptr;}
void DeleteTree();
private:
Node<T> *m_root;
protected:
Node<T> *Insert(Node<T>* node, T key);
Node<T> *Search(Node<T>* node, T key);
void InOrder(Node<T>* node, void (*funcPtr)(T &)) const;
void PreOrder(Node<T>* node, void (*funcPtr)(T &)) const;
void DeleteTree(Node<T>* node);
Node<T>* MaxValue(Node<T>* node);
};
template<class T>
BST<T>::BST(){
m_root = nullptr;
}
template<class T>
void BST<T>::InOrder(Node<T>* node, void(*funcPtr)(T &)) const
{
if (node != nullptr)
{
InOrder(node-> m_left, funcPtr); //recursive call for node left
funcPtr(node-> m_key);
InOrder(node->m_right, funcPtr);
}
}
template<class T>
void BST<T>::InOrder(void(*funcPtr)(T &)){
InOrder(m_root, funcPtr);
}
This line of code is called from main.cpp which pass the user input year into the map to return the bst which stores all relevant keys.
void GetData(string& year, map<string, BST<string>>& bstMap)
{
BST<string> bstKey = bstMap[year];
bstKey.InOrder(&GetTotal);
}
So here is where i am stuck..
void GetTotal(string& key) <- how do i reference my database map here?
{
cout<< key <<endl;
}
If you want to access variables outside of the BST template class (such as the map), then I advise changing your template to the following (assuming that m_root is a member variable of BST<T>, and that it is the root of tree):
template<class T, class Fn>
void BST<T>::InOrder(Fn funcPtr) const
{
InOrder(m_root, fn);
}
template<class T, class Fn>
void BST<T>::InOrder(Node<T>* node, Fn funcPtr) const
{
if (node)
{
InOrder(node-> m_left, funcPtr); //recursive call for node left
funcPtr(node-> m_key);
InOrder(node->m_right, funcPtr);
}
}
Then this way, you can pass a function object or lambda that knows about the map. In the case below, a lambda function is used:
void GetData(string& year, map<string, BST<string>>& bstMap)
{
BST<string> bstKey = bstMap[year];
bstKey.InOrder([&](std::string& key) { std::cout << bstMap[key] << "\n"; });
}
The above provides a lambda that captures the passed-in map parameter.

Why does my compiler make me add the "this->" pointer to access parent variables from a child class [duplicate]

This question already has answers here:
Why do I have to access template base class members through the this pointer?
(3 answers)
Derived template-class access to base-class member-data
(3 answers)
Closed 3 years ago.
I'm coding a simple LinkedList class that is a child class to an abstract List class. After finishing the List class I started to code the LinkedList class, and I noticed that the compiler would not let me access the protected variables from the members of the List class directly, even though LinkedList is a child class of List. When I do not add the "this->" pointer before one of List's variables, I get an error that says the variable is not declared in the scope. I'm sure there is a simple explanation for this, but I can't figure out why the two protected variables in my List class and its members are not accessible from the members of the LinkedList class.
List:
template<typename T>
class List {
protected:
unsigned int numElements;
Node<T>* head;
public:
List();
~List();
virtual void append(T x) = 0;
virtual T remove() = 0;
virtual bool isEmpty();
};
template<typename T>
List<T>::List() {
numElements = 0;
head = NULL;
}
template<typename T>
List<T>::~List() {
while(NULL != head) {
Node<T>* newHead = head->getNext();
delete head;
head = newHead;
}
}
template<typename T>
bool List<T>::isEmpty() {
if (numElements)
return false;
return true;
}
LinkedList with one member example:
template<typename T>
class LinkedList: public List<T> {
public:
void append(T x);
T remove();
void append_tail(T x);
void insert(T x, int pos);
T remove_at(int pos);
void print();
int getNumElements();
};
template<typename T>
void LinkedList<T>::append(T x) {
// create a new node with its next as the head
Node<T>* appendNode = new Node<T>(x, this->head);
// set the head to this new node
this->head = appendNode;
this->numElements++;
}
Node class that is included (not very relevant)
#ifndef NODE_H
#define NODE_H
template<typename T>
class Node {
private:
T value;
Node * next;
public:
Node(T v, Node * n);
T getValue() const;
Node * getNext() const;
void setNext(Node * p);
};
template<typename T>
Node<T>::Node(T v, Node * n) {
value = v;
next = n;
}
template<typename T>
T Node<T>::getValue() const {
return value;
}
template<typename T>
Node<T>* Node<T>::getNext() const {
return next;
}
template<typename T>
void Node<T>::setNext(Node * n) {
this->next = n;
}
#endif

Accessing the left node with an iterator object in a Binary Search Tree

This is a function to find the maximum amount of left nodes. I do realize that there is already a thread for that:
Count number of left nodes in BST
but I don't want pointers in my main file. So I am trying to find a slightly different approach.
bst<int>::binTreeIterator it;
int findMax(bst<int>::binTreeIterator it)
{
int l = 0, r;
if (!(it.leftSide() == NULL)) {
l += 1 + findMax(it.leftSide());
}
if (!(it.rightSide() == NULL)) {
r = findMax(it.rightSide());
}
return l;
}
my problem is with the leftSide()/rightSide() function; How do I implement them so that it returns an iterator object that points to the left side/ right side of the iterator "it" object?
template <class Type>
typename bst<Type>::binTreeIterator bst<Type>::binTreeIterator::leftSide()
{
}
Edit:
template <class Type>
class bst
{
struct binTreeNode
{
binTreeNode * left;
binTreeNode * right;
Type item;
};
public:
class binTreeIterator
{
public:
friend class bst;
binTreeIterator();
binTreeIterator(binTreeNode*);
bool operator==(binTreeNode*);
bool operator==(binTreeIterator);
binTreeIterator rightSide();
binTreeIterator leftSide();
private:
binTreeNode * current;
};
bst();
bst(const bst<Type>&);
const bst& operator=(const bst<Type>&);
~bst();
void insert(const Type&);
void display(); // TEST
binTreeIterator begin();
binTreeIterator end();
private:
binTreeNode * insert(binTreeNode*, const Type&);
void inorder(binTreeNode*);
void destroyTree(binTreeNode*);
void cloneTree(binTreeNode*, binTreeNode*);
binTreeNode * root;
};
This very simple snipped of code should do the trick already:
template <class Type>
typename bst<Type>::binTreeIterator bst<Type>::binTreeIterator::leftSide()
{
return current->left;
}
As you did not declare the iterator's constructor explicit, it will get called automatically from the pointer to left/right returned.

"Use of class template requires template argument list" and multiple classes

I have a header file for a program that utilizes singly linked list. The data that is originally stored in the nodes were integers, however, in an attempt to use the template class, I tried to convert the Node class and AnyList class to template classes; however, when I compile, an error message that says "'Node": use of class template requires template argument list" appears. I've seen examples of template classes, but there's a bit of confusion since I'm trying to make two template classes in one header file.
#ifndef ANYLIST_H
#define ANYLIST_H
#include<iostream>
#include <string>
using namespace std;
template <typename T>
class Node
{
public:
Node() : data(0), next(NULL) {}
Node(T& theData, Node *newNext) : data(theData), next(newNext){}
Node* getNext() const { return next; }
T getData( ) const { return data; }
void setData(T& theData) { data = theData; }
void setNext(Node *newNext) { next = newNext; }
~Node(){}
private:
T data;
Node *next; //pointer that points to next node
};
template <typename T>
class AnyList
{
friend ostream& operator<<(ostream& out, const AnyList<T>& theList);
public:
AnyList();
void insert(const T& elem);
int getNumOfElem() const;
void destroyList();
~AnyList();
private:
Node *first;
int count;
};
#endif
Simply put, Node<> and List<> are different templates. So you need to forward the template parameter from List to Node.
Replace
Node *first;
By
Node<T> *first;

Working with templates and error in C++

I am trying to implement a red black tree with the use of templates. For example, when inserting an item to the tree, the key and the item should both be generic types. Till now, I implemented a header file which consists of a struct and functions to be implemented. However, I don't know if I'm using templates the right way. Also, when I tried to implement the 'Insert' function, the IDE gives the error:
prototype for ‘void RedBlackTree::InsertKey(Item*&, Key*&)’ does not match any in class ‘RedBlackTree’ RedBlackTree.h
This is my header file:
#ifndef REDBLACKTREE_H_
#define REDBLACKTREE_H_
template <class Item, class Key>
class RedBlackTree
{
typedef enum
{
BLACK,
RED
}ColourNode;
typedef struct RBT
{
struct RBT *left;
struct RBT *right;
struct RBT *parent;
struct RBT *root;
ColourNode colour;
Item item;
Key key;
}RBTNode;
public:
~RedBlackTree(); // destructor
RedBlackTree(Item, Key); // default constructor
void InsertKey(Item, Key);
int InsertFixUp(Item, Key);
int RemoveKey(Item, Key);
int FindKey(Item, Key);
private:
RedBlackTree<Item, Key> *rootPointer;
RedBlackTree<Item, Key> *NILL_LEAF;
};
template <class Item, class Key>
void RedBlackTree<Item, Key>::InsertKey(Item *&T, Key *&z)
{
//node* nil=tree->nil;
//node* root=tree->root;
RBTNode *y;
RBTNode *x;
y=T->nil;
x=T->root;
while(x != T->nil)
{
y=x;
if((z->key)<(x->key))
x=x->left;
else
x=x->right;
}
y=z->parent;
if(y == T->nil)
z=T->root;
else
if((z->key)<(y->key))
z=y->left;
else
z=y->right;
z->left=T->nil;
z->right=T->nil;
z->colour=RED;
InsertFixUp(T,z);
}
#endif /* REDBLACKTREE_H_ */
Thanks in advance.
The problem is that the types of the arguments to InsertKey don't match the declaration. In the declaration the arguments are Item and Key, and in the implementation they are Item*& and Key*& (references to pointers). These need to match.
void InsertKey(Item, Key);
^^^^ ^^^
void RedBlackTree<Item, Key>::InsertKey(Item *&T, Key *&z)
^^^^^^^ ^^^^^^
You have to move the implementation of the function (the template) to the class definition.
template <class Item, class Key>
class RedBlackTree
{
//...
public:
~RedBlackTree(); // destructor
RedBlackTree(Item, Key); // default constructor
void InsertKey(Item *&T, Key *&z)
{
//...
}
//...
};