Issues with using IloCP with too many workers - constraint-programming

I have built a simple constraint enforcing that the value of a variable must be consistent with a modulo operation. Two consistency levels are available: bounds consistency and domain consistency. In this example, solving the problem with bounds consistency works well with 1 or 2 workers, but with 3 or more workers the propagate() function loops infinitely since it appears to be unable to remove the maximum value of the domain. My computer has 2 physical cores, so my guess is that using more workers than physical cores is the problem. I am using the latest version of Cplex Optimization Studio. Here is the code:
File main.cc:
#include <ilcp/cp.h>
#include "IloModulo.h"
int main() {
IloEnv env;
IloModel model(env);
IloIntVar x(env, 1, 100000);
model.add(IloModulo(env, x, 3, BOUNDS_CONSISTENCY));
model.add(IloMinimize(env, x));
IloCP solver(model);
solver.setParameter(IloCP::Workers, 1); // Infinite loop with 3 or more workers
solver.solve();
return 0;
}
File IloModulo.h:
#ifndef ILOMODULO_H_
#define ILOMODULO_H_
#include <ilcp/cpext.h>
#define BOUNDS_CONSISTENCY 1
#define DOMAIN_CONSISTENCY 2
class IlcModuloI : public IlcConstraintI {
private:
IlcIntVar x_;
IlcInt modulo_;
IlcInt consistency_;
public:
IlcModuloI(IloCPEngine cp,
IlcIntVar x,
IlcInt modulo,
IlcInt consistency);
~IlcModuloI();
virtual void propagate();
virtual void post();
};
// The IloModulo constraint is used with the following function
IloConstraint IloModulo(IloEnv env,
IloIntVar x,
IloInt modulo,
IloInt consistency);
#endif // ILOMODULO_H_
File IloModulo.cc:
#include "IloModulo.h"
IlcModuloI::IlcModuloI(IloCPEngine solver,
IlcIntVar x,
IlcInt modulo,
IlcInt consistency) :
IlcConstraintI(solver),
modulo_(modulo),
x_(x),
consistency_(consistency) {
;
}
IlcModuloI::~IlcModuloI() {
;
}
void IlcModuloI::propagate() {
switch (consistency_) {
case BOUNDS_CONSISTENCY: {
while ((x_.getMin() % modulo_ != 0) ||
(x_.getMax() % modulo_ != 0)) {
if (x_.getMin() % modulo_ != 0) {
x_.setMin(x_.getMin()+1);
continue;
}
if (x_.getMax() % modulo_ != 0) {
std::cout << "Min/max values: " << x_.getMin() << "/" << x_.getMax() << std::endl;
std::cout << "Decreasing maximum value by 1." << std::endl;
x_.setMax(x_.getMax()-1);
std::cout << "Min/max values: " << x_.getMin() << "/" << x_.getMax() << std::endl;
std::cout << "------------------------------" << std::endl;
continue;
}
}
break;
}
case DOMAIN_CONSISTENCY: {
IlcInt threshold = x_.getMin();
while (threshold <= x_.getMax()) {
if (threshold % modulo_ != 0) {
x_.removeValue(threshold);
}
if (threshold == x_.getMax()) {
break;
}
threshold = x_.getNextHigher(threshold);
}
break;
}
}
}
void IlcModuloI::post() {
switch (consistency_) {
case BOUNDS_CONSISTENCY: {
x_.whenRange(this);
break;
}
case DOMAIN_CONSISTENCY: {
x_.whenDomain(this);
break;
}
}
}
ILOCPCONSTRAINTWRAPPER3(IloModuloWrapper, solver,
IloIntVar, x_,
IloInt, modulo_,
IloInt, consistency_) {
use(solver, x_);
return new (solver.getHeap()) IlcModuloI(solver,
solver.getIntVar(x_),
modulo_,
consistency_);
}
IloConstraint IloModulo(IloEnv env,
IloIntVar x,
IloInt modulo,
IloInt consistency) {
return IloModuloWrapper(env, x, modulo, consistency);
}

Indeed it really seems to be an issue with the propagation of user defined constraints in some very specific conditions. I'm not sure if there is a direct link with the number of workers. The development team of CP Optimizer will investigate this problem and we will let you know.

