I have these three.h files that are giving me so many mind-boggling errors, that there must be some fundamental issue that I'm overlooking. My objective is to create an efficient linked list. I realize that there are some logic issues, but right now, I'm more interested in getting it to compile.
Basic Node class:
#pragma once
#include "stdafx.h"
#include <iostream>
#include <deque>
#include <climits>
#include "FreeList.h"
using namespace std;
using namespace System;
using namespace System::Collections;
using namespace System::Collections::Generic;
namespace ListTestProgram
{
generic <typename E> ref class LinkNode
{
private:
LinkNode<E>^ ptr;
FreeList<E>^ freelist;
public:
E element;
LinkNode^ next;
// constructors
LinkNode( E elemval, LinkNode<E>^ nextval = nullptr)
{
element = elemval; next = nextval;
freelist = gcnew FreeList<E>;
}
LinkNode(LinkNode<E>^ nextval = nullptr)
{
next = nextval;
freelist = gcnew FreeList<E>;
}
LinkNode<E>^ newNode()
{
freelist->nextNode();
}
void deleteNode(LinkNode<E>^ ptr)
{
freelist->add((LinkNode<E>^)ptr)->next;
}
};
}
Link class:
// LinkList.h provides for implementation of a linked list
#pragma once
#include "stdafx.h"
#include "LinkNode.h"
#include <iostream>
#include <deque>
#include <climits>
using namespace std;
using namespace System;
using namespace System::Collections;
using namespace System::Collections::Generic;
namespace ListTestProgram
{
generic <typename E> ref class LinkList
{
private:
LinkNode<E>^ head; // pointer to list header
LinkNode<E>^ tail; // pointer to last element
LinkNode<E>^ curr; //access to current element
int cnt; // size of list
static int defaultSize = 100;
void init() // Initializer
{
curr = tail = head = gcnew LinkNode<E>();
cnt = 0;
}
void removal() // Return link nodes to free store
{
while (head != nullptr)
{
curr = head;
head = head->next;
delete curr;
}
}
public:
LinkList<E>(){}
LinkList<E>(int size=defaultSize) {init(); } // Constructor
~LinkList() { removal(); } // Destructor
void print(); // print list contents
void clear() { removal(); init(); } // Clear list
// Insert "it" at current position
void insert (E it)
{
curr->next = gcnew LinkNode<E>(it, curr->next);
if(tail == curr) tail = curr->next; // new tail
cnt++;
}
void append(E it)
{
tail = tail->next = gcnew LinkNode<E> (it, nullptr);
cnt++;
}
// Remove and return current element
E remove()
{
assert(curr->next != nullptr, "No element");
E it = curr->next->element; // Remember value
LinkNode<E>^ ltemp = curr->next; // Rember link node
if(tail == curr->next) tail = curr; // reset tail
curr->next = curr->next->next; // remove from list
delete ltemp;
cnt--;
return it;
}
void moveToNext() // Place curr to head of list
{
curr = head;
}
void moveToEnd() // Place curr at end of list
{
curr = tail;
}
// Move curr one step left; no change if already at front
void prev()
{
if (curr == head) return;
LinkNode<E>^ temp = head;
// March down the list until we find the previous element
while (temp->next!=curr) temp= temp->next;
curr = temp;
}
// Move curr one step right; no change if already at end
void next()
{
if (curr != tail) curr = curr->next;
}
// Return the position of the current element
int currPos()
{
LinkNode<E>^ temp = head;
int i;
for (i = 0; curr != temp; i++)
temp = temp->next;
return i;
}
// Move down list to "pos" position
void moveToPos(int pos)
{
assert((pos>=0)&&(pos<=cnt), "Position out of range.");
curr = head;
for (int i=0; i<pos; i++) curr = curr->next;
}
E getValue()
{
assert(curr->next != nullptr, "No value");
return curr->next->element;
}
};
}
And the FreeList class, which I want to hold nodes that are deleted from the linked list so that they can be reused, avoiding repeated calls to delete and new.
// FreeList.h provides a stack of nodes from which new nodes from the LinkNode class can be
// derived. This is to limit the number of calls to gcnew, to minimize proceccor time. Items in the
// FreeList stack are returned to LinkNode calls when a new node is called for.
#pragma once
#include "stdafx.h"
#include <iostream>
#include <deque>
#include <climits>
#include "LinkNode.h"
using namespace std;
using namespace System;
using namespace System::Collections;
using namespace System::Collections::Generic;
namespace ListTestProgram
{
generic <typename E> ref class FreeList
{
/**** members ****/
public: Stack^ freelist;
/**** constructors / Destructors ****/
FreeList() { freelist = gcnew Stack; }
~FreeList(){}
/**** methods ****/
// return an instance of LinkNode
LinkNode<E>^ nextNode ()
{
if (freelist->Count != 0)
return freelist->Pop();
else
{
repopulateStack();
nextNode(node);
}
}
// repopulate an empty stack
void repopulateStack()
{
for (int i = 0; i < 100; i++)
freelist->Push(gcnew LinkNode<E>());
}
// add a deleted node to the freelist for later use
void add(LinkNode<E>^ trash)
{
freelist->Push(trash);
}
};
}
A few of my errors are:
Error 13 error C2039: 'nextNode' : is not a member of 'ListTestProgram::FreeList<E>' c:\users\ed\documents\visual studio 2012\school_projects\cpp programs\cpp_advanced\testinglists\testinglists\LinkNode.h 43 1 TestingLists
Error 6 error C2061: syntax error : identifier 'LinkNode' c:\users\ed\documents\visual studio 2012\school_projects\cpp programs\cpp_advanced\testinglists\testinglists\FreeList.h 52 1 TestingLists
Error 10 error C2065: 'trash' : undeclared identifier c:\users\ed\documents\visual studio 2012\school_projects\cpp programs\cpp_advanced\testinglists\testinglists\FreeList.h 54 1 TestingLists
Error 3 error C2143: syntax error : missing ';' before '<' c:\users\ed\documents\visual studio 2012\school_projects\cpp programs\cpp_advanced\testinglists\testinglists\FreeList.h 33 1 TestingLists
Error 1 error C2872: 'Stack' : ambiguous symbol c:\users\ed\documents\visual studio 2012\school_projects\cpp programs\cpp_advanced\testinglists\testinglists\FreeList.h 23 1 TestingLists
Error 6 error C2061: syntax error : identifier 'LinkNode' c:\users\ed\documents\visual studio 2012\school_projects\cpp programs\cpp_advanced\testinglists\testinglists\FreeList.h 52 1 TestingLists
Your headers have circular dependencies: FreeList.h depends on LinkNode.h which depends on FreeList.h. Header file dependencies must form a directed, acyclic graph.
Related
I have created an MFC dialog based application to study tab control. In a tab control it is possible to set application specific data to each tab.
I am trying to understand how to set/retrieve the data for individual tabs of the tab control.
Here is a sample application I am creating. Each tab of the control is supposed to store some GPU info.
As I understand, there are 3 steps to add application specific data.
Create a user defined structure, whose 1st member should be of type TCITEMHEADER.
struct GPU {
std::wstring name;
int busid;
};
struct tabData {
TCITEMHEADER tabItemHeader;
GPU gpu;
};
Tell the tab control about the extra bytes, the user defined structure is going to take. This I am doing in DoDataExchange().
int extraBytes = sizeof(tabData) - sizeof(TCITEMHEADER);
auto status = tabCtrl1.SetItemExtra(extraBytes);
Set user defined data while adding tabs.
static int tabCtr = 0;
tabData td;
td.tabItemHeader.pszText = _T("TabX");
td.tabItemHeader.mask = TCIF_TEXT;
td.gpu.name = L"AMD NVIDIA";
td.gpu.busid = 101;
TabCtrl_InsertItem(tabCtrl1.GetSafeHwnd(), tabCtr, &td);
Now to get the data, we simply have to call TabCtrl_GetItem().
tabData td2;
td2.tabItemHeader.pszText = new TCHAR[20];
td2.tabItemHeader.cchTextMax = 20;
td2.tabItemHeader.mask = TCIF_TEXT;
td2.gpu.busid = 0;
TabCtrl_GetItem(tabCtrl1.GetSafeHwnd(), 0, &td2);
But as we can see in the following image. I do get the tab text (pszText member - data Item 1 in image), but not the extra data that I had associated with it previously (Data Items 2 and 3 in image).
Which step am I missing?
Why is the structure associated with application defined data not getting populated?
Additional Info
Here is the complete code for the application.
CPP File:
// tabCtrlStackOverflowDlg.cpp : implementation file
//
#include "stdafx.h"
#include "tabCtrlStackOverflow.h"
#include "tabCtrlStackOverflowDlg.h"
#include "afxdialogex.h"
#include <string>
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
struct GPU {
std::wstring name;
int busid;
};
struct tabData
{
TCITEMHEADER tabItemHeader;
GPU gpu;
};
CtabCtrlStackOverflowDlg::CtabCtrlStackOverflowDlg(CWnd* pParent /*=NULL*/)
: CDialogEx(IDD_TABCTRLSTACKOVERFLOW_DIALOG, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CtabCtrlStackOverflowDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
DDX_Control(pDX, IDC_TAB1, tabCtrl1);
int extraBytes = sizeof(tabData) - sizeof(TCITEMHEADER);
auto status = tabCtrl1.SetItemExtra(extraBytes);
wchar_t *t = status ? L"SetItemExtra() success" : L"SetItemExtra() fail";
GetDlgItem(IDC_STATUSTEXT)->SetWindowTextW(t);
}
BEGIN_MESSAGE_MAP(CtabCtrlStackOverflowDlg, CDialogEx)
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDADDTAB, &CtabCtrlStackOverflowDlg::OnBnClickedAddtab)
ON_BN_CLICKED(IDC_GETITEM0, &CtabCtrlStackOverflowDlg::OnBnClickedGetitem0)
ON_BN_CLICKED(IDCLOSE, &CtabCtrlStackOverflowDlg::OnBnClickedClose)
END_MESSAGE_MAP()
// CtabCtrlStackOverflowDlg message handlers
BOOL CtabCtrlStackOverflowDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
// TODO: Add extra initialization here
return TRUE; // return TRUE unless you set the focus to a control
}
// If you add a minimize button to your dialog, you will need the code below
// to draw the icon. For MFC applications using the document/view model,
// this is automatically done for you by the framework.
void CtabCtrlStackOverflowDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // device context for painting
SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
// Center icon in client rectangle
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// Draw the icon
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialogEx::OnPaint();
}
}
// The system calls this function to obtain the cursor to display while the user drags
// the minimized window.
HCURSOR CtabCtrlStackOverflowDlg::OnQueryDragIcon()
{
return static_cast<HCURSOR>(m_hIcon);
}
void CtabCtrlStackOverflowDlg::OnBnClickedAddtab()
{
static int tabCtr = 0;
tabData td;
td.tabItemHeader.pszText = _T("TabX");
td.tabItemHeader.mask = TCIF_TEXT;
td.gpu.name = L"AMD NVIDIA";
td.gpu.busid = 101;
int status = TabCtrl_InsertItem(tabCtrl1.GetSafeHwnd(), tabCtr, &td);
wchar_t *t = L"";
if (status == -1)
{
t = L"TabCtrl_InsertItem() Fail";
}
else
{
t = L"TabCtrl_InsertItem() success";
}
GetDlgItem(IDC_STATUSTEXT)->SetWindowTextW(t);
tabCtr++;
}
void CtabCtrlStackOverflowDlg::OnBnClickedGetitem0()
{
tabData td2;
td2.tabItemHeader.pszText = new TCHAR[20];
td2.tabItemHeader.cchTextMax = 20;
td2.tabItemHeader.mask = TCIF_TEXT;
td2.gpu.busid = 0;
if (TabCtrl_GetItem(tabCtrl1.GetSafeHwnd(), 0, &td2) == TRUE)
{
std::wstring text = td2.tabItemHeader.pszText;
text += std::wstring(L" ") + td2.gpu.name;
GetDlgItem(IDC_STATUSTEXT)->SetWindowTextW(text.c_str());
}
else
{
GetDlgItem(IDC_STATUSTEXT)->SetWindowTextW(_T("TabCtrl_GetItem()
error"));
}
}
void CtabCtrlStackOverflowDlg::OnBnClickedClose()
{
CDialog::OnCancel();
}
Header File:
// tabCtrlStackOverflowDlg.h : header file
//
#pragma once
#include "afxcmn.h"
// CtabCtrlStackOverflowDlg dialog
class CtabCtrlStackOverflowDlg : public CDialogEx
{
// Construction
public:
CtabCtrlStackOverflowDlg(CWnd* pParent = NULL); // standard constructor
// Dialog Data
#ifdef AFX_DESIGN_TIME
enum { IDD = IDD_TABCTRLSTACKOVERFLOW_DIALOG };
#endif
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
// Implementation
protected:
HICON m_hIcon;
// Generated message map functions
virtual BOOL OnInitDialog();
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
DECLARE_MESSAGE_MAP()
public:
CTabCtrl tabCtrl1;
afx_msg void OnBnClickedAddtab();
afx_msg void OnBnClickedGetitem0();
afx_msg void OnBnClickedClose();
};
Solution Summary
From Barmak Shemirani's answer here are the 3 reasons my code wasn't working. Must read his answer for better understanding.
TCIF_PARAM must be set in mask, while doing TCM_INSERTITEM, and TCM_GETITEM.
I was using local variables created on stack (tabData td2; object). The reference to this variable was becoming invalid as soon as it was going out of scope.
Using std::wstring in the structure being used for TCM_INSERTITEM. It is better to use data types whose size can be accurately be determined (like plain old data types.).
As Barmak Shemirani points out in comments, the documentation for TCITEMHEADER is scarce. His answer provides a thorough explanation.
Conflict with documentation
Documentation for TCITEMHEADER does not mention using TCIF_PARAM flag. Maybe that's a mistake in documention!
It's better if SetItemExtra is moved to OnInitDialog after default procedure is called. This ensures SetItemExtra is called only once when control is empty.
The structure GPU has a std::wstring member whose data size is unknown at the start. TCM_INSERTITEM cannot make a copy of this data unless you have a simple POD structure.
To store the data in the tab, replace std::wstring with wchar_t name[100] so that data is a simple POD structure with fixed size.
struct GPU
{
//std::wstring name;
wchar_t name[100];
int busid;
};
struct tabData
{
TCITEMHEADER tabItemHeader;
GPU gpu;
};
void CMyDialog::OnBnClickedAddtab()
{
int index = tab.GetItemCount();
wchar_t tabname[50];
wsprintf(tabname, L"Tab %d", index);
tabData sdata = { 0 };
sdata.tabItemHeader.mask = TCIF_TEXT | TCIF_PARAM;
sdata.tabItemHeader.pszText = tabname;
wsprintf(sdata.gpu.name, L"AMD NVIDIA %d", index);
sdata.gpu.busid = 101;
tab.SendMessage(TCM_INSERTITEM, index, (LPARAM)(TCITEMHEADER*)(&sdata));
}
void CMyDialog::OnBnClickedGetitem0()
{
int index = tab.GetCurSel();
tabData data = { 0 };
wchar_t buf[20] = { 0 };
data.tabItemHeader.pszText = buf;
data.tabItemHeader.cchTextMax = sizeof(buf)/sizeof(wchar_t);
data.tabItemHeader.mask = TCIF_TEXT | TCIF_PARAM;
if(tab.SendMessage(TCM_GETITEM, index, (LPARAM)(TCITEMHEADER*)(&data)))
{
CString str;
str.Format(L"%d %s", data.gpu.busid, data.gpu.name);
GetDlgItem(IDC_STATIC1)->SetWindowText(str);
}
}
Alternative method:
If std::wstring name; cannot be replaced with wchar_t buffer, we have to define a separate permanent data, for example using std::vector. Then we use the lParam value in TCITEM to point to the vector.
This method only needs the standard 4 bytes of lParam, it doesn't require TCITEMHEADER and SetItemExtra. You can even define std::vector<GPU>. Example:
std::vector<tabData> m_data;
BOOL CMyDialog::OnInitDialog()
{
CDialogEx::OnInitDialog();
tabData data;
data.gpu.name = L"AMD NVIDIA1";
data.gpu.busid = 101;
m_data.push_back(data);
data.gpu.name = L"AMD NVIDIA2";
data.gpu.busid = 102;
m_data.push_back(data);
return TRUE;
}
void CMyDialog::OnBnClickedAddtab()
{
static int tabCtr = 0;
if(tabCtr >= (int)m_data.size())
return;
TCITEM item = { 0 };
item.pszText = _T("TabX");
item.mask = TCIF_TEXT | TCIF_PARAM;
item.lParam = (LPARAM)&m_data[tabCtr];
tab.InsertItem(tabCtr, &item);
tabCtr++;
}
void CMyDialog::OnBnClickedGetitem0()
{
TCITEM item = { 0 };
item.mask = TCIF_TEXT | TCIF_PARAM;
if(tab.GetItem(tab.GetCurSel(), &item) == TRUE)
{
tabData* ptr = (tabData*)item.lParam;
CString str;
str.Format(L"%d %s", ptr->gpu.busid, ptr->gpu.name.c_str());
GetDlgItem(IDC_STATIC1)->SetWindowText(str);
}
}
In my source file when I define my object s, Visual Studios says Identifier "stack" is undefined. I'm pretty sure I've separated the headers correctly but I don't know why I'm getting this error. Also side note when I put everything together in one source file and compile it, it just exits immediately without any reason. Thanks for the help in advance.
This is the source file
// pa3.cpp : Defines the entry point for the console application.
//
#include "stack.h"
#include "stdafx.h"
#include <iostream>
#include <string>
#include <fstream>
//#include <ctype.h>
using namespace std;
int main()
{
//int count;
stack s; //assign s object to stack
string input;
cout << "Please enter the name of the input file: \n";
//cin >> input;
getline(cin, input);
ifstream file(input);
string readline;
//ifstream file(input);
while (getline(file, readline)) //take first line of file and assign to readline
{
s.push(readline); //send it off to push
s.retrieveUPPER();
//file.close();
system("pause");
}
return 0;
}
This is the stack.h file
//#pragma once
#include "stdafx.h"
#include <iostream>
#include <string>
#include <fstream>
//#include <ctype.h>
using namespace std;
class stack
{
public:
int count;
void push(string);
//void pop();
void check(string);
void retrieveUPPER();
private:
static string firstline[1];
static string diskeywords[3];
static char upperword[100];
static char lowerword[100];
static char operatorsarr[100];
static char delimitersarr[100];
};
This is the stack.cpp
#include "stack.h"
#include "stdafx.h"
#include <iostream>
string stack::firstline[1] = { 0 };
string stack::diskeywords[3];
char stack::upperword[100];
char stack::lowerword[100];
char stack::operatorsarr[100];
char stack::delimitersarr[100];
void stack::retrieveUPPER()
{
for (int i = 0; i < 100; i++)
{
cout << upperword[i] << "\n";
}
}
void stack::push(string readline)
{
firstline[0] = readline;
count++;
check(readline);
}
void stack::check(string readline)
{
int length;
char letter;
int leftperenthe = 0;
int rightperenthe = 0;
//int capital;
//int wordFOR;
//int wordBEGIN;
//char keywords[3][8] = { "FOR", "BEGIN", "END" };
char operators[] = "+-=*/;";
char delimiters[] = { ',',';' };
length = readline.length();
for (int i = 0; i < length; i++)
{
for (int j = 0; j < 5; j++)
{
letter = readline[i];
if (isupper(letter)) //if capital letter
{
upperword[i] = letter;
}
else if (islower(letter)) //if lowercase letter
{
lowerword[i] = letter;
}
else if (letter == operators[j]) //if encounters a operator
{
operatorsarr[i] = letter;
}
else if (letter == delimiters[j]) //if encounters a delimiter
{
delimitersarr[i] = letter;
}
else if (letter = ' ') //if encounters a space
{
lowerword[i] = ' ';
operatorsarr[i] = ' ';
delimitersarr[i] = ' ';
}
else if (letter = '(') //if left perenthesis
{
leftperenthe++;
}
else if (letter = ')') //if right perenthesis
{
rightperenthe++;
}
}
}
}
I also had a similar situation:
#include<iostream>
#include<stack.h>
#include<vector>
using namespace std;
int main();
std::vector<int>stickPan (std::vector<int>arr)
{
stack<int>s;
...
}
I used VScode, used mingw-w64, and added paths for * .h files to .json files, but it didn't work.
Errors are reported:
identifier "stack" is undefined.
type name is not allowed.
identifier "s" is undefined
I had a problem while trying to equal one string to another, I get this error "Error reading characters of string." in Visual Studio 2015, in the line that i am doing this "student_name=otherStudent.student_name;" and I just don't get it.
could you please help me?
Student& Student::operator=(const Student &otherStudent) {
if (this != &otherStudent) // Avoid self assignment
{
student_name=otherStudent.student_name;
for (int i = 0; i < grades_size; i++) {
grades[i] = otherStudent.grades[i];
subjet_names[i] = otherStudent.subjet_names[i];
}
average_grade = otherStudent.average_grade;
}
return *this;
}
Here is the definition of the class if it helps:
#include <iostream>
#include <string>
using namespace std;
#define grades_size 6
class Student {
private:
string student_name;
float grades[grades_size];
string subjet_names[grades_size];
float average_grade;
public:
//Constructor
Student();
Student(string name);
//Print status of Student
void printStudent();
//Operator Overloads
Student & operator=(const Student & otherStudent);
};
In a single thread, I have this beautiful class that redirects all cout output to a QTextEdit
#include <iostream>
#include <streambuf>
#include <string>
#include <QScrollBar>
#include "QTextEdit"
#include "QDateTime"
class ThreadLogStream : public std::basic_streambuf<char>, QObject
{
Q_OBJECT
public:
ThreadLogStream(std::ostream &stream) : m_stream(stream)
{
m_old_buf = stream.rdbuf();
stream.rdbuf(this);
}
~ThreadLogStream()
{
// output anything that is left
if (!m_string.empty())
{
log_window->append(m_string.c_str());
}
m_stream.rdbuf(m_old_buf);
}
protected:
virtual int_type overflow(int_type v)
{
if (v == '\n')
{
log_window->append(m_string.c_str());
m_string.erase(m_string.begin(), m_string.end());
}
else
m_string += v;
return v;
}
virtual std::streamsize xsputn(const char *p, std::streamsize n)
{
m_string.append(p, p + n);
long pos = 0;
while (pos != static_cast<long>(std::string::npos))
{
pos = m_string.find('\n');
if (pos != static_cast<long>(std::string::npos))
{
std::string tmp(m_string.begin(), m_string.begin() + pos);
log_window->append(tmp.c_str());
m_string.erase(m_string.begin(), m_string.begin() + pos + 1);
}
}
return n;
}
private:
std::ostream &m_stream;
std::streambuf *m_old_buf;
std::string m_string;
QTextEdit* log_window;
};
However, this doesn't work if ANY thread (QThread) is initiated with a cout. This is because all pointers are messed up, and one has to use signals and slots for allowing transfer of data between the sub-thread and the main thread.
I would like to modify this class to emit a signal rather than write to a text file. This requires that this class becomes a Q_OBJECT and be inherited from one. I tried to inherit from QObject in addition to std::basic_streambuf<char> and added Q_OBJECT macro in the body but it didn't compile.
Could you please help me to achieve this? What should I do to get this class to emit signals that I can connect to and that are thread safe?
For those who need the full "working" answer, here it's. I just copied it because #GraemeRock asked for it.
#ifndef ThreadLogStream_H
#define ThreadLogStream_H
#include <iostream>
#include <streambuf>
#include <string>
#include <QScrollBar>
#include "QTextEdit"
#include "QDateTime"
class ThreadLogStream : public QObject, public std::basic_streambuf<char>
{
Q_OBJECT
public:
ThreadLogStream(std::ostream &stream) : m_stream(stream)
{
m_old_buf = stream.rdbuf();
stream.rdbuf(this);
}
~ThreadLogStream()
{
// output anything that is left
if (!m_string.empty())
{
emit sendLogString(QString::fromStdString(m_string));
}
m_stream.rdbuf(m_old_buf);
}
protected:
virtual int_type overflow(int_type v)
{
if (v == '\n')
{
emit sendLogString(QString::fromStdString(m_string));
m_string.erase(m_string.begin(), m_string.end());
}
else
m_string += v;
return v;
}
virtual std::streamsize xsputn(const char *p, std::streamsize n)
{
m_string.append(p, p + n);
long pos = 0;
while (pos != static_cast<long>(std::string::npos))
{
pos = static_cast<long>(m_string.find('\n'));
if (pos != static_cast<long>(std::string::npos))
{
std::string tmp(m_string.begin(), m_string.begin() + pos);
emit sendLogString(QString::fromStdString(tmp));
m_string.erase(m_string.begin(), m_string.begin() + pos + 1);
}
}
return n;
}
private:
std::ostream &m_stream;
std::streambuf *m_old_buf;
std::string m_string;
signals:
void sendLogString(const QString& str);
};
#endif // ThreadLogStream_H
The derivation needs to happen QObject-first:
class LogStream : public QObject, std::basic_streambuf<char> {
Q_OBJECT
...
};
...
If the goal was to minimally modify your code, there's a simpler way. You don't need to inherit QObject to emit signals iff you know exactly what slots the signals are going to. All you need to do is to invoke the slot in a thread safe way:
QMetaObject::invokeMethod(log_window, "append", Qt::QueuedConnection,
Q_ARG(QString, tmp.c_str()));
To speed things up, you can cache the method so that it doesn't have to be looked up every time:
class LogStream ... {
QPointer<QTextEdit> m_logWindow;
QMetaMethod m_append;
LogStream::LogStream(...) :
m_logWindow(...),
m_append(m_logWindow->metaObject()->method(
m_logWindow->metaObject()->indexOfSlot("append(QString)") )) {
...
}
};
You can then invoke it more efficiently:
m_append.invoke(m_logWindow, Qt::QueuedConnection, Q_ARG(QString, tmp.c_str()));
Finally, whenever you're holding pointers to objects whose lifetimes are not under your control, it's helpful to use QPointer since it never dangles. A QPointer resets itself to 0 when the pointed-to object gets destructed. It will at least prevent you from dereferencing a dangling pointer, since it never dangles.
I wrote some multithreading code using Boost thread library. I initialized two threads in the constructor using the placeholder _1 as the argument required by member function fillSample(int num). But this doesn't compile in my Visual Studio 2010. Following is the code:
#include<boost/thread.hpp>
#include<boost/thread/condition.hpp>
#include<boost/bind/placeholders.hpp>
#define SAMPLING_FREQ 250
#define MAX_NUM_SAMPLES 5*60*SAMPLING_FREQ
#define BUFFER_SIZE 8
class ECG
{
private:
int sample[BUFFER_SIZE];
int sampleIdx;
int readIdx, writeIdx;
boost::thread m_ThreadWrite;
boost::thread m_ThreadRead;
boost::mutex m_Mutex;
boost::condition bufferNotFull, bufferNotEmpty;
public:
ECG();
void fillSample(int num); //get sample from the data stream
void processSample(); //process ECG sample, return the last processed
};
ECG::ECG() : readyFlag(false), sampleIdx(0), readIdx(0), writeIdx(0)
{
m_ThreadWrite=boost::thread((boost::bind(&ECG::fillSample, this, _1)));
m_ThreadRead=boost::thread((boost::bind(&ECG::processSample, this)));
}
void ECG::fillSample(int num)
{
boost::mutex::scoped_lock lock(m_Mutex);
while( (writeIdx-readIdx)%BUFFER_SIZE == BUFFER_SIZE-1 )
{
bufferNotFull.wait(lock);
}
sample[writeIdx] = num;
writeIdx = (writeIdx+1) % BUFFER_SIZE;
bufferNotEmpty.notify_one();
}
void ECG::processSample()
{
boost::mutex::scoped_lock lock(m_Mutex);
while( readIdx == writeIdx )
{
bufferNotEmpty.wait(lock);
}
sample[readIdx] *= 2;
readIdx = (readIdx+1) % BUFFER_SIZE;
++sampleIdx;
bufferNotFull.notify_one();
}
I already included the placeholders.hpp header file but it still doesn't compile. If I replace the _1 with 0, then it will work. But this will initialize the thread function with 0, which is not what I want. Any ideas on how to make this work?
Move the creation to the initialization list:
m_ThreadWrite(boost::bind(&ECG::fillSample, this, _1)), ...
thread object is not copyable, and your compiler doesn't support its move constructor.