How to implement try_pop() method in queue in c++? - c++

Below is queue example in "C++ concurrency in action 2nd".
This example is single threaded queue, which will be modified to explain thread safe.
template<typename T>
class queue {
private:
struct node {
T data;
std::unique_ptr<node> next;
node(T data_) : data(std::move(data_)) {}
};
std::unique_ptr<node> head;
node* tail;
public:
queue() : tail(nullptr) {};
queue(const queue& other) = delete;
queue& operator=(const queue& other) = delete;
std::shared_ptr<T> try_pop() {
if (!head) return std::shared_ptr<T>();
std::shared_ptr<T> const res(std::make_shared<T>(std::move(head->data)));
std::unique_ptr<node> const old_head = std::move(head);
head = std::move(old_head->next);
if(!head) tail = nullptr;
return res;
}
void push(T new_value) {
std::unique_ptr<node> p(new node(std::move(new_value)));
node* const new_tail = p.get();
if(tail) {
tail->next = std::move(p);
} else {
head = std::move(p);
}
tail = new_tail;
}
};
I can understand this queue works fine, but the way try_pop() is implemented seems to be weird.
Is it better way to do like below ?
std::shared_ptr<T> try_pop() {
if (!head) return std::shared_ptr<T>();
std::shared_ptr<T> const res(std::make_shared<T>(std::move(head->data)));
// std::unique_ptr<node> const old_head = std::move(head);
// head = std::move(old_head->next);
head = std::move(head->next);
if(!head) tail = nullptr;
return res;
}
Why head should be copied to old_head on this?
Is there any reason I missed?

Related

Why assignment operator working even when it was forbidden?

I'm struggling with OOP again. I tried to implement signgly linked list, here's the code:
template <typename T>
class node
{
T value;
node* next;
public:
node(const T& n)
{
this->value = n;
this->next = nullptr;
}
~node()
{
//is it ok to leave destructor empty in my case?
}
};
template <typename T>
class list
{
node<T>* head;
node<T>* tail;
public:
list()
{
this->head = nullptr;
this->tail = nullptr;
}
list(const list& arr)
{
*this = list(); //<================== the line I mentioned
node* current_this = this->head;
node* current_arr = this->arr;
while (current_arr != nullptr)
{
current_this = new node(current_arr);
current_arr = current_arr->next;
current_this = current_this->next;
}
}
list(list&& arr)
{
this->head = arr.head;
this->tail = arr.tail;
arr = list(); //<================== the line I mentioned
}
~list()
{
node* current = this->head;
while (current != nullptr)
{
node* next = current->next;
delete current;
current = next;
}
}
};
I wrote some lines without thinking, i.e. arr = list() and *this = list(). I didn't implemented operator= so there should be some troubles. I thought compiler has decided to generate copy/move assigment for me, so I decided to add these lines, just for curiosity sake:
list& operator=(const list& arr) = delete;
list& operator=(list&& arr) = delete;
But it still compiled and I don't understand why. What's the deal here? Shoudn't assignment be forbidden and therefore arr = list() and *this = list() be illegal?
Templates aren't evaluated unless/until you instantiate them. If you don't use the copy or move constructor, then their bugs don't produce errors.

Doubly linked list. Trying to implement copy constructor from source into destination. Assuming destination's length is zero (C++)

struct Node {
int data;
Node* next;
Node* prev;
Node() : data(0), next(nullptr), prev(nullptr) { }
explicit Node(int elt) : data(elt), next(nullptr), prev(nullptr) { }
};
class DequeEmptyException { };
class Deque {
public:
Deque();
~Deque();
Deque(const Deque& orig);
Deque& operator=(const Deque& rhs);
private:
Node* head;
Node* tail;
int length;
void copy(const Deque& source, Deque& destination);
};
here are is my copy constructor from source to destination.
void Deque::copy(const Deque& source, Deque& destination) {
// create dummy header
destination.head = new Node;
destination.head->next = destination.head->prev = destination.head;
// copy nodes
for (Node *p = source.head->next; p != source.head; p = p->next) {
Node *t = new Node;
t->data = p->data;
// insert at end of list
t->next = destination.head;
t->prev = destination.head->prev;
t->prev->next = t;
t->next->prev = t;
}
source.length = destination.length;
}
This is my attempt. Does it look correct as there are many other implementations within the Deque that I was unable to show. This was the most confusing.

How to implement Front() method to return the first element of a templated doubly linked list C++?