Actually, there is no problem in CP Optimizer, the problem comes from the ‘while’ loop in the propagation. During propagation, when you change the min/max of a variable, the change is not necessarily applied immediately, so if you do x.setMax(x.getMax()-1), the new max is not applied immediately and in the same propagate(), x.getMax() still returns the ‘old’ max value. This is the reason for the loop. So if you really want to remove the values one by one like in your current algorithm, you should store the current bounds:
case BOUNDS_CONSISTENCY: {
IloInt min = x_.getMin();
IloInt max = x_.getMax();
while ((min % modulo_ != 0) || (max % modulo_ != 0)) {
if (min % modulo_ != 0) {
min += 1;
x_.setMin(min);
continue;
}
if (max % modulo_ != 0) {
max -=1;
x_.setMax(max);
continue;
}
}
But of course, you do not need to work by increments and you could directly set the right min/max bounds using integer division:
IloInt min = x_.getMin();
IloInt max = x_.getMax();
min = ( (min+_modulo-1) / _modulo) * _modulo;
max = ( (max) / _modulo) * _modulo;
x_.setRange(min, max);
And of course (but I don’t know what is the context of your question), you do not need to define a new constraint, you can just use the predefined modulo expressions in the model, that is, posting:
model.add( (x % _modulo)==0 );
At the concert level.

Related

My thread-safe queue code appears to work, any possible race conditions, deadlocks, or other design problems?

I am new to using condition_variables and unique_locks in C++. I am working on creating an event loop that polls two custom event-queues and a "boolean" (see integer acting as boolean), which can be acted upon by multiple sources.
I have a demo (below) that appears to work, which I would greatly appreciate if you can review and confirm if it follows the best practices for using unique_lock and condition_variables and any problems you foresee happening (race conditions, thread blocking, etc).
In ThreadSafeQueue::enqueue(...): are we unlocking twice by calling notify and having the unique_lock go out of scope?
In the method TheadSafeQueue::dequeueAll(): We assume it is being called by a method that has been notified (cond.notify), and therefore has been locked. Is there a better way to encapsulate this to keep the caller cleaner?
Do we need to make our class members volatile similar to this?
Is there a better way to mockup our situation that allows us to test if we've correctly implemented the locks? Perhaps without the sleep statements and automating the checking process?
ThreadSafeQueue.h:
#include <condition_variable>
#include <cstdint>
#include <iostream>
#include <mutex>
#include <vector>
template <class T>
class ThreadSafeQueue {
public:
ThreadSafeQueue(std::condition_variable* cond, std::mutex* unvrsl_m)
: ThreadSafeQueue(cond, unvrsl_m, 1) {}
ThreadSafeQueue(std::condition_variable* cond, std::mutex* unvrsl_m,
uint32_t capacity)
: cond(cond),
m(unvrsl_m),
head(0),
tail(0),
capacity(capacity),
buffer((T*)malloc(get_size() * sizeof(T))),
scratch_space((T*)malloc(get_size() * sizeof(T))) {}
std::condition_variable* cond;
~ThreadSafeQueue() {
free(scratch_space);
free(buffer);
}
void resize(uint32_t new_cap) {
std::unique_lock<std::mutex> lock(*m);
check_params_resize(new_cap);
free(scratch_space);
scratch_space = buffer;
buffer = (T*)malloc(sizeof(T) * new_cap);
copy_cyclical_queue();
free(scratch_space);
scratch_space = (T*)malloc(new_cap * sizeof(T));
tail = get_size();
head = 0;
capacity = new_cap;
}
void enqueue(const T& value) {
std::unique_lock<std::mutex> lock(*m);
resize();
buffer[tail++] = value;
if (tail == get_capacity()) {
tail = 0;
} else if (tail > get_capacity())
throw("Something went horribly wrong TSQ: 75");
cond->notify_one();
}
// Assuming m has already been locked by the caller...
void dequeueAll(std::vector<T>* vOut) {
if (get_size() == 0) return;
scratch_space = buffer;
copy_cyclical_queue();
vOut->insert(vOut->end(), buffer, buffer + get_size());
head = tail = 0;
}
// Const functions because they shouldn't be modifying the internal variables
// of the object
bool is_empty() const { return get_size() == 0; }
uint32_t get_size() const {
if (head == tail)
return 0;
else if (head < tail) {
// 1 2 3
// 0 1 2
// 1
// 0
return tail - head;
} else {
// 3 _ 1 2
// 0 1 2 3
// capacity-head + tail+1 = 4-2+0+1 = 2 + 1
return get_capacity() - head + tail + 1;
}
}
uint32_t get_capacity() const { return capacity; }
//---------------------------------------------------------------------------
private:
std::mutex* m;
uint32_t head;
uint32_t tail;
uint32_t capacity;
T* buffer;
T* scratch_space;
uint32_t get_next_empty_spot();
void copy_cyclical_queue() {
uint32_t size = get_size();
uint32_t cap = get_capacity();
if (size == 0) {
return; // because we have nothing to copy
}
if (head + size <= cap) {
// _ 1 2 3 ... index = 1, size = 3, 1+3 = 4 = capacity... only need 1 copy
memcpy(buffer, scratch_space + head, sizeof(T) * size);
} else {
// 5 1 2 3 4 ... index = 1, size = 5, 1+5 = 6 = capacity... need to copy
// 1-4 then 0-1
// copy number of bytes: front = 1, to (5-1 = 4 elements)
memcpy(buffer, scratch_space + head, sizeof(T) * (cap - head));
// just copy the bytes from the front up to the first element in the old
// array
memcpy(buffer + (cap - head), scratch_space, sizeof(T) * tail);
}
}
void check_params_resize(uint32_t new_cap) {
if (new_cap < get_size()) {
std::cerr << "ThreadSafeQueue: check_params_resize: size(" << get_size()
<< ") > new_cap(" << new_cap
<< ")... data "
"loss will occur if this happens. Prevented."
<< std::endl;
}
}
void resize() {
uint32_t new_cap;
uint32_t size = get_size();
uint32_t cap = get_capacity();
if (size + 1 >= cap - 1) {
std::cout << "RESIZE CALLED --- BAD" << std::endl;
new_cap = 2 * cap;
check_params_resize(new_cap);
free(scratch_space); // free existing (too small) scratch space
scratch_space = buffer; // transfer pointer over
buffer = (T*)malloc(sizeof(T) * new_cap); // allocate a bigger buffer
copy_cyclical_queue();
// move over everything with memcpy from scratch_space to buffer
free(scratch_space); // free what used to be the too-small buffer
scratch_space =
(T*)malloc(sizeof(T) * new_cap); // recreate scratch space
tail = size;
head = 0;
// since we're done with the old array... delete for memory management->
capacity = new_cap;
}
}
};
// Event Types
// keyboard/mouse
// network
// dirty flag
Main.cpp:
#include <unistd.h>
#include <cstdint>
#include <iostream>
#include <mutex>
#include <queue>
#include <sstream>
#include <thread>
#include "ThreadSafeQueue.h"
using namespace std;
void write_to_threadsafe_queue(ThreadSafeQueue<uint32_t> *q,
uint32_t startVal) {
uint32_t count = startVal;
while (true) {
q->enqueue(count);
cout << "Successfully enqueued: " << count << endl;
count += 2;
sleep(count);
}
}
void sleep_and_set_redraw(int *redraw, condition_variable *cond) {
while (true) {
sleep(3);
__sync_fetch_and_or(redraw, 1);
cond->notify_one();
}
}
void process_events(vector<uint32_t> *qOut, condition_variable *cond,
ThreadSafeQueue<uint32_t> *q1,
ThreadSafeQueue<uint32_t> *q2, int *redraw, mutex *m) {
while (true) {
unique_lock<mutex> lck(*m);
cond->wait(lck);
q1->dequeueAll(qOut);
q2->dequeueAll(qOut);
if (__sync_fetch_and_and(redraw, 0)) {
cout << "FLAG SET" << endl;
qOut->push_back(0);
}
for (auto a : *qOut) cout << a << "\t";
cout << endl;
cout << "PROCESSING: " << qOut->size() << endl;
qOut->clear();
}
}
void test_2_queues_and_bool() {
try {
condition_variable cond;
mutex m;
ThreadSafeQueue<uint32_t> q1(&cond, &m, 1024);
ThreadSafeQueue<uint32_t> q2(&cond, &m, 1024);
int redraw = 0;
vector<uint32_t> qOut;
thread t1(write_to_threadsafe_queue, &q1, 2);
thread t2(write_to_threadsafe_queue, &q2, 1);
thread t3(sleep_and_set_redraw, &redraw, &cond);
thread t4(process_events, &qOut, &cond, &q1, &q2, &redraw, &m);
t1.join();
t2.join();
t3.join();
t4.join();
} catch (system_error &e) {
cout << "MAIN TEST CRASHED" << e.what();
}
}
int main() { test_2_queues_and_bool(); }

synchronize 3 threads to print sequential output

I was asked this question in an interview. I was pretty clueless.
So I decided to learn some multithreading and hopefully find an answer to this question.
I need to use 3 threads to print the output: 01020304050607.....
Thread1: prints 0
Thread2: prints odd numbers
Thread3: prints even numbers
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
std::mutex m;
std::condition_variable cv1, cv2, cv3;
int count = 0;
void printzero(int end)
{
while (count <= end)
{
std::unique_lock<std::mutex> lock(m);
cv1.wait(lock);
std::cout << 0 << " ";
++count;
if (count % 2 == 1)
{
lock.unlock();
cv2.notify_one();
}
else
{
lock.unlock();
cv3.notify_one();
}
}
}
void printodd(int end)
{
while (count <= end)
{
std::unique_lock<std::mutex> lock(m);
cv2.wait(lock);
if (count % 2 == 1)
{
std::cout << count << " ";
++count;
lock.unlock();
cv1.notify_one();
}
}
}
void printeven(int end)
{
while (count <= end)
{
std::unique_lock<std::mutex> lock(m);
cv3.wait(lock);
if (count % 2 == 0)
{
std::cout << count << " ";
++count;
lock.unlock();
cv1.notify_one();
}
}
}
int main()
{
int end = 10;
std::thread t3(printzero, end);
std::thread t1(printodd, end);
std::thread t2(printeven, end);
cv1.notify_one();
t1.join();
t2.join();
t3.join();
return 0;
}
My solution seems to be in a deadlock situation. I'm not even sure if the logic is correct. Please help
There are several issues with your code. Here is what you need to do in order to make it work:
Revise your while (count <= end) check. Reading count without synchronization is undefined behavior (UB).
Use a proper predicate with std::condition_variable::wait. Problems of your code without predicate:
If notify_one is called before wait then the notification is lost. In the worst case, main's call to notify_one is executed before the threads start running. As a result, all threads may wait indefinitely.
Spurious wakeups may disrupt your program flow. See also cppreference.com on std::condition variable.
Use std::flush (just to be sure).
I played around with your code quite a lot. Below you find a version where I applied my suggested fixes. In addition, I also experimented with some other ideas that came to my mind.
#include <cassert>
#include <condition_variable>
#include <functional>
#include <iostream>
#include <mutex>
#include <thread>
#include <vector>
// see the `std::mutex` for an example how to avoid global variables
std::condition_variable cv_zero{};
std::condition_variable cv_nonzero{};
bool done = false;
int next_digit = 1;
bool need_zero = true;
void print_zero(std::mutex& mt) {
while(true) {// do not read shared state without holding a lock
std::unique_lock<std::mutex> lk(mt);
auto pred = [&] { return done || need_zero; };
cv_zero.wait(lk, pred);
if(done) break;
std::cout << 0 << "\t"
<< -1 << "\t"// prove that it works
<< std::this_thread::get_id() << "\n"// prove that it works
<< std::flush;
need_zero = false;
lk.unlock();
cv_nonzero.notify_all();// Let the other threads decide which one
// wants to proceed. This is probably less
// efficient, but preferred for
// simplicity.
}
}
void print_nonzero(std::mutex& mt, int end, int n, int N) {
// Example for `n` and `N`: Launch `N == 2` threads with this
// function. Then the thread with `n == 1` prints all odd numbers, and
// the one with `n == 0` prints all even numbers.
assert(N >= 1 && "number of 'nonzero' threads must be positive");
assert(n >= 0 && n < N && "rank of this nonzero thread must be valid");
while(true) {// do not read shared state without holding a lock
std::unique_lock<std::mutex> lk(mt);
auto pred = [&] { return done || (!need_zero && next_digit % N == n); };
cv_nonzero.wait(lk, pred);
if(done) break;
std::cout << next_digit << "\t"
<< n << "\t"// prove that it works
<< std::this_thread::get_id() << "\n"// prove that it works
<< std::flush;
// Consider the edge case of `end == INT_MAX && next_digit == INT_MAX`.
// -> You need to check *before* incrementing in order to avoid UB.
assert(next_digit <= end);
if(next_digit == end) {
done = true;
cv_zero.notify_all();
cv_nonzero.notify_all();
break;
}
++next_digit;
need_zero = true;
lk.unlock();
cv_zero.notify_one();
}
}
int main() {
int end = 10;
int N = 2;// number of threads for `print_nonzero`
std::mutex mt{};// example how to pass by reference (avoiding globals)
std::thread t_zero(print_zero, std::ref(mt));
// Create `N` `print_nonzero` threads with `n` in [0, `N`).
std::vector<std::thread> ts_nonzero{};
for(int n=0; n<N; ++n) {
// Note that it is important to pass `n` by value.
ts_nonzero.emplace_back(print_nonzero, std::ref(mt), end, n, N);
}
t_zero.join();
for(auto&& t : ts_nonzero) {
t.join();
}
}

find method in a stack

I've implemented a method to find a value from a 'Stack'. I just want to know that are there any logical errors or any kind of errors in this code?
Thanks.
bool about::findData(char key)
{
if(top==-1)
cout<<"Stack is Empty"<<endl;
else
{
for(int x=0; x<maxSize-1; x++)
{
if(stackArray[x]==key)
return stackArray[x];
}
}
}
I don't really know what you want to do / ask. From a logical point of view this program will work.
But you got some hops and jumps in there. I will start with a working example and point out the differences.
Note that I won't address the stack logic in itself. It seems like you did start out with a stack like thing, but got driven quite away from it?
You have a (top == -1) query in there, but never ever declared top / assigned a value to it. I assumed that you meant the size of the array (not that this is probably not the best way to calculate the array size, especially as C++ has a vector container anyways).
You iterate over maxSize elements, yet didn't declare maxSize anywhere. This also assumes that your stack is of fixed size and all elements are initiated with a sane value. I am just iterating over the size of the current array right now.
You return the value return stackArray[x];, yet declared your function to have a bool return value - this does no harm in itself, but I went for returning true and false to adhere to the return value.
Try the code online.
#include <iostream>
using namespace std;
int maxSize = 10;
char stackArray[] = {'b', 'a', 'c', 'd'};
//char stackArray[] = {};
bool findData(char key)
{
int top = (sizeof(stackArray)/sizeof(*stackArray));
cout << "Size of array is: " << top << endl;
if(top <= 0)
cout<<"Stack is Empty"<<endl;
else
{
for(int x=0; x<top; x++)
{
if(stackArray[x]==key)
return true;
}
return false;
}
}
int main() {
if(findData('a')) {
cout << "We found the value!" << endl;
} else {
cout << "We didn't find the value!" << endl;
}
}
EDIT
And as I couldn't resist I jump started with an array based stack architecture from here and implemented our beloved findValue within this stack.
#include <iostream>
#include <string>
using namespace std;
class Stack {
private:
int top;
int capacity;
int *storage;
public:
Stack(int capacity) {
if (capacity <= 0)
throw string("Stack's capacity must be positive");
storage = new int[capacity];
this->capacity = capacity;
top = -1;
}
void push(int value) {
if (top == capacity)
throw string("Stack's underlying storage is overflow");
top++;
storage[top] = value;
}
int peek() {
if (top == -1)
throw string("Stack is empty");
return storage[top];
}
void pop() {
if (top == -1)
throw string("Stack is empty");
top--;
}
/* Our findValue function. */
bool findValue(int key) {
/* Now the usage of top makes perfect sense,
the counter variable always contains the amount
of stored elements. */
if (top == -1) {
throw string("Stack is empty");
}
/* We traverse all elements of our storage. */
for (int i = 0; i < top; i++) {
if(storage[i] == key) {
return true;
}
}
return false;
}
bool isEmpty() {
return (top == -1);
}
~Stack() {
delete[] storage;
}
};
int main() {
/* Init the new stack. */
Stack* stack = new Stack(10);
/* Push some elements for more fun. */
stack->push('a');
stack->push('b');
stack->push('c');
stack->push('d');
stack->push('e');
stack->push('f');
/* Show charcode of last pushed element. */
cout << stack->peek() << endl;
/* Find 'e'. */
if (stack->findValue('e')) {
cout << "We found the value" << endl;
}
/* Try and not find 'g'. */
if (!(stack->findValue('g'))) {
cout << "We didn't find the value" << endl;
}
delete stack;
return 0;
}

logic error in prime # program. The program just ends after I answer the prompt so I can't see the numbers

//Purpose: Display first 'n' (user chosen) number if emirps to the console, five per line.
//Note: An "emirp" is a prime number that is also prime when reversed.
#include <iostream>
using namespace std;
bool isPrime(int value); //Prototyle for "prime number function"
int reverse (int value2); //Prototype for "emirp function"
int main()
{
//Ask the user for a positive number
cout << "Please enter a positive number: ";
int n;
cin >> n;
//Reject negative value input
if ( n < 1)
{
cout << "INVALID NUMBER \n";
}
else
{
//Calculate all emirps up to 'n'.
int test = 0;
int number = 2;
while (test < n)
{
if (isPrime(number) && reverse(number))
{
cout << "\n" << reverse(number) << "\t\t\t";
test++;
}
else
{
test++;
}
}
}
system("pause");
return 0;
}
bool isPrime(int value)
{
//If value is prime, the remainder (count) will be zero twice--for 1 and itself.
int divisor = 1;
int count = 0;
int prime = 0;
if (value % divisor == 0)
{
count++;
++divisor;
}
if (count = 2)
{
return true;
}
else
{
return false;
}
}
int reverse(int value2)
{
//reverse the number
value2*=10;
value2 = value2 %10;
value2/=10;
//same procedure as prime function
int divisor2 = 1;
int count2 = 0;
int emirp = 0;
if (value2 % divisor2 == 0)
{
count2++;
++divisor2;
}
if (count2 = 2)
{
int emirp = value2;
}
return emirp;
}
How does this even build?
if (count = 2)
{
...
}
Also:
your reverse function just returns an int, what do you expect
if (isPrime(number) && reverse(number)) to do with that result?
It's not a good way of working to do the whole calculation again btw:
cout << "\n" << reverse(number) << "\t\t\t"; //you already did this in your "check"
Edit:
And no wonder it doesn't work.
You check the number-value (2) every time, not n
If it is just about viewing the console output :
Press CTRL+F5 to run application in Visual studio.
just provide a getch()in main() function
And your code syntax is not in a right way :
if (count = 2) //count ==2
{
return true;
}
if (isPrime(number) && reverse(number))
{
cout << "\n" << reverse(number) << "\t\t\t";
test++;
}
will call reverse() 2 times.
modify it something like ;
int RevNum = reverse(number);
if (isPrime(number) &&RevNum)
{
cout << "\n" << RevNum << "\t\t\t";
test++;
}

Acces violation error with queue and linked list c++

I have an assignment and it's to build a queue linked list.
Our professor gave us the skeleton and told us to use his main.
I wrote my functions and everything compiled right but when I ran it, I got this error.
Don't know what to do from here.
Source Code:
#include<iostream>
using namespace std;
template<class ItemType>
struct NodeType
{
ItemType info;
NodeType* next;
};
template<class ItemType>
class Queue
{
private:
int size;
NodeType<ItemType>* front; // It points to the front of a singly-linked list
NodeType<ItemType>* rear; // It points to the end of a singly-linked list
public:
Queue(); // default constructor: Queue is created and empty
Queue(const Queue<ItemType> &x); // copy constructor: implicitly called
// for a deep copy
void MakeEmpty(); // Queue is made empty; you should deallocate all
// the nodes of the linked list
bool IsEmpty( ); // test if the queue is empty
bool IsFull( ); // test if the queue is full; assume MAXITEM=5
int length( ); // return the number of elements in the queue
void Print( ); // print the value of all elements in the queue in the sequence
// from the front to rear
void Enqueue(ItemType x); // insert x to the rear of the queue
// Precondition: the queue is not full
void Dequeue(ItemType &x); // delete the element from the front of the queue
// Precondition: the queue is not empty
~Queue(); // Destructor: memory for the dynamic array needs to be deallocated
};
template<class ItemType>
Queue<ItemType>::Queue()
{
size = 0;
front = NULL;
rear = NULL;
}
template<class ItemType>
Queue<ItemType>::Queue(const Queue<ItemType> &x)
{
NodeType<ItemType>* ptr1 ;
NodeType<ItemType>* ptr2 ;
if ( x.front == NULL )
{
front = NULL ;
}
else // allocate memory for first node
{
front = new NodeType<ItemType> ;
front->info = x.front->info ;
ptr1 = x.front->next ;
ptr2 = front ;
while ( ptr1 != NULL ) // deep copy other nodes
{
ptr2->next = new NodeType<ItemType> ;
ptr2 = ptr2->next ;
ptr2->info = ptr1->info ;
ptr1 = ptr1->next ;
}
ptr2->next = NULL;
rear = ptr2;
}
}
template<class ItemType>
void Queue<ItemType>::MakeEmpty()
{
NodeType<ItemType>* tempPtr;
while(front != NULL)
{
tempPtr = front;
front = front->next;
delete tempPtr;
}
rear=NULL;
}
template<class ItemType>
bool Queue<ItemType>::IsEmpty()
{
return (size == 0);
}
template<class ItemType>
bool Queue<ItemType>::IsFull()
{
return (size >= 5);
}
template<class ItemType>
int Queue<ItemType>::length()
{
return size;
}
template<class ItemType>
void Queue<ItemType>::Enqueue(ItemType x)
{
NodeType<ItemType>* newNode;
newNode = new NodeType<ItemType>;
newNode->info = x;
newNode->next = NULL;
if(rear == NULL)
{
front = newNode;
}
else
{
rear->next = newNode;
rear = newNode;
}
size++;
}
template<class ItemType>
void Queue<ItemType>::Dequeue(ItemType &x)
{
NodeType<ItemType>* tempPtr;
if(!IsEmpty())
{
tempPtr = front;
x = front->info;
front = front->next;
if(front == NULL)
{
rear = NULL;
}
delete tempPtr;
}
size--;
}
template<class ItemType>
void Queue<ItemType>::Print()
{
NodeType<ItemType> *temp;
temp = rear;
while(temp != NULL)
{
cout<<temp->info<<endl;
temp = temp->next;
}
}
template<class ItemType>
Queue<ItemType>::~Queue()
{
MakeEmpty();
}
int main()
{
Queue<int>IntQueue;
int x;
IntQueue.MakeEmpty();
IntQueue.Dequeue(x);
IntQueue.Enqueue(10);
IntQueue.Enqueue(20);
IntQueue.Enqueue(30);
IntQueue.Enqueue(40);
cout << "int length 3 = " << IntQueue.length() << endl;
IntQueue.Dequeue(x);
cout << "int length 4 = " << IntQueue.length() << endl;
cout << "The int queue contains: " << endl;
IntQueue.Print();
if(IntQueue.IsFull() == false)
cout << "The int queue is not full !" << endl;
else
cout << "The int queue is full !" << endl;
Queue<float>FloatQueue;
float y;
FloatQueue.MakeEmpty();
FloatQueue.Dequeue(y);
FloatQueue.Enqueue(7.1);
cout << "float length 3 = " << FloatQueue.length() << endl;
FloatQueue.Enqueue(2.3);
cout << "float length 4 = " << FloatQueue.length() << endl;
FloatQueue.Enqueue(3.1);
FloatQueue.Dequeue(y);
cout << "The float queue contains: " << endl;
FloatQueue.Print();
Queue<float> FloatQueue2 = FloatQueue;
cout << "The float queue 2 contains: " << endl;
FloatQueue2.Print();
FloatQueue.MakeEmpty();
cout << "The float queue 3 contains: " << endl;
FloatQueue2.Print();
system("pause");
return 0;
}
The problem I'm having is obviously with the print function.
Any help is appreciated.
Inside your copy constructor, you aren't setting rear to anything when x.front == NULL. This sets temp to an invalid value inside Print, causing you both to loop when you shouldn't and dereference an invalid pointer.

Resources