I am very new to writing in c++ and am working on using pipes to communicate between processes. I have written a very simple program that works when I am sending strings or integers but when I try to send a struct (message in this case) I get null when I try to read it on the other side. Does anyone have some insight into this that they would share? Thanks for your time.
#include <unistd.h>
#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define BUFFER_LEN sizeof(message)
using namespace std;
struct message{
int from;
string msg;
};
void childCode(int *pipeOUT, int *pipeIN, message buffer){
// Local Buffer for input from pipeIN
cout << "Child: Sending Message"<< endl;
buffer.msg = "Child:I am the child!!";
write(pipeOUT[1],(char*) &buffer, BUFFER_LEN); // Test Child -> Parent comms
cout << "Child: Message Sent"<<endl;
read(pipeIN[0],(char*) &buffer,BUFFER_LEN); // Test Child <- Parent comms
cout << "Child: Recieved: "<< buffer.msg << endl;
cout << "Child Exiting..."<< endl;
exit(0); // Child process End
}
int main(int argCount, char** argVector){
pid_t pid;
int childPipeIN[2];
int childPipeOUT[2];
message buffer; // Buffer for reading from pipe
// Make Parent <- Child pipe
int ret = pipe(childPipeIN);
if (ret == -1){
perror("There was an error creating the childPipeIN. Exiting...");
exit(1);
}
// Make Parent -> Child pipe
ret = pipe(childPipeOUT);
if (ret == -1){
perror("There was an error creating the childPipeOUT. Exiting...");
exit(1);
}
// Fork off Child
pid = fork();
if (pid == -1){
perror("There has been an issue forking off the child. Exiting...");
exit(1);
}
if (pid == 0){ // Child code
cout << "Child PID = " << getpid() << endl;
childCode(childPipeIN,childPipeOUT,buffer);
}
else{ // Parent Code
cout << "Parent PID = " << getpid() << endl;
// Test Parent <- Child comms
read(childPipeIN[0], (char*) &buffer, BUFFER_LEN);
cout << "Parent: I recieved this from the child...\n" << buffer.msg << endl;
buffer.msg = "Parent: Got you message!";
// Test Parent -> Child comms
write(childPipeOUT[1], (char*) &buffer, BUFFER_LEN);
wait(null);
cout << "Parent: Children are done. Exiting..." << endl;
}
exit(0);
}
Yeah. I voted to close. Then I read Dupe more closely and realized it didn't explain the problem or the solution very well, and the solution didn't really fit with OP's intent.
The problem:
One does not simply write a std::string into a pipe. std::string is not a trivial piece of data. There are pointers there that do not sleep.
Come to think of it, it's bloody dangerous to write a std::string into anything. Including another std::string. I would not, could not with a file. This smurf is hard to rhyme, so I'll go no further with Dr. Seuss.
To another process, the pointer that references the storage containing the string's data, the magic that allows strings to be resizable, likely means absolutely nothing, and if it does mean something, you can bet it's not something you want to mess with because it certainly isn't the string's data.
Even in the same process in another std::string the two strings cannot peacefully co-exist pointing to the same memory. When one goes out of scope, resizes, or does practically anything else that mutates the string badness will ensue.
Don't believe me? Check BUFFER_LEN. No matter how big your message gets, BUFFER_LEN never changes.
This applies to everything you want to write that isn't a simple hunk of data. Integer, write away. Structure of integers and an array of characters of fixed size, write away. std::vector? No such luck. You can write std::vector::data if and only if whatever it contains is trivial.
std::is_pod may help you decide what you can and cannot read and write the easy way.
Solution:
Serialize the data. Establish a communications protocol that defines the format of the data, then use that protocol as the basis of your reading and writing code.
Typical solutions for moving a string are null terminating the buffer just like in the good ol' days of C and prepending the size of the string to the characters in the string like the good old days of Pascal.
I like the Pascal approach because it allows you to size the receiver's buffer ahead of time. With null termination you have to play a few dozen rounds of Getta-byte looking for the null terminator and hope your buffer's big enough or compound the ugliness with the dynamic allocation and copying that comes with buffer resizes.
Writing is pretty much what you are doing now, but structure member by structure member. In the above case
Write message.from to pipe.
Write length of message.msg to pipe.
Write message.msg.data() to pipe.
Two caveats:
Watch your endian! Firmly establish the byte order used by your protocol. If the native endian does not match the protocol endian, some bit shifting may be required to re-orient the message.
One man's int may be the size of another man's long so use fixed width integers.
Reading is a bit more complicated because a single call to read will return up to the requested length. It may take more than one read to get all the data you need, so you'll want a function that loops until all of the data arrives or cannot arrive because the pipe, file, socket, whatever is closed.
Loop on read until all of message.from has arrived.
Loop on read until all of the length of message.msg has arrived.
Use message.msg.resize(length) to size message.msg to hold the message.
Loop on read until all of message.msg has arrived. You can read the message directly into message.msg.data().
Related
I am using ReadFileEx to read some bytes from a file and using WriteFileEx to write some bytes to a device. This action will repeat till all file bytes are read and written to the device.
Reason I use Ex APIs is because by requesting overlapped IOs to the OS can keep UI thread responsive updating a progress bar while the read/write function is doing their tasks.
The process begins with a ReadFileEx and a MY_OVERLAPPED structure and a ReadCompletionRoutine will be passed in together. Once the read is done, the read completion routine will be called. Inside the routine, a WriteFileEx will be emitted and WriteCompletionRoutine will be called. Inside the write completion routine, another ReadFileEx will be emitted after the offset of the MY_OVERLAPPED structure is reset to next position. That is, two completions will call each other once a read or write is done.
Notice that the above process will only be executed if the calling thread is under alertable state. I use a while loop to keep the thread alertable by keep checking a global state variable is set to TRUE or not. The state variable, completed, will be set to TRUE inside ReadCompletionRoutine once all procedure is done.
FYI, MY_OVERLAPPED structure is a self-define structure that inherits OVERLAPPPED structure so that I can add 2 more information I need to it.
Now, my question is I would like to add a cancel function so that the user can cancel all the process that has been started. What I do is pretty simple. I set the completed variable to TRUE when a cancel button is pressed, so the while loop will break and alertable state will be stoped so the completion routines won't be executed. But, I don't know how to cancel the overlapped request that sent by the Read/WriteFileEx and their completion routines along with the MY_OVERLAPPED structure(see the //******* part in code). Now my code will crash once the cancel button is pressed. The cancel part is the one causing the crash. Please help, thank you.
//In MyClass.h========================================
struct MY_OVERLAPPED: OVERLAPPED {
MyClass *event;
unsigned long long count;
};
//In MyClass.cpp - main===============================
MY_OVERLAPPED overlap;
memset(&overlap, 0,sizeof(overlap));
//point to this class (MyClass), so all variables can later be accessed
overlap.event = this;
//set read position
overlap.Offset = 0;
overlap.OffsetHigh = 0;
overlap.count = 0;
//start the first read io request, read 524288 bytes, which 524288 bytes will be written in ReadCompletionRoutine
ReadFileEx(overlap.event->hSource, overlap.event->data, 524288, &overlap, ReadCompletionRoutine);
while(completed != true) {
updateProgress(overlap.count);
SleepEx(0,TRUE);
}
//********
CancelIo(overlap.event.hSource);
CancelIo(overlap.event.hDevice);
//********
//In MyClass.cpp - CALLBACKs===============================
void CALLBACK ReadCompletionRoutine(DWORD errorCode, DWORD bytestransfered, LPOVERLAPPED lpOverlapped)
{
//type cast to MY_OVERLAPPED
MY_OVERLAPPED *overlap = static_cast<MY_OVERLAPPED*>(lpOverlapped);
//write 524288 bytes and continue to read next 524288 bytes in WriteCompletionRoutine
WriteFileEx(overlap->event->hDevice, overlap->event->data, 524288, overlap, WriteCompletionRoutine);
}
void CALLBACK WriteCompletionRoutine(DWORD errorCode, DWORD bytestransfered, LPOVERLAPPED lpOverlapped)
{
MY_OVERLAPPED *overlap = static_cast<MY_OVERLAPPED*>(lpOverlapped);
if(overlap->count<fileSize/524288) {
//set new offset to 524288*i, i = overlap->count for next block reading
overlap->count = (overlap->count)+1;
LARGE_INTEGER location;
location.QuadPart = 524288*(overlap->count);
overlap->Offset = location.LowPart;
overlap->OffsetHigh = location.HighPart;
ReadFileEx(overlap->event->hSource, overlap->event->data, 524288, overlap, ReadCompletionRoutine);
}
else {
completed = TRUE;
}
}
Note that I prefer not to use multi-thread programming. Other than that, any better way of accomplishing the same goals is appreciated. Please and feel free to provide detail code and explanations. Thanks.
I actually would use a background thread for this, because modern C++ makes this very easy. Much easier, certainly, than what you are trying to do at the moment. So please try to shed any preconceptions you might have that this is the wrong approach for you and please try to read this post in the spirit in which it is intended. Thanks.
First up, here's some very simple proof of concept code which you can compile and run for yourself to try it out. At first sight, this might look a bit 'so what?', but bear with me, I'll explain at the end:
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <thread>
#include <chrono>
#include <memory>
#include <atomic>
int usage ()
{
std::cout << "Usage: copy_file infile outfile\n";
return 255;
}
void copy_file (FILE *infile, FILE *outfile, std::atomic_bool *cancel)
{
constexpr int bufsize = 32768;
std::unique_ptr <char []> buf (new char [bufsize]);
std::cout << "Copying: ";
while (1)
{
if (*cancel)
{
std::cout << "\nCopy cancelled";
break;
}
size_t bytes_read = fread (buf.get (), 1, bufsize, infile);
if (bytes_read == 0)
{
// Check for error here, then break out of the loop
break;
}
size_t bytes_written = fwrite (buf.get (), 1, bytes_read, outfile);
// Again, check for error etc
std::cout << ".";
}
std::cout << "\nCopy complete\n";
// Now probably something like PostMessage here to alert your main loop hat the copy is complete
}
int main (int argc, char **argv)
{
if (argc < 3) return usage ();
FILE *infile = fopen (argv [1], "rb");
if (infile == NULL)
{
std::cout << "Cannot open input file " << argv [1] << "\n";
return 255;
}
FILE *outfile = fopen (argv [2], "wb");
if (outfile == NULL)
{
std::cout << "Cannot open output file " << argv [2] << "\n";
fclose (infile);
return 255;
}
std::atomic_bool cancel = false;
std::thread copy_thread = std::thread (copy_file, infile, outfile, &cancel);
std::this_thread::sleep_for (std::chrono::milliseconds (200));
cancel = true;
copy_thread.join (); // waits for thread to complete
fclose (infile);
fclose (outfile); // + error check!
std::cout << "Program exit\n";
}
And when I run this on my machine, I get something like:
background_copy_test bigfile outfile
Copying:
.....................................................................................
..............
Copy cancelled
Copy complete
Program exit
So, what's noteworthy about this? Well, in no particular order:
It's dead simple.
It's standard C++. There are no Windows calls in there at all (and I did that deliberately, to try to make a point).
It's foolproof.
It 'just works'.
Now of course, you're not going to put your main thread to sleep while you're copying the file in real life. No no no. Instead, you're going to just kick the copy off via std::thread and then put up your 'Copying...' dialog with a Cancel button in it (presumably, this would be a modal dialog)
Then:
If that button is pressed, just set cancel to true and the magic will then happen.
Have copy_file send your 'copying' dialog a WM_APP+nnn message when it is done. It can also do that to have the dialog update its progress bar (I'm leaving all that stuff to you).
Don't omit that call to join() before you destroy copy_thread or it goes out of scope!
What else? Well, to get your head around this properly, study a bit of modern C++. cppreference is a useful site, but you should really read a good book. Then you should be able to apply the lessons learned here to your particular use-case.
Edit: It occurs to me to say that you might do better to create your thread in the WM_INITDIALOG handler for your 'Copying' dialog. Then you can pass the dialog's HWND to copy_file so that it knows where to send those messages to. Just a thought.
And you have a fair bit of reading to do if you're going to profit from this post. But then again, you should. And this post is going to achieve precisely nothing, I fear. Shame.
What I'm trying to do is to send a message to each child and than see which one prints it out first. I've tried using one pipe so my code looks like this:
int main()
{
int pfd[2];
if(pipe(pfd)<0){
perror("pfd error");
exit(1);
}
int n=5;
for(int i=1;i<=n;i++){
pid_t pid=fork();
if(pid<0){
perror("fork error\n");
exit(1);
}
if(pid==0){
close(pfd[1]);
char ms[256];
int h;
read(pfd[0],&h,sizeof(int));
read(pfd[0],ms,h*sizeof(char));
cout<<i<<"_"<<ms<<endl;
close(pfd[0]);
exit(0);
}
if(pid>0){
close(pfd[0]);
}
}
int j=1;
char uzenet[256];
strcpy(uzenet,"start");
int ho=strlen(uzenet);
while(j<=n){
if(write(pfd[1],&ho,sizeof(int))==-1){
perror("write error");
exit(1);
}
if(write(pfd[1],uzenet,ho*sizeof(char))==-1){
perror("write error");
exit(1);
}
j++;
}
close(pfd[1]);
while(wait(NULL)>0){};
exit(0);
}
And it prints out this:
2_
1_start
4_
3_
5_
But what I want is this:
2_start
1_start
4_start
3_start
5_start
You can't with only one pipe!
Data read from a pipe is consumed, that means that once something is read from a pipe it will never be available, it disappears from the pipe (think about water and pipe, drinking consumes the water).
If you share the reading part of a pipe in between different processes, then they will be concurrent. That means that you can't have any guaranty on who will read some data. The system is able to choose any reader he wants among all readers that request reading at some point. In the worst case, one process would read everything. In general, you will have a kind of random choice. It is not random, but almost impossible to control (and surely a bad idea to try). At least don't think about replicate N times the message on the writing part, and hope that the N readers will be able to read one copy each.
pipes cannot be used to broadcast something. If you want it, implement your own broadcasting system.
---ADD---
Also don't forget that pipe data have no semantic, there is no concept of messages inside, if you need it you'll have to implement some protocol to simulate it. I mean that you can't flood the pipe with liters and drink drops, or the converse...
I'm creating a simple HACK for educational purpose only. A simple Triggerbot that reads from memory the value of player aiming to enemy YES = 1 or NO = 0. I have made some other similar HACKS however I never found this problem .. in Rainbow Six Siege I have the memory address both static or dynamic however cheat engine read it well but when I try to read it from my C++ Program it does't work. Not sure why if it had work with other games. I'm new to this and maybe I did something wrong.
#include "stdafx.h"
#include <iostream>
#include <windows.h>
#define F6Key 0x75
using namespace std ;
int value ;
int main()
{
cout << "Open Game .." << endl ;
system("Pause") ;
LPCWSTR gameone = L"Rainbow Six";
HWND hwnd = FindWindow(0, gameone);
if (gameone == 0)
{
cout << "Not Found." << endl ;
system("Pause") ;
}
else
{
cout << "Success finding game." << endl;
DWORD processid ;
GetWindowThreadProcessId(hwnd, &processid) ;
HANDLE process = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processid) ;
cout << processid ;
if (!ReadProcessMemory(process, (void *)0x25421AD9D6C, (void *)&value, sizeof(value), NULL)) {
cout << "Unable to read memory proccess";
}
else {
cout << "\n Read: " << value;
}
system("Pause");
}
return 0 ;
}
Here is the code simple Find the Window by name, gets its PID fine no problem. OpenProcess then when I call the method ReadProcessMemory with the process, address pointer value by parameter is unable to read it print the if condition and never the else of value read.
If I remove the function from the If condition just for testing if at least points to something it gives some random values... is weird that I'm unable to read memory It always work ::(
Can someone help me out? It is some king of security software or something?
First of all, you have to check OpenProcess return value. If your process does not have sufficient rights, it will return NULL and any attempts to use this handle will fail. Use GetLastError function to determine error reason.
Most likely it will be ERROR_ACCESS_DENIED.
Secondary, to successfully access external process memory space, you should open its handle with PROCESS_VM_READ right or enable seDebugPrivilege for you process token. Example how to do that you could see in the MSDN.
And lastly. If memory address (0x25421AD9D6C in your case) is invalid, ReadProcessMemory will fail. In that case value variable would not be initialized and any attempts to use it is an undefined behavior.
Also, if you managed to get process handle, it should be closed using CloseHandle function when you finish using it.
Upd: If ReadProcessMemory returns FALSE and GetLastError - ERROR_PARTIAL_COPY that means that a page fault has occured, you are trying to read from a buffer and at least part of it is not assigned to the physical memory. If you know your value offset, get module load address using PSAPI GetModuleInformation function and add offset to the lpBaseOfDll field of the MODULEINFO structure.
I am trying to write a c++ program for my linux machine that can interact with some instrumentation that responds to simple ascii commands. The problem I'm running into, I would think, would be a fairly common request but my searches of various forums came up with nothing quite the same.
My problem is this: When I connect to the instrument, due to some communication issues, it often pukes up a bunch of data of varying length that I don't want. The data the machine prints has line endings with '\r'. I have been trying to write a simple loop what will keep reading and ignoring data until the machine is quiet for two seconds, then carry on to perform some data requests once the storm is over.
When searching forums, I found gobs and gobs of threads about cin.ignore, cin.sync, getline and cin.getline. These all seemed quite useful but when I attempted to implement them in a way that should be simple, they never behaved quite as I expected them to.
I apologize in advance if this is a duplicate post as I would have thought I wasn't the first person to want to throw away garbage input but I have found no such post.
The code I have been trying a few different arrangements of looks something like this:
sleep(2);
cin.clear();
while ( cin.peek() != char_traits<char>::eof()) {
//cin.sync();
//cin.ignore(numeric_limits<streamsize>::max(),char_traits<char>::eof());
cin.clear();
char tmp[1];
while ( cin.getline(tmp,80,'\r') ) {}
cin.clear();
sleep(2);
}
I understand from my searches that doing some sort of while(!cin.eof()) is bad practice but tried it anyway for grins as well as while(getline(cin,str,'\r')) and while(cin.ignore()). I am at a loss here as there is clearly something I'm missing.
Thoughts?
EDIT: --final code--
Alright! This did it! Thanks for point me to termios #MatsPetersson! I wound up stealing quite a lot of your code, but I'm glad I had the opportunity to figure out what was going on. This website helped me make sense of the tcassert manual page: http://en.wikibooks.org/wiki/Serial_Programming/termios
#include <cstdlib>
#include <iostream>
#include <stdio.h>
#include <unistd.h>
#include <limits>
#include <termios.h>
#include <errno.h>
#include <cassert>
using namespace std;
const int STDIN_HANDLE=fileno(stdin);
int main()
{
string str;
//Configuring terminal behavior
termios tios, original;
assert( tcgetattr(STDIN_HANDLE, &tios)==0 );
original = tios;
tios.c_lflag &= ~ICANON; // Don't read a whole line at a time.
tios.c_cc[VTIME] = 20; // 0.5 second timeout.
tios.c_cc[VMIN] = 0; // Read single character at a time.
assert( tcsetattr(STDIN_HANDLE, TCSAFLUSH, &tios)==0 );
const int size=999; //numeric_limits<streamsize>::max() turns out to be too big.
char tmp[size];
int res;
cerr << "---------------STDIN_HANDLE=" << STDIN_HANDLE << endl;
cerr << "---------------enter loop" << endl;
while ( res=read(STDIN_HANDLE, tmp, sizeof(tmp)) ) {
cerr << "----read: " << tmp << endl;
}
cerr << "--------------exit loop" << endl;
cout << "END";
assert( tcsetattr(STDIN_HANDLE, TCSANOW, &original)==0 );
return 0;
}
That wasn't as bad as I began to fear it would be! Works perfectly! Obviously all the cerr << -- lines are not necessary. As well as some of the #include's but I'll use them in the full program so I left them in for my own purposes.
Well... It mostly works anyway. It works fine so long as I don't redirect the stdio for the program to a tcp-ip address using socat. Then it gives me a "Not a Typewriter" error which is what I guess happens when it attempts to control something that isn't a tty. That sounds like a different question though, so I'll have to leave it here and start again I guess.
Thanks folks!
Here's a quick sample of how to do console input (and can easily be adapted to do input from another input source, such as a serial port).
Note that it's hard to "type fast enough" for this to read more than one character at a time, but if you copy'n'paste, it will indeed read 256 characters at once, so assuming your machine that you are connecting to is indeed feeding out a large amount of stuff, it should work just fine to read large-ish chunks - I tested it by marking a region in one window, and middle-button-clicking in the window running this code.
I have added SOME comments, but for FULL details, you need to do man tcsetattr - there are a whole lot of settings that may or may not help you. This is configured to read data of "any" kind, and exit if you hit escape (it also exits if you hit an arrow-key or similar, because those translate to an ESC-something sequence, and thus will trigger the "exit" functionality. It's a good idea to not crash out of, or set up some handler to restore the terminal behaviour, as if you do accidentally exit before you've restored to original setting, the console will act a tad weird.
#include <termios.h>
#include <unistd.h>
#include <cassert>
#include <iostream>
const int STDIN_HANDLE = 0;
int main()
{
termios tios, original;
int status;
status = tcgetattr(STDIN_HANDLE, &tios);
assert(status >= 0);
original = tios;
// Set some input flags
tios.c_iflag &= ~IXOFF; // Turn off XON/XOFF...
tios.c_iflag &= ~INLCR; // Don't translate NL to CR.
// Set some output flags
// tios.c_oflag = ... // not needed, I think.
// Local modes flags.
tios.c_lflag &= ~ISIG; // Don't signal on CTRL-C, CTRL-Z, etc.
tios.c_lflag &= ~ICANON; // Don't read a whole line at a time.
tios.c_lflag &= ~(ECHO | ECHOE | ECHOK); // Don't show the input.
// Set some other parameters
tios.c_cc[VTIME] = 5; // 0.5 second timeout.
tios.c_cc[VMIN] = 0; // Read single character at a time.
status = tcsetattr(STDIN_HANDLE, TCSANOW, &tios);
assert(status >= 0);
char buffer[256];
int tocount = 0;
for(;;)
{
int count = read(STDIN_HANDLE, buffer, sizeof(buffer));
if (count < 0)
{
std::cout << "Error..." << std::endl;
break;
}
if (count == 0)
{
// No input for VTIME * 0.1s.
tocount++;
if (tocount > 5)
{
std::cout << "Hmmm. No input for a bit..." << std::endl;
tocount = 0;
}
}
else
{
tocount = 0;
if (buffer[0]== 27) // Escape
{
break;
}
for(int i = 0; i < count; i++)
{
std::cout << std::hex << (unsigned)buffer[i] << " ";
if (!(i % 16))
{
std::cout << std::endl;
}
}
std::cout << std::endl;
}
}
status = tcsetattr(STDIN_HANDLE, TCSANOW, &original);
return 0;
}
If your instrumentation offers a stream interface, and assuming that it would wait before returning whenever no input is available, I'd suggest to simply use :
cin.ignore(numeric_limits<streamsize>::max(),'\r'); // ignore everything until '\r'
Another alternative could be to use poll, which provides a mechanism for multiplexing (and waiting for) input/output over a set of file descriptors. This has the advantage of letting you read several instrumentation devices if you'd need.
I have 2 threads: one of them is constantly cout'ing to the console some value, let's say increments an int value every second - so every second on the console is 1,2,3... and so on.
Another thread is waiting for user input - with the command cin.
Here is my problem: when I start typing something, when the time comes to cout the int value, my input gets erased from the input field, and put into the console with the int value. So when I want to type in "hello" it looks something like this:
1
2
3
he4
l5
lo6
7
8
Is there a way to prevent my input from getting put to the console, while other thread is writing to the console?
FYI this is needed for a chat app at client side - one thread is listening for messages and outputs this message as soon as it comes in, and the other thread is listening for user input to be sent to a server app.
Usually the terminal itself echos the keys typed. You can turn this off and get your program to echo it. This question will give you pointers on how to do it Hide password input on terminal
You can then just get the one thread to handle output.
If you are a slow typer, then the solution to your problem can be, as I said, making it a single thread, but that may make the app to receive only after it sends.
Another way is to increase your receiving thread's sleep time, which would provide you some more time to type (without interruption)
You could make a GUI (or use ncurses if you really want to work in the console). This way you avoid having std::cout shared by the threads.
I think you could solve this problem with a semaphore. When you have an incoming message you check to see if the user is writing something. If he does you wait until he finishes to print the message.
Is there a way to prevent my input from getting put to the console, while other thread is writing to the console?
It is the other way around. The other thread shouldn't interrupt the display of what you are typing.
Say you have typed "Hel" and then a new message comes in from the other thread. What do you do? How should it be displayed?
Totally disable echoing of what you type and only display it after you hit enter. In this way you can display messages from the different threads properly, in an atomic fashion. The big drawback is that you cannot see what you have typed already... :(
You immediately echo what you type. When the new message comes in, you undo the "Hel", print the new message and print again "Hel" on a new line and you can continue typing. Doable but a bit ugly.
You echo what you type in a separate place. That is, you split somehow the display. In one place you display the posted/received messages in order; and in another place you display what you are typing. You either need a GUI or at least some console library to do this. This would be the nicest solution but perhaps the most difficult to port to another OS due to the library dependencies.
In any case, you need a (preferably internally) synchronized stream that you can safely call from different threads and can write strings into it atomically. That is, you need to write your own synchronized stream class.
Hope this helps.
Well i recently solved this same issue with a basic workaround. This might not be the #1 solution but worked like a charm for me, as a newbie;
#include <iostream> // I/O
#include <Windows.h> // Sleep();
#include <conio.h> // _getch();
#include <string> // MessageBuffer
#include <thread> // Thread
using namespace std;
void ThreadedOutput();
string MessageBuffer; // or make it static
void main()
{
thread output(ThreadedOutput); // Attach the output thread
int count = 0;
char cur = 'a'; // Temporary at start
while (cur != '\r')
{
cur = _getch(); // Take 1 input
if (cur >= 32 && cur <= 126) // Check if input lies in alphanumeric and special keys
{
MessageBuffer += cur; // Store input in buffer
cout << cur; // Output the value user entered
count++;
}
else if (cur == 8) // If input key was backspace
{
cout << "\b \b"; // Move cursor 1 step back, overwrite previous character with space, move cursor 1 step back
MessageBuffer = MessageBuffer.substr(0, MessageBuffer.size() - 1); // Remove last character from buffer
count--;
}
else if (cur == 13) // If input was 'return' key
{
for (int i = 0; i < (signed)MessageBuffer.length(); i++) // Remove the written input
cout << "\b \b";
// "MessageBuffer" has your input, use it somewhere
MessageBuffer = ""; // Clear the buffer
}
}
output.join(); // Join the thread
}
void ThreadedOutput()
{
int i = 0;
while (true)
{
for (int i = 0; i < (signed)MessageBuffer.length(); i++) // Remove the written input
cout << "\b \b";
cout << ++i << endl; // Give parallel output with input
cout << MessageBuffer; // Rewrite the stored buffer
Sleep(1000); // Prevent this example spam
}
}