Whenever I implement the front() method (to return the first element of the doubly linked list) in the main I get a segmentation fault even though the back() method (returning the info of tail) that was implemented in a similar manner works. Can someone help?
template <class T>
class Node {
public:
T info;
Node<T>* next;
Node<T>* prev;
Node(const T info){
this->info = info;
next = NULL;
prev = NULL;
}
Node* getNode(T info){
Node* newnode = (Node *)malloc(sizeof(Node));
}
};
template <class T>
class DLlist {
private:
Node<T>* head;
Node<T>* tail;
int size;
public:
DLlist();
T front();
T back();
};
template<class T>
DLlist<T>::DLlist(){
head = NULL;
tail = NULL;
size = 0;
}
template <class T>
void DLlist<T>::addback(const T newdata){
if (isEmpty()){
Node<T> *newnode = new Node<T>(newdata);
head = tail = newnode;
size++;
return;
}
Node<T> *newnode;
newnode = new Node<T>(newdata);
newnode->prev = tail;
newnode->next = NULL;
tail->prev = newnode;
tail = newnode;
size++;
}
template <class T>
T DLlist<T>::front(){
return (head->info);
}
In your addback() function:
Node<T> *newnode; // Two lines where
newnode = new Node<T>(newdata); // one suffices
newnode->prev = tail;
newnode->next = NULL; // Unnecessary, your constructor did this
tail->prev = newnode; // THIS IS YOUR PROBLEM
tail = newnode;
size++;
Your tail should be setting its next pointer to the new node, not its previous. Drawing this stuff out on a piece of paper can go a long way in better understanding how it should work.
I am always willing to chalk up poor formatting on this site to copy/paste, but there are other things you can do to simplify your code, make it a bit more modern, etc.
So here's your code again, cleaned up a tad (This code went through clang-format using the Webkit style):
#include <iostream>
template <class T>
struct Node {
T info;
Node<T>* next = nullptr;
Node<T>* prev = nullptr;
Node(const T& info)
: info(info)
{
}
// Don't know why you need this, so just deleting it because it's a bad
// function
};
template <class T>
class DLlist {
private:
Node<T>* head = nullptr;
Node<T>* tail = nullptr;
int size = 0;
public:
DLlist() = default;
bool isEmpty() { return head == nullptr && tail == nullptr; }
void push_back(const T& newdata);
const T front() const;
const T back() const;
};
template <class T>
void DLlist<T>::push_back(const T& newdata)
{
if (isEmpty()) {
head = new Node<T>(newdata);
tail = head;
size++;
return;
}
Node<T>* newnode = new Node<T>(newdata);
newnode->prev = tail;
tail->next = newnode; // THIS WAS PROBABLY YOUR ISSUE
tail = newnode;
size++;
}
template <class T>
const T DLlist<T>::front() const
{
return head->info;
}
template <class T>
const T DLlist<T>::back() const
{
return tail->info;
}
int main()
{
DLlist<int> list;
list.push_back(42);
list.push_back(54);
std::cout << list.front() << '\n'; // 42 prints just fine, Debian w/ LLVM 9
}

Destructing a linked list

