One of the fundamental differences between a script and an application is that many tasks need to happen simultaneously. For example, a webcam application can be “waiting” for input while continually updating the camera feed to the screen. From a programming standpoint there is no practical limit on the number of tasks (threads) that can be spawned and executed simultaneously, rather the limitation is the programmers ability to comprehend what is happening during debugging and troubleshooting. This is where a human-followable synchronization system is helpful, in this case the message queue.

In this example, a versatile queue (std::deque) is used underneath a simple interface. Here the functionality is limited to passing character based messages to the front and back of the queue, checking if the queue is empty, and retrieving the messages. Additionally several overloads exist to facilitate the passing of constant character strings, while the internal functionality remains the same.

Because the message queue is used to aid the programmer in the synchronization of an application, it is helpful to print to the console as messages are added to a queue. Note that while printing to the console is a relic of console programming, the “output” of the underlying libraries can be seen while debugging in Visual Studio’s output window. Additional overloads are added to provide the option to suppress the output, this option will be used when queues are cascaded in more elaborate programs.

alx_system_messagequeue.h

//Jacob W. Chesna
//2019_08_01: isEmpty to isMessage
//2019_08_01: Changed back to bool on isMessage, because the lib is not likely to be used without a export layer.
//2019_07_09: switched to internally managed memory to accomidate const char & char. Now caller allocates memory for getNext() to copy into
//2019_07_02: changed to int from bool, bool is not blittable
//2019_07_02: changed back to char* and allocated the returned message on the heap (using new)
//2019_02_05: updated to const char* 
//2019_02_08: FUNDAMENTAL CHANGE to <deque> from <queue>, enables estop events to be added to front of queue (Double Ended Queue)

#pragma once
//#include <queue>
#include <deque> 


namespace alx {
	namespace system{
		class MessageQueue {
			//std::queue <const char*> msgs;
			std::deque <char*> msgs;
		public:
			MessageQueue();
			~MessageQueue();
			void addBack_so(char* _message);
			void addBack(char* _message);
			void addBack_so(const char* _message);
			void addBack(const char* _message);
			void addFront_so(char* _message);
			void addFront(char* _message);
			void addFront_so(const char* _message);
			void addFront(const char* _message);
			bool isMessage();
			int getNext(char* _buffer, size_t _bufferLength);
		};
	}
}

alx_system_messageQueue.cpp

//Jacob Chesna
//20190926: added buffer reset to getNext - technically not needed...but
//2019_07_09: switched to internally managed memory to accomidate const char & char. Now caller allocates memory for getNext() to copy into
//2019_07_02: changed to int from bool, bool is not blittable
//2019_07_02: changed back to char* and allocated the returned message on the heap (using new)
//2019_02_05: updated to const char* 
//2019_02_08: FUNDAMENTAL CHANGE to <deque> from <queue>, enables estop events to be added to front of queue (Double Ended Queue)


#include "stdafx.h"
#include "alx_system_messageQueue.h"
#include <iostream>

namespace alx {
	namespace system {
		MessageQueue::MessageQueue() {}
		MessageQueue::~MessageQueue() {}
		
		void MessageQueue::addBack_so(char* _message) {
			size_t _strLen = strlen(_message)+1;		//get Length + term char
			char* _buffer = new char[_strLen];			//alocate internal buffer
			memcpy(_buffer, _message, _strLen);			//copy _message
			msgs.push_back(_buffer);					//add pointer to queue				
		}
		void MessageQueue::addBack(char* _message) {
			addBack_so(_message);
			std::cout << _message << std::endl;
		}
		void MessageQueue::addBack_so(const char* _message) {
			size_t _strLen = strlen(_message) + 1;			//get Length + term char
			char* _buffer = new char[_strLen];			//alocate internal buffer
			memcpy(_buffer, _message, _strLen);			//copy _message
			msgs.push_back(_buffer);					//add pointer to queue	
		}
		void MessageQueue::addBack(const char* _message) {
			addBack_so(_message);
			std::cout << _message << std::endl;
		}
		void MessageQueue::addFront_so(char* _message) {
			size_t _strLen = strlen(_message) + 1;			//get Length + term char
			char* _buffer = new char[_strLen];			//alocate internal buffer
			memcpy(_buffer, _message, _strLen);			//copy _message
			msgs.push_front(_buffer);					//add pointer to queue	
		}
		void MessageQueue::addFront(char* _message) {
			addFront_so(_message);
			std::cout << _message << std::endl;
		}
		void MessageQueue::addFront_so(const char* _message) {
			size_t _strLen = strlen(_message) + 1;			//get Length + term char
			char* _buffer = new char[_strLen];			//alocate internal buffer
			memcpy(_buffer, _message, _strLen);			//copy _message
			msgs.push_front(_buffer);					//add pointer to queue	
		}
		void MessageQueue::addFront(const char* _message) {
			addFront_so(_message);
			std::cout << _message << std::endl;
		}
		bool MessageQueue::isMessage() {
			return !msgs.empty();
		}
		int MessageQueue::getNext(char* _buffer, size_t _bufferLength) {
			int retVal = 0;
			if (!msgs.empty()) {
				size_t _msgLen = strlen(msgs.front())+1;					//get Length + term char
				if (_msgLen < _bufferLength) {
					memset(_buffer, '\0', _bufferLength);					//reset buffer
					memcpy(_buffer, msgs.front(), _msgLen);					//copy to local buffer
					retVal = (int)_msgLen;
				}
				else {
					char _eStr[255] = "Error: incomplete msg-> ";			//prepend message with error
					size_t _eLen = strlen(_eStr);								//length of error length, not including term char
					for (size_t i = 0; i < _bufferLength; i++) {
						if (i < _eLen)
							_buffer[i] = _eStr[i];
						else
							_buffer[_eLen+i] = msgs.front()[i];
					}
					_buffer[_bufferLength] = NULL;
					retVal = _bufferLength;
				}
				delete msgs.front();										//delete char's in internal buffer (leaves pointer)
				msgs.pop_front();											//delete message from queue
			}
			return retVal;
		}
	}
}