I'm getting a segfault when executing this code. Specifically after g_lru_stack.add_node(&lru_node) is called.
Running under GDB shows that the dummy node doesn't look like it has been initialised. Is this to do with how extern globals are initialised? If so any help would be greatly appreciated here.
I have included snippets from both the header and cpp file.
Specifically, my question is this:
How can I get LRU_Stack g_lru_stack , declared at the top of object.cc, to call the LRU_Node ctor that takes no arguments.
It seems this ctor never gets called, hence why my dummy node isn't initialised.
Object.h
class obj_payload;
extern cas_mutex g_lru_stack_mutex;
class LRU_Node {
private:
obj_payload* payload;
LRU_Node* up;
LRU_Node* down;
size_t predicted_bytes_in_cache;
public:
LRU_Node() : payload(nullptr), up(this), down(this), predicted_bytes_in_cache(1337) {} // Dummy ctor
LRU_Node(obj_payload* p) : payload(p), up(nullptr), down(nullptr), predicted_bytes_in_cache(88) {} // Normal Creation of node
//Adds a node to the top of the stack
//Has dummy context
void add_to_stack(LRU_Node* newNode);
//Sets how many bytes of the object are predicted to be in the cache
//Has dummy context
void is_node_in_cache(LRU_Node* node);
//Moves a node to the top of the stack
//Has dummy context
void move_node_to_top(LRU_Node* node);
//Has context of caller
size_t get_predicted_bytes_in_cache();
};
class LRU_Stack {
LRU_Node dummy;
public:
void add_node(LRU_Node* node);
void move_node_to_top(LRU_Node* node);
};
extern LRU_Stack g_lru_stack;
class obj_payload {
typedef uint32_t ctr_t;
private:
ctr_t refcnt;
const uint32_t sz; // size of the data space in bytes
LRU_Node lru_node; // Jordan -- This arg objects node for the LRU_Stack
obj_payload( typeinfo tinfo_,
uint32_t size_,
int refcnt_init=1 )
: refcnt( refcnt_init ),
sz( size_ ),
tinfo( tinfo_ ), lru_node(this) {
g_lru_stack.add_node(&lru_node);
}
Object.cc
#include "object.h"
namespace obj {
//Jordan -- Global LRU_Node Stack
cas_mutex g_lru_stack_mutex;
LRU_Stack g_lru_stack;
//Adds a node to the top of the stack
//Has dummy context
void LRU_Node::add_to_stack(LRU_Node* newNode) {
newNode->down = down; // Set the new nodes previous -> dummys previous
newNode->up = this; // Set new nodes next -> dummy
down->up = newNode; // Dummy next -> new node (i.e. Previous top of stack node up -> newNode)
down = newNode; // Dummy previous -> new node (i.e. Dummy down pointer now links back round to the new node at the top)
}
//Sets how many bytes of the object are predicted to be in the cache
//Has dummy context
void LRU_Node::is_node_in_cache(LRU_Node* node) {
size_t total = 0;
LRU_Node* orignal = node;
while (node != this) {
total += node->payload->get_size(); // Add current size to total
node = node->up; // Go to next node
}
node = orignal; //Reset node to the passed in node, then set how many bytes it has contained within cache
if (total <= cache_size) {
node->predicted_bytes_in_cache = node->payload->get_size();
}
else {
node->predicted_bytes_in_cache = (node->payload->get_size()) - (total - cache_size) < node->payload->get_size() ? (node->payload->get_size()) - (total - cache_size) : 0;
}
}
//Moves a node to the top of the stack
//Has dummy context
void LRU_Node::move_node_to_top(LRU_Node* node) {
if (down != node) { // Check that the node to move is not already top of stack
node->down->up = node->up;
node->up->down = node->down;
if (down == node->up) { // If the node is seccond top of stack
node->up->up = node;
}
node->down = down;
node->up = this;
down->up = node;
down = node;
}
}
//Has context of caller
size_t LRU_Node::get_predicted_bytes_in_cache() {
return predicted_bytes_in_cache;
}
//Has dummy context
bool LRU_Node::is_empty() {
return (up == this);
}
void LRU_Stack::add_node(LRU_Node* node) {
g_lru_stack_mutex.lock();
dummy.add_to_stack(node);
g_lru_stack_mutex.unlock();
}
void LRU_Stack::move_node_to_top(LRU_Node* node) {
g_lru_stack_mutex.lock();
dummy.is_node_in_cache(node);
dummy.move_node_to_top(node);
g_lru_stack_mutex.unlock();
}
"extern globals" are not objects (unless they include an initializer): They are forward-declarations.
Global objects are initialized in two phases:
All those with compile-time constant initializers are done, the rest is zeroed.
The run-time initializers are run in order of object definition. (No ordering guarantees for objects in different compilation units!).
Seems like UB got you there.
To solve the error, do one of these:
Put the definition of the object before its first use in the same compilation unit (The most efficient method).
Put the object as a static in an accessor function. Init will be on first use (thread-safe!).
Type& getTypeSingleton() {
static Type x/*optional initializer*/;
return x;
}
(Implementation dependent) The compilation unit first mentioned on the command-line will be initialized first in all current implementations (As effifient as the first, but fragile).
Related
I need to overload the ostream operator with new functionality for a doubly linked Skip List class.
When I cout the instance of my class, I want it to iterate through my the levels of my skip list, and wherever the head pointer is pointed to a nullptr I want it to print the level name and a status of empty.
Would look something like:
After adding 7
Level: 4 -- empty
Level: 3 -- empty
Level: 2 -- empty
Level: 1 -- empty
Level: 0 -- 7
I need the number of levels to be dynamically entered. I try to assign int level = SkipList::maxLevels_; but I get the error invalid use of non-static data member
I've made the ostream a friend. How can I instruct it access the maxLevels_ data member?
SkipList.h
#include <stdio.h>
#include <iostream>
#ifndef SKIP_LIST_
#define SKIP_LIST_
using namespace std;
class SkipList
{
private:
struct SkipListNode {
// Convenience constructor to create node, set its data, and set all pointers to nullptr
explicit SkipListNode(int data){
data_ = data;
next_ = NULL;
prev_ = NULL;
upLevel_ = NULL;
downLevel_ = NULL;
}
// data for SNode
int data_;
// link to next at same level
SkipListNode* next_;
// link to previous at same level
SkipListNode* prev_;
// link up one level
SkipListNode* upLevel_;
// link down one level
SkipListNode* downLevel_;
};
// maximum # of levels of SkipList, levels are 0 to maxLevels-1
int maxLevels_;
// array of maxLevels_ SkipListNode pointers as head pointers. For example,
// if maxLevels_ == 2, we'd have Heads[0] and Heads[1]. Dynamically allocated
// by constructor.
SkipListNode** heads_;
// array of maxLevels_ SkipListNode pointers as tail pointers.
SkipListNode** tails_;
// given a pointer to a SkipListNode, place it before the given nextNode
void addBefore(SkipListNode* newNode, SkipListNode* nextNode, int level);
// return true 50% of time,
// each node has a 50% chance of being at higher level
bool alsoHigher() const;
public:
//Constructor
SkipList(){maxLevels_ = 1;}
SkipList(int maxLevels);
//Destructor
// virtual ~SkipList();
// return true if successfully added, no duplicates
bool insert(int item);
// item deletion; return true if successfully removed
bool erase(int item);
// return true if found in SkipList
bool contains(int item) const;
friend ostream& operator<<(ostream& os, const SkipList& list){
int level = SkipList::maxLevels_;
while (level >= 0) {
SkipListNode* temp = list.heads_[level];
if (temp == nullptr) {
os << "Level: " << level << "-- empty";
}
else {
while (temp) {
os << temp->data_ << " ";
temp = temp->next_;
}
}
os << endl;
level--;
}
}
};
#endif
SkipList::maxLevels_; refers to the static maxLevels_ member of the SkipList class.
So, if you need maxLevels_ to be the maximum level of all the instances of SkipList you have to declare it as static.
Otherwise in your overloaded friend function you have to use the private member of the list instance.
friend ostream& operator<<(ostream& os, const SkipList& list){
int level = list.maxLevels_;
...
I'm having trouble figuring out the destructor for my hashTable class, the destructor is like this:
template <typename ElementType>
HashSet<ElementType>::~HashSet() noexcept
{
for (unsigned int i=0;i<hashCapacity;i++)
{
Node* current = hashTable[i];
while(current != nullptr)
{
Node* entry = current;
current = current->next;
delete[] entry;
}
}
delete[] hashTable;
}
No matter I use either delete[] or delete, it gives me either double-free errors or segmentation fault.
The class template is below:
template <typename ElementType>
class HashSet : public Set<ElementType>
{
public:
// The default capacity of the HashSet before anything has been
// added to it.
static constexpr unsigned int DEFAULT_CAPACITY = 10;
// A HashFunction is a function that takes a reference to a const
// ElementType and returns an unsigned int.
using HashFunction = std::function<unsigned int(const ElementType&)>;
public:
// Initializes a HashSet to be empty so that it will use the given
// hash function whenever it needs to hash an element.
explicit HashSet(HashFunction hashFunction);
// Cleans up the HashSet so that it leaks no memory.
~HashSet() noexcept override;
// add() adds an element to the set. If the element is already in the set,
// this function has no effect. This function triggers a resizing of the
// array when the ratio of size to capacity would exceed 0.8, in which case
// the new capacity should be determined by this formula:
//
// capacity * 2 + 1
//
// In the case where the array is resized, this function runs in linear
// time (with respect to the number of elements, assuming a good hash
// function); otherwise, it runs in constant time (again, assuming a good
// hash function). The amortized running time is also constant.
void add(const ElementType& element) override;
Where my add function and default constructor implementation is like this:
template <typename ElementType>
HashSet<ElementType>::HashSet(HashFunction hashFunction)
: hashFunction{hashFunction}
{
hashCapacity = DEFAULT_CAPACITY;
hashSize = 0;
hashTable = new Node* [hashCapacity];
for (int i=0;i<hashCapacity;++i)
{
hashTable[i] = nullptr;
}
}
template <typename ElementType>
void HashSet<ElementType>::add(const ElementType& element)
{
if (contains(element)==false)
{
if ((hashSize/hashCapacity) > 0.8)
{
}
else
{
unsigned int index = hashFunction(element) % hashCapacity;
hashSize += 1;
Node* add = new Node;
add->next = nullptr;
add->value = element;
if (hashTable[index]==nullptr)
{
hashTable[index] = add;
}
else
{
Node* addNode = hashTable[index];
while(addNode->next != nullptr)
{
addNode = addNode->next;
}
addNode->next = add;
}
}
}
}
Note: that resize hashtable part is incomplete because I'm examining the functionality for my hash table to hold a small amount of value first.
When declaring a pointer variable, is there ever a use for more than one asterisk? I know when you want to have a pointer point to another you would use multiple, but just to clarify, when declaring you still only put one?
Weard things: an example of 3 asterisks:
const char **stringArray;
now, if you want to allocate this array in a function as an argument, you need the following:
void stringAllocator(const char ***stringArray, int size) {
*stringArray = (const char **) malloc(size);
}
...
stringAllocator (&stringArry, 20);
...
So, you can imagine more than 3 as well, though i had never saw more than 4 :-)
an a bit weirder stuff in c++ using stars in combination with &
void stringAllocator(const char **&stringArray, int size) {
stringArray = (const char **) malloc(size);
}
...
stringAllocator (stringArry, 20);
...
In the above case works as a star reduction technique. It does the same as the first examle
When declaring a pointer variable, is there ever a use for more than one asterisk?
Sure, there are uses for declarations of pointer to pointer variables.
Let's suppose you have a function that does allocate some class instance internally, but needs to indicate failure at the same time. You can give it a signature like
bool CreateSomeType(SomeType** pointerToSomeTypePointer) {
try {
*pointerToSomeTypePointer = new SomeType();
}
catch(...) {
return false;
}
return true;
}
and being called like this
SomeType* ptr = nullptr;
if(CreateSomeType(&ptr)) {
// Use ptr
// ...
delete ptr;
}
else {
// Log failure
}
A common use case of two stars is when a function has to alter a pointer value, e.g. in ADT implementations like "Stack".
Though this design is more C than C++ (in C++ you can use other mechanisms like references), see the following example. I wrote it in "C"-style (even if you mentioned C++):
struct node {
int x;
struct node* next;
};
// this one works:
void insertBeforeHead(node** head, int value) {
struct node* newNode = (struct node*)malloc(sizeof(struct node));
newNode->next = *head;
newNode->x = value;
*head = newNode; // alters the pointer value of the caller
}
// this one does not work:
void insertBeforeHead2(node* head, int value) {
struct node* newNode = (struct node*)malloc(new Node);
newNode->next = head;
newNode->x = value;
head = newNode; // alters only the local copy of the pointer value
}
int main () {
struct node* mainHead = NULL;
insertBeforeHead(&mainHead,10); // changes the value of mainHead
insertBeforeHead2(mainHead,20); // does not change the value of mainHead, althouth it should.
}
I am currently using VS2015 for this.
I am trying to create a binary search tree in c++ so that I can learn both the language and the data structure while trying to see if I can follow good practices. However, I am coming through a problem where I am not properly instantiating the object properly in the driver file.
BSTHeader.h
#pragma once
/*
Properties of Binary Search Tree:
1.) Elements less than root will go to the left child of root
2.) Elements greater than root will go to the right child of root
*/
#include <memory>
// Binary Search Tree handler class
class BSTHeader {
/*
Naive implementation of BSTNode (non-generic version)
Nested class is private, but it's internal fields and member functions
are public to outer class: BSTHeader
*/
class BSTNode {
public:
int data;
std::unique_ptr<BSTNode> left;
std::unique_ptr<BSTNode> right;
BSTNode(int val) {
data = val;
left = NULL;
right = NULL;
}
~BSTNode() {}
};
std::unique_ptr<BSTNode> root; // Root of BST
unsigned int size; // Total amount of nodes in tree from root
public:
BSTHeader();
BSTHeader(int val);
~BSTHeader();
bool insert(std::unique_ptr<BSTNode>& root, int val);
}
BSTHeader.cpp
#include "BSTHeader.h"
/*
Constructors:
*/
BSTHeader::BSTHeader() {
root = NULL;
size = 0;
}
BSTHeader::BSTHeader(int val) {
root = std::unique_ptr<BSTNode>(new BSTHeader::BSTNode(val)); // Smart pointer to an internal BSTNode
size = 1;
}
BSTHeader::~BSTHeader() {} // Empty destructor from use of smart pointer
/*
Member functions:
*/
bool BSTHeader::insert(std::unique_ptr<BSTNode>& root, int val) {
if (root == NULL) { // Place new element here
root = std::unique_ptr<BSTNode>(new BSTHeader::BSTNode(val));
size++;
return true;
}
if (val < root.get()->data) { // val < root
insert(root.get()->left, val);
}
else if (val > root.get()->data) { // val > root
insert(root.get()->right, val);
}
The issue I get is here, where I believe I am trying to instantiate a BSTHeader object.
Program.cpp
#include "BSTHeader.h"
int main()
{
BSTHeader::BSTHeader bst(); // <----- ERROR
return 0;
}
The error I am getting is cannot determine which instance of overloaded function "BSTHeader:BSTHeader" is intended
However, whenever I do:
BSTHeader bst()
I am not able to access the insert(..., ...) function for the object doing bst.insert(..., ...) due to expression must have class type even though the error above does not appear.
Yet everything works fine and I am able to access all the member methods by doing this: BSTHeader bst(5) by using the overloaded constructor.
I am not sure whether its a namespace issue or not. I feel as though I am missing something.
The line
BSTHeader::BSTHeader bst(); // <----- ERROR
is a declaration of a function named bst that takes no arguments and returns a BSTHeader::BSTHeader.
This is known as the "most vexing parse", and often described in less polite language.
If you want to instantiate an instance, giving the constructor no arguments, remove the ().
I am writing a binary tree search program but I'm not sure how to add nodes and search through them. The nodes come from a .txt file that is being read with a different file so just assume that already works.
The text file looks like:
Name Location
Old Building 31.2222
New Building 21.2111
Like I said, the program already reads in the file so that's not an issue. However, I have to insert the name and location into the nodes of the binary tree. Then I have to search everything within a range which is where the plus minus comes from.
Side note: my copy constructor may be incorrect as well though it complies properly.
Thanks for the help!
#ifndef BINTREE_HPP
#define BINTREE_HPP
#include <utility>
#include <string>
#include <vector>
class bintree {
// A binary search tree for locations in Lineland.
// Notes:
// - Assume a flat, one-dimensional world with locations from -180 to 180.
// - All locations and distances are measured in the same units (degrees).
public:
// Default constructor
bintree() {
this->root = NULL;
}
// Copy constructor
bintree(const bintree &t) {
this -> root = NULL;
*this = t;
}
// Destructor
~bintree() {
}
// Copy assignment is implemented using the copy-swap idiom
friend void swap(bintree &t1, bintree &t2) {
using std::swap;
// Swap all data members here, e.g.,
// swap(t1.foo, t2.foo);
// Pointers should be swapped -- but not the things they point to.
}
bintree &operator= (bintree other) {
// You don't need to modify this function.
swap(*this, other);
return *this;
}
void insert(const std::string& name, double p) {
// insert node with name and location (p)
}
void within_radius(double p, double r, std::vector<std::string> &result) const {
// Search for elements within the range `p` plus or minus `r`.
// Clears `result` and puts the elements in `result`.
// Postcondition: `result` contains all (and only) elements of the
// tree, in any order, that lie within the range `p` plus or minus
// `r`.
}
private:
struct node
{
node *left;
node *right;
};
node* root;
};
#endif
First, your nodes need to hold the data:
struct node
{
node *left;
node *right;
std::string name; // This is the key for your reasearch
double p; // followed by other data
};
Then you can think to browsing through your tree to insert a new node.
In this example, I assume that you can insert several nodes with the same name.
void insert(const std::string& name, double p) {
node *n = new node; // create a new node
n->name=name; n->p=p; // intialise the data payload
n->left=n->right=nullptr; // and make it a leaf.
if (root==nullptr) // if tree is empty,
root = n; // add the new node.
else { // else find where to insert it
node* t=root;
while (true) {
if (t->name > n->name) { // go to left
if (t->left==nullptr) {
t->left = n;
break;
}
else t=t->left;
}
else if (t->name == n->name) { // insert between current and next
n->right = t->right;
t->right = n;
break;
}
else { // go to right
if (t->right==nullptr) {
t->right = n;
break;
}
else t=t->right;
}
}
}
}
Here a live demo.
Note that I have only answered your insertion question, you still have to do a lot on your own (operator= and copy constructor need review, a destructor needs to be created, etc...)