I was trying to implement a linked list for solving an algorithm problem.
It basically worked, however, it turned out that I was using too much memory.
I would appreciate if someone point out defects of following destructor design.
template<typename T>
struct Node {
Node(): item(0),next(0) {}
Node(T x): item(x),next(0) {}
T item;
Node* next;
};
template <typename T>
struct List {
List() : head(0),tail(0) {}
Node<T>* head;
Node<T>* tail;
void insert(T x) {
Node<T>* newNode = new Node<T>(x);
if(head == NULL) {
head = tail = newNode;
} else {
tail->next = newNode;
tail = tail->next;
}
}
void clearRecur(Node<T>* h) {
if(h) {
clearRecur(h->next);
delete h;
}
}
void clear() {
if(head) {
clearRecur(head);
}
}
};
A list can be cleared recursively or iteratively.
Alternatively to your (IMHO correct) version, I use a slight different approach – make the Node itself "responsible" to delete its tail. This leads to recursive clearing as well (but with less code).
Recursive clearing:
template<typename T>
struct Node {
Node(): item(), next(nullptr) {}
Node(T x): item(x), next(nullptr) {}
~Node() { delete next; } // <== recursive clearing
T item;
Node* next;
// Using the default copy ctor would corrupt the memory management.
// Deleting it lets the compiler check for accidental usage.
Node(const Node&) = delete;
// Deleting assignment operator as well.
Node& operator=(const Node&) = delete;
};
template <typename T>
struct List {
List() : head(nullptr), tail(nullptr) {}
~List() { clear(); }
Node<T>* head, tail;
void insert(T x) {
Node<T>* newNode = new Node<T>(x);
if (head == nullptr) head = tail = newNode;
else {
tail->next = newNode;
tail = tail->next;
}
}
void clear() {
delete head;
head = tail = nullptr;
}
// Using the default copy ctor would corrupt the memory management.
// Deleting it lets the compiler check for accidental usage.
List(const List&) = delete;
// Delete assignment operator as well.
List& operator=(const List&) = delete;
};
This is the way, I did it in our current project. At the first glance, it seemed enjoying simple and worked fine. I changed my mind when our beta-testers came into play. In real world projects, the lists were such long that the recursive clearing ran out of stack memory. (Yepp – a stack overflow.) I should've known better!
Thus, I made the clearing iteratively – whereby the "responsibility" is moved back from Node to List. (The API user will not note this as it happens "under the hood".)
Iterative clearing:
template<typename T>
struct Node {
Node(): item(), next(nullptr) {}
Node(T x): item(x), next(nullptr) {}
T item;
Node* next;
};
template <typename T>
struct List {
List() : head(nullptr), tail(nullptr) {}
~List() { clear(); }
Node<T>* head, tail;
void insert(T x) {
Node<T>* newNode = new Node<T>(x);
if (head == nullptr) head = tail = newNode;
else {
tail->next = newNode;
tail = tail->next;
}
}
void clear() {
while (head) {
Node<T> *p = head; head = head->next;
delete p;
}
tail = nullptr;
}
// Using the default copy ctor would corrupt the memory management.
// Deleting it lets the compiler check for accidental usage.
List(const List&) = delete;
// Delete assignment operator as well.
List& operator=(const List&) = delete;
};

c++ compilation error with listing_6_6.cpp from book C++ Concurrency In Action

I'm trying to compile the example listing_6.6.cpp from the book "C++ Concurrency In Action" of Anthony Williams, but it's not compiling and I don't understand why.
I'm new to multithreading, but I've checked the code and googled for an answer, but I still don't understand why it doesn't compile.
The problem is in this method:
std::unique_ptr<node> pop_head()
{
std::lock_guard<std::mutex> head_lock(head_mutex);
if (head.get() == get_tail())
{
return nullptr;
}
std::unique_ptr<node> const old_head = std::move(head);
head = std::move(old_head->next);
return old_head; // <-- here is the problem
}
the error message is: attempting to reference a deleted function
The full error message is (I'm using VS 2013):
threadsafe_queue.h(34): error C2280: 'std::unique_ptr<threadsafe_queue<Message>::node,std::default_delete<_Ty>>::unique_ptr(const std::unique_ptr<_Ty,std::default_delete<_Ty>> &)' : attempting to reference a deleted function
and the full code is:
#include <memory>
#include <mutex>
template<typename T>
class threadsafe_queue
{
private:
struct node
{
std::shared_ptr<T> data;
std::unique_ptr<node> next;
};
std::mutex head_mutex;
std::unique_ptr<node> head;
std::mutex tail_mutex;
node* tail;
node* get_tail()
{
std::lock_guard<std::mutex> tail_lock(tail_mutex);
return tail;
}
std::unique_ptr<node> pop_head()
{
std::lock_guard<std::mutex> head_lock(head_mutex);
if (head.get() == get_tail())
{
return nullptr;
}
std::unique_ptr<node> const old_head = std::move(head);
head = std::move(old_head->next);
return old_head;
}
public:
threadsafe_queue() :
head(new node), tail(head.get())
{}
threadsafe_queue(const threadsafe_queue& other) = delete;
threadsafe_queue& operator=(const threadsafe_queue& other) = delete;
std::shared_ptr<T> try_pop()
{
std::unique_ptr<node> old_head = pop_head();
return old_head ? old_head->data : std::shared_ptr<T>();
}
void push(T new_value)
{
std::shared_ptr<T> new_data(
std::make_shared<T>(std::move(new_value)));
std::unique_ptr<node> p(new node);
node* const new_tail = p.get();
std::lock_guard<std::mutex> tail_lock(tail_mutex);
tail->data = new_data;
tail->next = std::move(p);
tail = new_tail;
}
};
Thansk!
TLDR: You can't move from a const object, even when returning.
The constructor selected to initialize the return value of pop_head from a const unique_ptr is the deleted copy constructor, so the compiler is correct to reject this program as ill-formed. If you look at Listing 6.6 in the book itself (page 161-162) you'll notice that the offending const is not present.