I can't figure out why in this code example the std::set container is not ordering the Entities as I expect on the basis of the compare class I defined. Anyone can help me please? Thanks
#include <iostream>
#include <set>
class Entity {
public:
int num;
Entity(int num):num(num){}
bool operator< (const Entity& _entity) const { return (this->num < _entity.num); }
};
struct my_cmp {
bool operator() (const Entity* lhs, const Entity* rhs) const { return (lhs < rhs); }
};
class EntityManager {
private:
std::set<Entity*, my_cmp> entities;
public:
void AddEntity(int num) { entities.insert(new Entity(num)); }
void ListAllEntities() const {
unsigned int i = 0;
for (auto& entity: entities) {
std::cout << "Entity[" << i << "]: num:" << entity->num << std::endl;
i++;
}
}
};
int main(void) {
EntityManager manager;
manager.AddEntity(2);
manager.AddEntity(1);
manager.AddEntity(4);
manager.AddEntity(3);
manager.ListAllEntities();
return 0;
}
Output:
Entity[0]: num:2
Entity[1]: num:1
Entity[2]: num:4
Entity[3]: num:3
I would expect the following output instead:
Entity[1]: num:1
Entity[0]: num:2
Entity[3]: num:3
Entity[2]: num:4
You need to dereference your pointers *lhs < *rhs. You're just comparing the value of the pointers currently, so your order is dependent on their location in memory.
#include <iostream>
#include <set>
class Entity {
public:
int num;
Entity(int num):num(num){}
bool operator< (const Entity& _entity) const { return (this->num < _entity.num); }
};
struct my_cmp {
bool operator() (const Entity* lhs, const Entity* rhs) const { return (*lhs < *rhs); }
};
class EntityManager {
private:
std::set<Entity*, my_cmp> entities;
public:
void AddEntity(int num) { entities.insert(new Entity(num)); }
void ListAllEntities() const {
unsigned int i = 0;
for (auto& entity: entities) {
std::cout << "Entity[" << i << "]: num:" << entity->num << std::endl;
i++;
}
}
};
int main(void) {
EntityManager manager;
manager.AddEntity(2);
manager.AddEntity(1);
manager.AddEntity(4);
manager.AddEntity(3);
manager.ListAllEntities();
return 0;
}
Demo
I was implementing the ring buffer and have encountered an error. What does it mean to store a reference of outer class(class ring) object(m_ring) in inner class(class iterator) and when I remove the reference(&) the program compiles correctly but crashes. Please explain what is happening.(See the comment in Ring.h) Sorry for bad English.
// Ring.h
#ifndef RING.H
#define RING.H
#include <iostream>
using namespace std;
template<class T>
class ring {
unsigned int m_size;
int m_pos;
T *m_values;
public:
class iterator;
public:
ring(unsigned int size) : m_size(size), m_pos(0)
{
m_values = new T[m_size];
}
~ring()
{
delete[] m_values;
}
void add(const T &val)
{
m_values[m_pos] = val;
m_pos++;
m_pos %= m_size;
}
T& get(int pos)
{
return m_values[pos];
}
iterator begin()
{
return iterator(0, *this);
}
iterator end()
{
return iterator(m_size, *this);
}
};
template<class T>
class ring<T>::iterator {
int m_pos;
ring &m_ring; // Removing & gives garbage output.
public:
iterator(int pos, ring& aRing) : m_pos(pos), m_ring(aRing){}
bool operator!=(const iterator &other) const
{
return other.m_pos != m_pos;
}
iterator &operator++(int)
{
m_pos++;
return *this;
}
iterator &operator++()
{
m_pos++;
return *this;
}
T &operator*()
{
// return m_ring.m_values[m_pos];
return m_ring.get(m_pos);
}
};
#endif // RING
Driver program :
// Ring_Buffer_Class.cpp
#include <iostream>
#include "ring.h"
using namespace std;
int main()
{
ring<string> textring(3);
textring.add("one");
textring.add("two");
textring.add("three");
textring.add("four");
// C++ 98
for(ring<string>::iterator it = textring.begin(); it != textring.end(); it++)
{
cout << *it << endl;
}
cout << endl;
// C++11
for(string value : textring)
{
cout << value << endl;
}
return 0;
}
I also observed that removing ~ring() (Destructor) results into correct output.
Expected output :
four
two
three
four
two
three
My code runs ok when thread number is 15 or less, but when I run it with larger thread number (but still a very tiny number) say 50. I ran into following error when main function exits, seems like error occurs in the cleaning up process. I couldn't figure out where the bug is. My development tool is Visual Studio 2017. Here's my code:
threadsafe_queue class:
#pragma once
#include <memory>
#include <mutex>
template<typename T>
class threadsafe_queue
{
private:
struct Node {
std::shared_ptr<T> data;
std::unique_ptr<Node> next;
};
Node* tail;
std::unique_ptr<Node> head;
std::mutex head_mutex;
std::mutex tail_mutex;
std::condition_variable data_cond;
Node* get_tail();
std::unique_ptr<Node> pop_head();
std::unique_lock<std::mutex> wait_for_data();
public:
threadsafe_queue();
~threadsafe_queue();
threadsafe_queue(const threadsafe_queue& t) = delete;
threadsafe_queue operator = (const threadsafe_queue& t) = delete;
void push(T);
bool try_pop(T&);
std::shared_ptr<T> try_pop();
void wait_and_pop(T&);
std::shared_ptr<T> wait_and_pop();
bool empty();
};
using namespace std;
template<typename T>
threadsafe_queue<T>::threadsafe_queue() {
head = std::unique_ptr<Node>(new Node);
tail = head.get();
}
template<typename T>
threadsafe_queue<T>::~threadsafe_queue()
{
}
template<typename T>
typename threadsafe_queue<T>::Node* threadsafe_queue<T>::get_tail() {
lock_guard<mutex> lock(tail_mutex);
return tail;
}
template<typename T>
unique_ptr<typename threadsafe_queue<T>::Node> threadsafe_queue<T>::pop_head()
{
auto old_head = move(head);
head = move(old_head->next);
return old_head;
}
template<typename T>
unique_lock<mutex> threadsafe_queue<T>::wait_for_data()
{
unique_lock<mutex> headLock(head_mutex);
data_cond.wait(headLock, [&] {return head.get() != get_tail(); });
return std::move(headLock);
}
template<typename T>
void threadsafe_queue<T>::wait_and_pop(T & value)
{
unique_lock<mutex> lock(wait_for_data());
value = move(pop_head()->data);
}
template<typename T>
shared_ptr<T> threadsafe_queue<T>::wait_and_pop()
{
unique_lock<mutex> lock(wait_for_data());
return pop_head()->data;
}
template<typename T>
void threadsafe_queue<T>::push(T newValue)
{
shared_ptr<T> data(make_shared<T>(std::move(newValue)));
unique_ptr<Node> new_tail(new Node);
{
lock_guard<mutex> lock(tail_mutex);
tail->data = data;
Node* new_tail_ptr = new_tail.get();
tail->next = move(new_tail);
tail = new_tail_ptr;
}
data_cond.notify_one();
}
template<typename T>
bool threadsafe_queue<T>::try_pop(T & value)
{
lock_guard<mutex> headLock(head_mutex);
if (head == get_tail())
return false;
value = move(pop_head()->data);
return true;
}
template<typename T>
shared_ptr<T> threadsafe_queue<T>::try_pop()
{
lock_guard<mutex> headLock(head_mutex);
if (head == get_tail())
return shared_ptr<T>();
return pop_head()->data;
}
template<typename T>
bool threadsafe_queue<T>::empty()
{
lock_guard<mutex> lock(head_mutex);
return head.get() == get_tail();
}
main function:
#pragma once
#include "threadsafe_queue.h"
#include <assert.h>
#include <memory>
#include <atomic>
#include <vector>
#include <thread>
using namespace std;
void worker(threadsafe_queue<int>& queue, std::atomic<int>& count, int const & pushcount, int const & popcount) {
for (unsigned i = 0; i < pushcount; i++) {
queue.push(i);
count++;
}
for (unsigned i = 0; i < popcount; i++) {
queue.wait_and_pop();
count--;
}
}
int main() {
threadsafe_queue<int> queue;
std::atomic<int> item_count = 0;
std::vector<thread*> threads;
unsigned const THREAD_COUNT=50, PUSH_COUT=100, POP_COUNT=50;
for (unsigned i = 0; i < THREAD_COUNT; i++) {
threads.push_back(new thread(worker, ref(queue), ref(item_count), ref(PUSH_COUT), ref(POP_COUNT)));
}
for (auto thread : threads) {
thread->join();
}
for (auto thread : threads) {
delete thread;
}
assert(item_count == THREAD_COUNT * (PUSH_COUT-POP_COUNT));
return 0;
}
error message:
Unhandled exception at 0x00862899 in Sample.exe: 0xC00000FD: Stack overflow
(parameters: 0x00000001, 0x00E02FDC). occurred
The location of the error is in memory library code:
const pointer& _Myptr() const _NOEXCEPT
{ // return const reference to pointer
return (_Mypair._Get_second());
}
The answer is based on #IgorTandetnik 's comment above. Basically I needed to implement ~threadsafe_queue to destroy the nodes iteratively. The nodes are linked, so they will be destructed in recursive manner, which causes stack overflow when number of nodes remaining in the queue is relatively large. Below is the destructor code.
threadsafe_queue<T>::~threadsafe_queue(){
Node* current = head.release();
while (current != tail) {
Node* temp = (current->next).release();
delete current;
current = temp;
}
delete tail;
}
#include <memory>
#include <map>
#include <stdexcept>
using namespace std;
class NoSuchNode:public runtime_error
{
public:
NoSuchNode(const char* str):runtime_error(str){}
NoSuchNode():runtime_error("can not find th node"){}
};
class NodeHasExist:public runtime_error
{
public:
NodeHasExist():runtime_error("the node has existed"){}
};
template <typename T>
struct Edge
{
T weight;
};
template <typename X,typename Y>
class AdMatrix
{
int num;
map<int,unique_ptr<Y>> nodes;
unique_ptr<unique_ptr<Edge<X>[]>[]> edges;
bool isNode(int no);
public:
AdMatrix(int num);
void addEdge(int from,int to, const X & weight);
void addEdge(int from,int to, X && weight);
int addNode(int no);
Edge<X>& getEdge(int from,int to);
Y& getNode(int no);
};
template <typename X,typename Y>
Y& AdMatrix<X,Y>::getNode(int no)
{
if(this->isNode(no))
return *this->nodes[no];
else
throw NoSuchNode();
}
template <typename X,typename Y>
Edge<X>& AdMatrix<X,Y>::getEdge(int from,int to)
{
if(this->isNode(from) && this->isNode(to))
return this->edges[from][to];
else
throw NoSuchNode();
}
template <typename X,typename Y>
AdMatrix<X,Y>::AdMatrix(int num)
{
this->edges = unique_ptr<unique_ptr<Edge<X>[]>[]>(new unique_ptr<Edge<X>[]>[num]);
for (int i=0; i<num; i++) {
this->edges[i] = unique_ptr<Edge<X>[]>(new Edge<X>[num]);
}
this->num = num;
}
template <typename X,typename Y>
bool AdMatrix<X,Y>::isNode(int no)
{
auto it = this->nodes.find(no);
if(it!=this->nodes.end())
return true;
else
return false;
}
template <typename X,typename Y>
int AdMatrix<X,Y>::addNode(int no)
{
if(this->isNode(no))
throw NodeHasExist();
else
this->nodes[no]=unique_ptr<Y>(new Y());
return no;
}
template <typename X,typename Y>
void AdMatrix<X,Y>::addEdge(int from, int to, const X & weight)
{
if(this->isNode(from) && this->isNode(to))
this->edges[from][to].weight=weight;
else
throw NoSuchNode();
}
template <typename X,typename Y>
void AdMatrix<X,Y>::addEdge(int from, int to,X && weight)
{
if(this->isNode(from) && this->isNode(to))
this->edges[from][to].weight=move(weight);
else
throw NoSuchNode();
}
----main
#include "AdMatrix.h"
#include <vector>
class EdgeWeight
{
int character_t;
bool isEmpty_t;
public:
EdgeWeight(){this->isEmpty_t=true;this->character_t=-1;}
EdgeWeight(int character)
{
this->character_t = character;
this->isEmpty_t=false;
}
bool isChar(int character);
void setEmpty(bool isEmpty){this->isEmpty_t = isEmpty;}
bool isEmpty(){return this->isEmpty_t;}
};
struct Node
{
bool isVisited;
vector<int> edges_to; //even other container,set
Node(){this->isVisited=false;}
};
int main(int args,char** argvs)
{
AdMatrix<unique_ptr<EdgeWeight>,Node> matrix(5);
matrix.addNode(1);
matrix.addNode(2);
matrix.addEdge(1, 2, unique_ptr<EdgeWeight>(new EdgeWeight(10)));
auto & node = matrix.getNode(1);
node.edges_to.push_back(1);
return 0;
}
someone who ran it on Linux has not find any error. Even checked with Valgrind, and did not find any memory issues.
My develop environment:
Mac OS X 10.10.2
dialect c++11
libc++
Xcode Version 6.3.2 (6D2105)
Apple LLVM version 6.1.0 (clang-602.0.53) (based on LLVM 3.6.0svn)
Target: x86_64-apple-darwin14.1.0
Thread model: posix
The error:(I can not upload the image)
libc++abi.dylib: terminating with uncaught exception of type std::length_error: vector
In std::map, this ends up causing an error when the first object is constructed. I've checked the debugger, and I see that free_list::init() creates the consecutive memory addresses correctly. I'm aware this allocator cannot be used in vector or other related containers, but it's only meant to work with the nodular containers.
I get a run-time error from this in xutility (in VC12), at line 158:
_Container_proxy *_Parent_proxy = _Parent->_Myproxy;
Checking the debugger, it appears that _Parent was never initialized, bringing about the 0xC0000005 run-time error. Why or how it didn't get initialized and why this occurred when the first object was being constructed (after std::map did 3 separate allocations), I do not know.
I would like to have this work with std::map and std::list and the other nodular containers and am not worried about whether it can perform in std::vector, etc.
#include <algorithm>
class free_list {
public:
free_list() {}
free_list(free_list&& other)
: m_next(other.m_next) {
other.m_next = nullptr;
}
free_list(void* data, std::size_t num_elements, std::size_t element_size) {
init(data, num_elements, element_size);
}
free_list& operator=(free_list&& other) {
m_next = other.m_next;
other.m_next = nullptr;
}
void init(void* data, std::size_t num_elements, std::size_t element_size) {
union building {
void* as_void;
char* as_char;
free_list* as_self;
};
building b;
b.as_void = data;
m_next = b.as_self;
b.as_char += element_size;
free_list* runner = m_next;
for (std::size_t s = 1; s < num_elements; ++s) {
runner->m_next = b.as_self;
runner = runner->m_next;
b.as_char += element_size;
}
runner->m_next = nullptr;
}
free_list* obtain() {
if (m_next == nullptr) {
return nullptr;
}
free_list* head = m_next;
m_next = head->m_next;
return head;
}
void give_back(free_list* ptr) {
ptr->m_next = m_next;
m_next = ptr;
}
free_list* m_next;
};
template<class T>
class pool_alloc {
typedef pool_alloc<T> myt;
public:
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
typedef T value_type;
typedef T& reference;
typedef const T& const_reference;
typedef T* pointer;
typedef const T* const_pointer;
typedef std::false_type propagate_on_container_copy_assignment;
typedef std::true_type propagate_on_container_move_assignment;
typedef std::true_type propagate_on_container_swap;
template<class U> struct rebind {
typedef pool_alloc<U> other;
};
~pool_alloc() {
destroy();
}
pool_alloc() : data(nullptr), fl(), capacity(4096) {
}
pool_alloc(size_type capacity) : data(nullptr), fl(), capacity(capacity) {}
pool_alloc(const myt& other)
: data(nullptr), fl(), capacity(other.capacity) {}
pool_alloc(myt&& other)
: data(other.data), fl(std::move(other.fl)), capacity(other.capacity) {
other.data = nullptr;
}
template<class U>
pool_alloc(const pool_alloc<U>& other)
: data(nullptr), fl(), capacity(other.max_size()) {}
myt& operator=(const myt& other) {
destroy();
capacity = other.capacity;
}
myt& operator=(myt&& other) {
destroy();
data = other.data;
other.data = nullptr;
capacity = other.capacity;
fl = std::move(other.fl);
}
static pointer address(reference ref) {
return &ref;
}
static const_pointer address(const_reference ref) {
return &ref;
}
size_type max_size() const {
return capacity;
}
pointer allocate(size_type) {
if (data == nullptr) create();
return reinterpret_cast<pointer>(fl.obtain());
}
void deallocate(pointer ptr, size_type) {
fl.give_back(reinterpret_cast<free_list*>(ptr));
}
template<class... Args>
static void construct(pointer ptr, Args&&... args) {
::new (ptr) T(std::forward<Args>(args)...);
}
static void destroy(pointer ptr) {
ptr->~T();
}
bool operator==(const myt& other) const {
return reinterpret_cast<char*>(data) ==
reinterpret_cast<char*>(other.data);
}
bool operator!=(const myt& other) const {
return !operator==(other);
}
private:
void create() {
data = ::operator new(capacity * sizeof(value_type));
fl.init(data, capacity, sizeof(value_type));
}
void destroy() {
::operator delete(data);
data = nullptr;
}
void* data;
free_list fl;
size_type capacity;
};
template<>
class pool_alloc < void > {
public:
template <class U> struct rebind { typedef pool_alloc<U> other; };
typedef void* pointer;
typedef const void* const_pointer;
typedef void value_type;
};
The problem comes when std::pair is being constructed (in MSVC12 utility at line 214):
template<class _Other1,
class _Other2,
class = typename enable_if<is_convertible<_Other1, _Ty1>::value
&& is_convertible<_Other2, _Ty2>::value,
void>::type>
pair(_Other1&& _Val1, _Other2&& _Val2)
_NOEXCEPT_OP((is_nothrow_constructible<_Ty1, _Other1&&>::value
&& is_nothrow_constructible<_Ty2, _Other2&&>::value))
: first(_STD forward<_Other1>(_Val1)),
second(_STD forward<_Other2>(_Val2))
{ // construct from moved values
}
Even after stepping in, the run-time error occurs, the same as described above with _Parent not being initialized.
I was able to answer my own question through extensive debugging. Apparently, VC12's std::map implementation at least at times will cast an _Alnod (permanent allocator that stays in scope for the life of the map, which is used to allocate and deallocate the nodes in the map, what I'd expect to be what actually calls allocate() and deallocate()) as an _Alproxy, a temporary allocator which creates some sort of object called _Mproxy (or something like that) using allocate(). The problem, though, is that VC12's implementation then lets _Alproxy go out of scope while still expecting the pointer to the allocated object to remain valid, so it is clear then that I would have to use ::operator new and ::operator delete on an object like _Mproxy: using a memory pool that then goes out of scope while a pointer to a location in it remains is what causes the crash.
I came up with what I suppose could be called a dirty trick, a test that is performed when copy-constructing or copy-assigning an allocator to another allocator type:
template<class U>
pool_alloc(const pool_alloc<U>& other)
: data(nullptr), fl(), capacity(other.max_size()), use_data(true) {
if (sizeof(T) < sizeof(U)) use_data = false;
}
I added the bool member use_data to the allocator class, which if true means to use the memory pool and which if false means to use ::operator new and ::operator delete. By default, it is true. The question of its value arises when the allocator gets cast as another allocator type whose template parameter's size is smaller than that of the source allocator; in that case, use_data is set to false. Because this _Mproxy object or whatever it's called is rather small, this fix seems to work, even when using std::set with char as the element type.
I've tested this using std::set with type char in both VC12 and GCC 4.8.1 in 32-bit and have found that in both cases it works. When allocating and deallocating the nodes in both cases, the memory pool is used.
Here is the full source code:
#include <algorithm>
class free_list {
public:
free_list() {}
free_list(free_list&& other)
: m_next(other.m_next) {
other.m_next = nullptr;
}
free_list(void* data, std::size_t num_elements, std::size_t element_size) {
init(data, num_elements, element_size);
}
free_list& operator=(free_list&& other) {
if (this != &other) {
m_next = other.m_next;
other.m_next = nullptr;
}
return *this;
}
void init(void* data, std::size_t num_elements, std::size_t element_size) {
union building {
void* as_void;
char* as_char;
free_list* as_self;
};
building b;
b.as_void = data;
m_next = b.as_self;
b.as_char += element_size;
free_list* runner = m_next;
for (std::size_t s = 1; s < num_elements; ++s) {
runner->m_next = b.as_self;
runner = runner->m_next;
b.as_char += element_size;
}
runner->m_next = nullptr;
}
free_list* obtain() {
if (m_next == nullptr) {
return nullptr;
}
free_list* head = m_next;
m_next = head->m_next;
return head;
}
void give_back(free_list* ptr) {
ptr->m_next = m_next;
m_next = ptr;
}
free_list* m_next;
};
template<class T>
class pool_alloc {
typedef pool_alloc<T> myt;
public:
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
typedef T value_type;
typedef T& reference;
typedef const T& const_reference;
typedef T* pointer;
typedef const T* const_pointer;
typedef std::false_type propagate_on_container_copy_assignment;
typedef std::true_type propagate_on_container_move_assignment;
typedef std::true_type propagate_on_container_swap;
myt select_on_container_copy_construction() const {
return *this;
}
template<class U> struct rebind {
typedef pool_alloc<U> other;
};
~pool_alloc() {
clear();
}
pool_alloc() : data(nullptr), fl(), capacity(4096), use_data(true) {}
pool_alloc(size_type capacity) : data(nullptr), fl(),
capacity(capacity), use_data(true) {}
pool_alloc(const myt& other)
: data(nullptr), fl(), capacity(other.capacity),
use_data(other.use_data) {}
pool_alloc(myt&& other)
: data(other.data), fl(std::move(other.fl)), capacity(other.capacity),
use_data(other.use_data) {
other.data = nullptr;
}
template<class U>
pool_alloc(const pool_alloc<U>& other)
: data(nullptr), fl(), capacity(other.max_size()), use_data(true) {
if (sizeof(T) < sizeof(U)) use_data = false;
}
myt& operator=(const myt& other) {
if (*this != other) {
clear();
capacity = other.capacity;
use_data = other.use_data;
}
}
myt& operator=(myt&& other) {
if (*this != other) {
clear();
data = other.data;
other.data = nullptr;
capacity = other.capacity;
use_data = other.use_data;
fl = std::move(other.fl);
}
return *this;
}
template<class U>
myt& operator=(const pool_alloc<U>& other) {
if (this != reinterpret_cast<myt*>(&other)) {
capacity = other.max_size();
if (sizeof(T) < sizeof(U))
use_data = false;
else
use_data = true;
}
return *this;
}
static pointer address(reference ref) {
return &ref;
}
static const_pointer address(const_reference ref) {
return &ref;
}
size_type max_size() const {
return capacity;
}
pointer allocate(size_type) {
if (use_data) {
if (data == nullptr) create();
return reinterpret_cast<pointer>(fl.obtain());
} else {
return reinterpret_cast<pointer>(::operator new(sizeof(T)));
}
}
void deallocate(pointer ptr, size_type) {
if (use_data) {
fl.give_back(reinterpret_cast<free_list*>(ptr));
} else {
::operator delete(reinterpret_cast<void*>(ptr));
}
}
template<class... Args>
static void construct(pointer ptr, Args&&... args) {
::new ((void*)ptr) value_type(std::forward<Args>(args)...);
}
static void destroy(pointer ptr) {
ptr->~value_type();
}
bool operator==(const myt& other) const {
return reinterpret_cast<char*>(data) ==
reinterpret_cast<char*>(other.data);
}
bool operator!=(const myt& other) const {
return !operator==(other);
}
private:
void create() {
size_type size = sizeof(value_type) < sizeof(free_list*) ?
sizeof(free_list*) : sizeof(value_type);
data = ::operator new(capacity * size);
fl.init(data, capacity, size);
}
void clear() {
::operator delete(data);
data = nullptr;
}
void* data;
free_list fl;
size_type capacity;
bool use_data;
};
template<>
class pool_alloc < void > {
public:
template <class U> struct rebind { typedef pool_alloc<U> other; };
typedef void* pointer;
typedef const void* const_pointer;
typedef void value_type;
};
template<class Container, class Alloc>
void change_capacity(Container& c, typename Alloc::size_type new_capacity) {
Container temp(c, Alloc(new_capacity));
c = std::move(temp);
}
Since the allocator is not automatic-growing (don't know how to make such a thing), I have added the change_capacity() function.