Hi, i like to make my class working with exceptions or if needed not working with exception. I like to make them switchable. I made a serial port class and it throws me exceptions as i need. But when i choose to not use them how can i get this elegantly. Currently i "if else" them via a member variable which will be set at the point of creating the object. But that's silly. Is there a better way of doing that?
Thanks
What I have tried:
#pragma once
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <cstdlib>
#include <iostream>
#include <assert.h>
#include <mutex>
#include <string>
#include <sstream>
#include <chrono>
#include <thread>#include <time.h>
#include <fstream>
#ifdef _DEBUG
#define pDebug(msg) (std::cout << msg << std::endl)
#else
#define pDebug(msg) //
#endif
#define MAX_WAIT_TIME 3000
#define BUFFER_LENGTH 200
namespace serial
{
class MSerialException : public std::exception
{
MSerialException& operator=(const MSerialException&) = delete;
std::string e_what_;
public:
MSerialException(std::string file,int line,const char* description) {
std::stringstream ss;
ss << "SerialException - " << description << " failed." << " file " << file << " line " << line;
e_what_ = ss.str();
}
MSerialException(const MSerialException& other) : e_what_(other.e_what_) {}
virtual ~MSerialException() {}
virtual const char* what() const throw () {
return e_what_.c_str();
}
};
typedef struct
{
DCB dcb;
COMMTIMEOUTS timeouts;
}PORT_DEFINITIONS;
class MSerial
{
private:
HANDLE m_fd;
HANDLE m_hEvent;
COMSTAT m_status;
DWORD m_errors;
const WCHAR* comport;
bool m_connected;
bool m_Overlapped;
bool m_Thread;
bool m_WithException;
HANDLE m_hThreadTerm;
HANDLE m_hThreadStarted;
HANDLE m_hDataRx;
HANDLE m_hThread;
OVERLAPPED Overlapped;
LPDWORD lpThreadId;
std::mutex m_read_mutex;
std::mutex m_write_mutex;
char *incoming_Buffer;
std::mutex m_buffer_mutex;
public:
MSerial();
MSerial(BOOL bOverlapped, BOOL bWithException);
virtual ~MSerial();
bool open(const CHAR* comport, PORT_DEFINITIONS settings);
bool open_async(const CHAR* comport, PORT_DEFINITIONS settings);
bool close();
DWORD read(char* inc_msg);
DWORD read(char* inc_msg,
DWORD read_bytes_count
);
int write(const char* data_sent,
DWORD data_sent_length
);
bool WriteEx(LPCVOID lpBuffer,
DWORD dwNumberOfBytesToWrite,
LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
);
bool ReadEx(LPVOID lpBuffer,
DWORD dwNumberOfBytesToRead,
LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
);
static unsigned __stdcall ThreadFn(void* pvParam);
inline bool isOpen() const;
inline void SetDataReadEvent() { SetEvent(m_hDataRx); }
DWORD WaitForReadEvent(DWORD timeout) { return WaitForSingleObject(m_hDataRx, timeout); };
DWORD available();
void flush();
void purge();
};
}
#include "MSerial.h"
#define THROW(message) (throw MSerialException(__FILE__, __LINE__, message))
inline std::string _prefix_port_if_needed(const std::string& input)
{
static std::string windows_com_port_prefix = "\\\\.\\";
if (input.compare(0, windows_com_port_prefix.size(), windows_com_port_prefix) != 0)
{
return windows_com_port_prefix + input;
}
return input;
}
using namespace serial;
MSerial::MSerial()
{
m_errors = 0;
memset(&m_status, 0, sizeof(COMSTAT));
memset(&Overlapped, 0, sizeof(OVERLAPPED));
m_Overlapped = false;
m_connected = false;
m_Thread = false;
m_WithException = false;
m_fd = INVALID_HANDLE_VALUE;
m_hEvent = INVALID_HANDLE_VALUE;
m_hThreadTerm = INVALID_HANDLE_VALUE;
m_hThreadStarted = INVALID_HANDLE_VALUE;
m_hDataRx = INVALID_HANDLE_VALUE;
m_hThread = INVALID_HANDLE_VALUE;
comport = NULL;
incoming_Buffer = NULL;
lpThreadId = NULL;
std::string msg = "constructor for MSerial";
pDebug(msg);
}
MSerial::MSerial(BOOL bOverlapped = false, BOOL bWithException = false)
: m_Overlapped(bOverlapped), m_WithException(bWithException)
{
m_errors = 0;
memset(&m_status,0,sizeof(COMSTAT));
memset(&Overlapped, 0, sizeof(OVERLAPPED));
m_connected = false;
m_Thread = false;
m_fd = INVALID_HANDLE_VALUE;
m_hEvent = INVALID_HANDLE_VALUE;
m_hThreadTerm = INVALID_HANDLE_VALUE;
m_hThreadStarted = INVALID_HANDLE_VALUE;
m_hDataRx = INVALID_HANDLE_VALUE;
m_hThread = INVALID_HANDLE_VALUE;
comport = NULL;
incoming_Buffer = NULL;
lpThreadId = NULL;
std::string msg = "constructor for MSerial";
pDebug(msg);
}
MSerial::~MSerial()
{
CancelIo(m_fd);
if (isOpen())
close();
if (m_Thread)
{
delete incoming_Buffer;
}
std::string msg = "destructor for MSerial";
pDebug(msg);
}
bool MSerial::open(const CHAR* comport, PORT_DEFINITIONS setting)
{
bool ret = false;
if (isOpen())
if (m_WithException)
THROW("Comport already opened.");
else
return false;
if (comport == NULL)
if (m_WithException)
THROW("No comport specified.");
else
return false;
if (setting.dcb.BaudRate == 0)
if (m_WithException)
THROW("No Baud rate specified.");
else
return false;
std::string port_with_prefix = _prefix_port_if_needed(comport);
LPCSTR lp_port = port_with_prefix.c_str();
m_fd = CreateFile(lp_port,
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
m_Overlapped ? FILE_FLAG_OVERLAPPED : FILE_ATTRIBUTE_NORMAL,
NULL);
if (m_fd == INVALID_HANDLE_VALUE)
{
std::stringstream ss;
ss << "Error while opening serial port: " << GetLastError();
if (m_WithException)
THROW(ss.str().c_str());
else
return false;
}
else {
BOOL success = FlushFileBuffers(m_fd);
if (!success)
{
std::stringstream ss;
ss << "Failed to flush serial port: " << GetLastError();
CloseHandle(m_fd);
if (m_WithException)
THROW(ss.str().c_str());
else
return false;
}
DCB dcb = { 0 };
COMMTIMEOUTS timeouts = { 0 };
dcb.DCBlength = sizeof(DCB);
if (GetCommState(m_fd, &dcb) && GetCommTimeouts(m_fd, &timeouts))
{
dcb.BaudRate = setting.dcb.BaudRate;
dcb.ByteSize = setting.dcb.ByteSize;
dcb.Parity = setting.dcb.Parity;
dcb.StopBits = setting.dcb.StopBits;
dcb.fDtrControl = setting.dcb.fDtrControl;
dcb.fRtsControl = setting.dcb.fRtsControl;
dcb.fDsrSensitivity = 0;
dcb.fOutxDsrFlow = 0;
if (!SetCommState(m_fd, &dcb))
{
if (m_WithException)
THROW("Error: could not set serial port params");
else
return false;
}
else
m_connected = true;
if (!m_Overlapped) {
timeouts.ReadIntervalTimeout = setting.timeouts.ReadIntervalTimeout;
timeouts.ReadTotalTimeoutMultiplier = setting.timeouts.ReadTotalTimeoutMultiplier;
timeouts.ReadTotalTimeoutConstant = setting.timeouts.ReadTotalTimeoutConstant;
timeouts.WriteTotalTimeoutMultiplier = setting.timeouts.ReadTotalTimeoutMultiplier;
timeouts.WriteTotalTimeoutConstant = setting.timeouts.WriteTotalTimeoutConstant;
if (!SetCommTimeouts(m_fd, &timeouts))
{
if (m_WithException)
THROW("Error: could not set serial port params");
else
return false;
}
else
m_connected = true;
}
else {
Overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (Overlapped.hEvent == NULL)
{
std::stringstream ss;
ss << "Failed to CreateEvent Reason: " << GetLastError();
if (m_WithException)
THROW(ss.str().c_str());
else
return false;
}
}
}
else {
if (m_WithException)
THROW("Error: Failed to get current serial port params");
else
return false;
}
purge();
}
return m_connected;
}
bool MSerial::open_async(const CHAR* comport, PORT_DEFINITIONS setting)
{
if (isOpen())
if (m_WithException)
THROW("Comport already opened.");
else
return false;
if (comport == NULL)
if (m_WithException)
THROW("No comport specified.");
else
return false;
if (setting.dcb.BaudRate == 0)
if (m_WithException)
THROW("No Baud rate specified.");
else
return false;
if (!m_Overlapped)
THROW("Not specified for overlapped.");
if (open(comport, setting))
{
incoming_Buffer = new char[BUFFER_LENGTH];
if (incoming_Buffer == NULL)
THROW("acquire Buffer failed.");
Overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (Overlapped.hEvent == NULL)
{
std::stringstream ss;
ss << "Failed to CreateEvent Reason: " << GetLastError();
THROW(ss.str().c_str());
}
memset(incoming_Buffer, 0, BUFFER_LENGTH);
m_hThreadTerm = CreateEvent(NULL, TRUE, FALSE, NULL);
m_hThreadStarted = CreateEvent(NULL, TRUE, FALSE, NULL);
if (m_hThreadTerm == INVALID_HANDLE_VALUE && !m_hThreadTerm)
THROW("creation of Events failed.");
if (m_hThreadStarted == INVALID_HANDLE_VALUE && !m_hThreadStarted)
THROW("creation of Events failed.");
m_hThread = (HANDLE)_beginthreadex(0, 0, MSerial::ThreadFn, (void*)this, 0, 0);
DWORD dwWait = WaitForSingleObject(m_hThreadStarted, INFINITE);
if (dwWait != WAIT_OBJECT_0)
THROW("creation of Threads failed.");
m_connected = true;
}
return m_connected;
}
DWORD MSerial::read(char* inc_msg)
{
if (!isOpen())
THROW("MSerial not connected");
std::lock_guard<std::mutex> lock(m_read_mutex);
DWORD bytes_read = 0;
ClearCommError(m_fd, &m_errors, &m_status);
if (m_status.cbInQue > 0) {
if (!ReadFile(m_fd, inc_msg, m_status.cbInQue, &bytes_read, NULL)) {
std::stringstream ss;
ss << "Error while reading from the serial port: " << GetLastError();
THROW(ss.str().c_str());
}
}
return m_status.cbInQue;
}
DWORD MSerial::read(char* inc_msg, DWORD read_bytes_count)
{
if (!isOpen())
THROW("MSerial not connected");
if (read_bytes_count == 0)
return read_bytes_count;
std::lock_guard<std::mutex> lock(m_read_mutex);
DWORD bytes_read = 0;
ClearCommError(m_fd, &m_errors, &m_status);
if (m_status.cbInQue > 0) {
if (!ReadFile(m_fd, inc_msg, read_bytes_count, &bytes_read, NULL)) {
std::stringstream ss;
ss << "Error while reading from the serial port: " << GetLastError();
THROW(ss.str().c_str());
}
}
return bytes_read;
}
int MSerial::write(const char* data_sent,DWORD data_sent_length)
{
if (!isOpen())
THROW("MSerial not connected");
if (data_sent_length == 0)
return data_sent_length;
std::lock_guard<std::mutex> lock(m_write_mutex);
DWORD bytes_sent;
if (!WriteFile(m_fd, (void*)data_sent, data_sent_length, &bytes_sent, NULL)) {
ClearCommError(m_fd, &m_errors, &m_status);
return bytes_sent;
}
else
return bytes_sent;
}
bool MSerial::WriteEx(LPCVOID lpBuffer, DWORD dwNumberOfBytesToWrite,LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
{
if (!m_Overlapped)
THROW("Not specified as overlapped");
if (lpCompletionRoutine == NULL)
THROW("No CompletionRoutine specified");
std::lock_guard<std::mutex> lock(m_write_mutex);
return WriteFileEx(m_fd,lpBuffer,dwNumberOfBytesToWrite,&Overlapped,lpCompletionRoutine);
}
bool MSerial::ReadEx(LPVOID lpBuffer, DWORD dwNumberOfBytesToRead, LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
{
if (!m_Overlapped)
THROW("Not specified as overlapped");
if (lpCompletionRoutine == NULL)
THROW("No CompletionRoutine specified");
std::lock_guard<std::mutex> lock(m_read_mutex);
return ReadFileEx(m_fd, lpBuffer, dwNumberOfBytesToRead, &Overlapped, lpCompletionRoutine);
}
bool MSerial::close()
{
purge();
if (m_connected)
{
if (m_Thread)
{
SignalObjectAndWait(m_hThreadTerm, m_hThread, INFINITE, FALSE);
}
CloseHandle(m_hThreadTerm);
CloseHandle(m_hThreadStarted);
CloseHandle(m_hDataRx);
CloseHandle(m_hThread);
m_connected = false;
CloseHandle(m_fd);
return true;
}
else
return false;
}
bool MSerial::isOpen() const
{
return m_connected;
}
void MSerial::flush()
{
if (!isOpen()) {
THROW("MSerial::flush()");
}
FlushFileBuffers(m_fd);
}
void MSerial::purge()
{
if (!isOpen())
THROW("MSerial::purge()");
if (!PurgeComm(m_fd, PURGE_RXABORT | PURGE_RXCLEAR | PURGE_TXABORT | PURGE_TXCLEAR))
{
std::stringstream ss;
ss << "Error while reseting serial port: " << GetLastError();
THROW(ss.str().c_str());
}
}
DWORD MSerial::available()
{
if (!isOpen())
THROW("MSerial not connected");
COMSTAT cs;
if (!ClearCommError(m_fd, NULL, &cs)) {
std::stringstream ss;
ss << "Error while checking status of the serial port: " << GetLastError();
THROW(ss.str().c_str());
}
return cs.cbInQue;
}
unsigned __stdcall MSerial::ThreadFn(void* pvParam)
{
MSerial* apThis = (MSerial*)pvParam;
bool abContinue = true;
DWORD dwEventMask = 0;
OVERLAPPED ov;
memset(&ov, 0, sizeof(ov));
ov.hEvent = CreateEvent(0, true, 0, 0);
HANDLE arHandles[2];
arHandles[0] = apThis->m_hThreadTerm;
if (ov.hEvent == NULL)
{
std::stringstream ss;
ss << "Error while overlapped event: " << GetLastError();
THROW(ss.str().c_str());
}
if (!SetCommMask(apThis->m_fd, EV_RXCHAR | EV_TXEMPTY))
{
std::stringstream ss;
ss << "Failed to SetCommMask Reason: " << GetLastError();
THROW(ss.str().c_str());
}
apThis->m_hDataRx = CreateEvent(NULL, FALSE, FALSE, NULL);
if (apThis->m_hDataRx == NULL)
{
std::stringstream ss;
ss << "Failed to CreateEvent Reason: " << GetLastError();
THROW(ss.str().c_str());
}
DWORD dwWait;
SetEvent(apThis->m_hThreadStarted);
while (abContinue)
{
BOOL abRet = WaitCommEvent(apThis->m_fd, &dwEventMask, &ov);
if (!abRet)
{
if (GetLastError() != ERROR_IO_PENDING)
{
std::stringstream ss;
ss << "Error while waiting for comm event: " << GetLastError();
THROW(ss.str().c_str());
}
}
arHandles[1] = ov.hEvent;
dwWait = WaitForMultipleObjects(2, arHandles, FALSE, INFINITE);
switch (dwWait)
{
case WAIT_OBJECT_0:
{
_endthreadex(1);
}
break;
case WAIT_OBJECT_0 + 1:
{
DWORD dwMask;
if (GetCommMask(apThis->m_fd, &dwMask))
{
if (dwMask == EV_TXEMPTY)
{
ResetEvent(ov.hEvent);
continue;
}
}
int iAccum = 0;
std::lock_guard<std::mutex> lock(apThis->m_buffer_mutex);
try
{
std::string szDebug;
BOOL abRet = false;
DWORD dwBytesRead = 0;
OVERLAPPED ovRead = { 0 };
memset(&ovRead, 0, sizeof(ovRead));
ovRead.hEvent = CreateEvent(0, true, 0, 0);
if (ovRead.hEvent == NULL)
{
std::stringstream ss;
ss << "Error while overlapped event: " << GetLastError();
THROW(ss.str().c_str());
}
do
{
ResetEvent(ovRead.hEvent);
char szTmp[1];
int iSize = sizeof(szTmp);
memset(szTmp, 0, sizeof szTmp);
abRet = ReadFile(apThis->m_fd, szTmp, sizeof(szTmp), &dwBytesRead, &ovRead);
if (!abRet)
{
abContinue = FALSE;
break;
}
if (dwBytesRead > 0)
{
apThis->incoming_Buffer[iAccum] = *szTmp;
iAccum += dwBytesRead;
}
} while (0);
CloseHandle(ovRead.hEvent);
}
catch (...)
{
std::stringstream ss;
ss << "ThreadFn catched fire";
THROW(ss.str().c_str());
}
if (iAccum > 0)
{
apThis->SetDataReadEvent();
}
ResetEvent(ov.hEvent);
}
break;
}
}
return 0;
}