How to interact synchroniously with a process in Boost.Process 0.5 - c++

This is a question about Boost.Process 0.5, not any later or earlier
version, Boost now contains a Boost.Process library with a different syntax and features.
Suppose I have a simple program that ask for a number and return another number, namely:
// ask.x, simple program with IO
#include<iostream>
int main(){
double n;
std::cout << "number?" << std::endl;
std::cin >> n;
std::cout << n + 1 << std::endl;
}
Now I want to interact with this program programmaticaly by means of Boost.Process 0.5 (http://www.highscore.de/boost/process0.5/boost_process/tutorial.html). When I try to use the library I don't get the expected behavior, the number is never sent to the program. (reading the first line is ok). I tried to write a generalization of the example described in http://www.highscore.de/boost/process0.5/boost_process/tutorial.html#boost_process.tutorial.synchronous_i_o but I failed.
MWE, the first half is a lot of necessary boilerplate, also where I think I make the mistake.
#include <boost/process.hpp> // version 0.5
#include <boost/iostreams/device/file_descriptor.hpp>
#include <boost/iostreams/stream.hpp>
#include <string>
#define ALSOSEND // for fully interactive case
using namespace boost;
int main() {
// Boilerplate code, see input only example here https://stackoverflow.com/questions/12329065/how-to-bind-program-termination-with-end-of-stream-in-boost-process-0-5
process::pipe pi = boost::process::create_pipe();
process::pipe po = boost::process::create_pipe();
{
iostreams::file_descriptor_sink sink(
pi.sink,
iostreams::close_handle
);
iostreams::file_descriptor_source source(
po.source,
boost::iostreams::close_handle
);
process::execute(
process::initializers::run_exe("./ask.x"),
process::initializers::bind_stdout(sink)
#ifdef ALSOSEND
, process::initializers::bind_stdin(source)
#endif
);
}
iostreams::file_descriptor_source fdsource(pi.source, iostreams::close_handle);
iostreams::stream<iostreams::file_descriptor_source> is(fdsource);
iostreams::file_descriptor_sink fdsink(po.source, iostreams::close_handle);
iostreams::stream<iostreams::file_descriptor_sink> os(fdsink);
// actual interaction with the process
std::string line;
std::getline(is, line);
assert(line == "number?");
std::cout << "sending: " << "5" << std::endl;
os << "5" << std::endl; // RUN GETS STUCK HERE
std::getline(is, line);
assert(line == "6");
}
Obviously I don't understand the logic of sinks and sources. I also tried using a single pipe for sink and source but it didn't work.
How can I make the program both read and write from and to the executed project?
I cannot find an example which both input and output are interleaved.
EDIT to show the working example with an earlier version of the library
This how it used to be done in Boost.Process GSOC2010 (not 0.5 as
in the question above), note that the actual interaction with the
program is the same as above.
#include <boost/filesystem.hpp> // quasibug in process GSOC2010 needs to include filesystem BEFORE
#include <boost/process.hpp> // version GSOC2010 (not 0.5)
#include <boost/iostreams/device/file_descriptor.hpp>
#include <boost/iostreams/stream.hpp>
#include <string>
using namespace boost;
int main() {
// boiler plate code
std::vector<std::string> args;
process::context ctx;
ctx.process_name = "askprocess";
ctx.streams[process::stdout_id] = boost::process::behavior::pipe();
ctx.streams[process::stdin_id ] = boost::process::behavior::pipe();
process::child c = create_child("./ask", args, ctx);
process::pistream is(c.get_handle(process::stdout_id));
process::postream os(c.get_handle(process::stdin_id ));
// actual interaction with the process
std::string line;
std::getline(is, line);
assert(line == "number?");
std::cout << "sending: " << "5" << std::endl;
os << "5" << std::endl; // RUN GETS STUCK HERE
std::getline(is, line);
assert(line == "6");
}

Seems to be a typo in the following line:
iostreams::file_descriptor_sink fdsink(po.source, iostreams::close_handle);
Must be:
iostreams::file_descriptor_sink fdsink(po.sink, iostreams::close_handle);

Related

How do I redirect stderr to /dev/null in C++?

#include <cstddef>
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main() {
//read the lines from the piped file using cin
string response;
int i = 0;
while (getline(cin, response)) {
//if the response is empty, stop
if (response.empty()) {
break;
}
//Write each odd line (1,3, etc..) to stderr (cerr)
//Write each even line (2,4. etc..) to stdout (cout)
if (i % 2 != 1) { //send odd to stderr
cerr << "err: " << response << endl;
}
else { //send even to stdout
cout << "out: " << response << endl;
}
i++;
}
return 0;
}
I want to redirect stderr to /dev/null, how would I go about doing so? I'm new to C++ and trying to learn by practicing, however, I'm not easily able to find an answer that fits my existing program.
Besides the excellent commentary above, it is pretty easy to make a “null” streambuf sink in C++.
#include <iostream>
struct null_streambuf: public std::streambuf
{
using int_type = std::streambuf::int_type;
using traits = std::streambuf::traits_type;
virtual int_type overflow( int_type value ) override
{
return value;
}
};
To use it, just set the rdbuf:
int main()
{
std::cerr.rdbuf( new null_streambuf );
std::cerr << "Does not print!\n";
}
If you wish to be able to turn it off and on, you will have to remember the original and restore it, not forgetting to delete the new null_streambuf.
int main()
{
std::cerr << "Prints!\n";
auto original_cerr_streambuf = std::cerr.rdbuf( new null_streambuf );
std::cerr << "Does not print.\n";
delete std::cerr.rdbuf( original_cerr_streambuf );
std::cerr << "Prints again!\n";
}
This does have the objective effect of being compiled to code, which I suspect is the advantage you are looking for: the ability to dynamically enable and disable diagnostic output.
This is, however, the usual function of a debug build, where you use the DEBUG macro to decide whether or not to compile something (such as error output operations) into the final executable.
Keep in mind that this does not disable output on standard error via other means, but only through cerr.

How do I read the output of a batch file into a string in C++

I'm trying to make a small program that will create a Batch file, do something in it and then return a string from it, and after it delete the Batch.
I would like to store the output of the batch file in the variable line.
I tried using getline() but I think it work with .txt files only. I can be wrong.
#include <iostream>
#include <fstream>
#include <stdlib.h>
#include <string>
using namespace std;
int main(int argc, char *argv[]) {
ofstream batch;
string line;
batch.open("temp.bat", ios::out);
batch <<"#echo OFF\nwmic os get caption /value\nwmic path win32_videocontroller get description /value\npause\nexit";
batch.close();
system("temp.bat");
remove("temp.bat");
};
In my code I simply using system with my Batch file. I'd like to use cout<<line.
I expect string called line would be equal to the output of the Batch file.
You need to redirect output when using system():
#include <cstdio> // std::remove(const char*)
#include <cstdlib> // std::system(const char*)
#include <fstream>
#include <iostream>
#include <string>
#include <unordered_map>
int main()
{
std::string foo_bat = "foo.bat";
std::string foo_out = "foo.out";
// Write the batch file
{
std::ofstream f( foo_bat );
f << R"z(
#echo off
wmic os get caption /value
wmic path win32_videocontroller get description /value
)z";
}
// Execute the batch file, redirecting output using the current (narrow) code page
if (!!std::system( (foo_bat + " | find /v \"\" > " + foo_out + " 2> NUL").c_str() ))
{
// (Clean up and complain)
std::remove( foo_bat.c_str() );
std::remove( foo_out.c_str() );
std::cout << "fooey!\n";
return 1;
}
// Read the redirected output file
std::unordered_map <std::string, std::string> env;
{
std::ifstream f( foo_out );
std::string s;
while (getline( f >> std::ws, s ))
{
auto n = s.find( '=' );
if (n != s.npos)
env[ s.substr( 0, n ) ] = s.substr( n+1 );
}
}
// Clean up
std::remove( foo_bat.c_str() );
std::remove( foo_out.c_str() );
// Show the user what we got
for (auto p : env)
std::cout << p.first << " : " << p.second << "\n";
}
WMIC is a problematic program when it comes to controlling the output code page, hence the weird pipe trick we use with system().
But, after all that, you should use the WMI API directly to get this kind of information.
A possible, though admittedly not ideal solution would be to have the batch file write it's output to a .txt file and then read in that file into your program. Look at this SO thread to see how to do this.

Run-Time Check Failure #2 dissapears when I define a completely new variable int i = 0 without using it

When I run this code I get the error:
Run-Time Check Failure #2 - Stack around the variable 'line' was corrupted.
#define WIN32_LEAN_AND_MEAN
#pragma comment(lib, "ws2_32.lib")
#include <winsock2.h>
#include <ws2tcpip.h>
#include "VisionUtils.h"
#include <fstream>
#include <iostream>
#include <string>
using namespace std;
int main(void)
{
static std::string dm_ext = ".xml";
std::ifstream testFileNames("training_data\\testFiles.txt");
std::string line;
//int i = 0;
std::cout << "Main loop start" << std::endl;
if (testFileNames.is_open()){
while (std::getline(testFileNames, line))//main loop
{
cout << line << endl;
cout << "training_data\\depth\\" + line + dm_ext << endl;
cv::FileStorage depthData("training_data\\depth\\" + line + dm_ext, cv::FileStorage::READ);
}
}
return 0;
}
Now if I simply define int i = 0 before the loop, the error disappears
int main(void)
{
static std::string dm_ext = ".xml";
std::ifstream testFileNames("training_data\\testFiles.txt");
std::string line;
int i = 0;
std::cout << "Main loop start" << std::endl;
if (testFileNames.is_open()){
while (std::getline(testFileNames, line))//main loop
{
cout << line << endl;
cout << "training_data\\depth\\" + line + dm_ext << endl;
cv::FileStorage depthData("training_data\\depth\\" + line + dm_ext, cv::FileStorage::READ);
}
}
return 0;
}
I am trying to adapt and improve a code that a previous engineer has created and I am not quite familiar with it yet. The code seems to be using an older version of opencv2, though I haven't been able to determine which one. I plan on upgrading to the current version of opencv in the future but I need to do some performance evaluation using the old code and am there fore bound to using that for now.
I would be glad to hear from anyone if they have an idea as to what is causing this weird behavior and how to fix the error I am having.
This is my first post on stackoverflow so please let me know if you see anything that I can improve on in my post.

When boost library "interprocess" defines a named_mutex do those named_mutexes work properly between different processes, or only with threads?

I think I must be assuming something from the name boost::interprocess that is not true.
The documents repeat that named_mutex is global here.
I am unable to make it work though. Two copies of the same executable should be run at the same time, and I expect that a named mutex in a library named boost::interprocess might actually BLOCK sometimes. It doesn't. It also doesn't prevent data file corruption in the code below.
Here's some code from the boost docs:
#include <boost/interprocess/sync/scoped_lock.hpp>
#include <boost/interprocess/sync/named_mutex.hpp>
#include <fstream>
#include <iostream>
#include <cstdio>
int main ()
{
using namespace boost::interprocess;
try{
struct file_remove
{
file_remove() { std::remove("file_name"); }
~file_remove(){ std::remove("file_name"); }
} file_remover;
struct mutex_remove
{
mutex_remove() { named_mutex::remove("fstream_named_mutex"); }
~mutex_remove(){ named_mutex::remove("fstream_named_mutex"); }
} remover;
//Open or create the named mutex
named_mutex mutex(open_or_create, "fstream_named_mutex");
std::ofstream file("file_name");
for(int i = 0; i < 10; ++i){
//Do some operations...
//Write to file atomically
scoped_lock<named_mutex> lock(mutex);
file << "Process name, ";
file << "This is iteration #" << i;
file << std::endl;
}
}
catch(interprocess_exception &ex){
std::cout << ex.what() << std::endl;
return 1;
}
return 0;
Here's what I did to it so I could prove to myself the mutex was doing something:
#include <windows.h>
#include <boost/interprocess/sync/interprocess_mutex.hpp>
#include <boost/lambda/lambda.hpp>
#include <boost/interprocess/sync/scoped_lock.hpp>
#include <boost/interprocess/sync/named_mutex.hpp>
#include <iostream>
#include <iterator>
#include <algorithm>
#include <fstream>
#include <iostream>
#include <cstdio>
int main (int argc, char *argv[])
{
srand((unsigned) time(NULL));
using namespace boost::interprocess;
try{
/*
struct file_remove
{
file_remove() { std::remove("file_name"); }
~file_remove(){ std::remove("file_name"); }
} file_remover;
*/
struct mutex_remove
{
mutex_remove() { named_mutex::remove("fstream_named_mutex"); }
~mutex_remove(){ named_mutex::remove("fstream_named_mutex"); }
} remover;
//Open or create the named mutex
named_mutex mutex(open_or_create, "fstream_named_mutex");
std::ofstream file("file_name");
for(int i = 0; i < 100; ++i){
//Do some operations...
//Write to file atomically
DWORD n1,n2;
n1 = GetTickCount();
scoped_lock<named_mutex> lock(mutex);
n2 = GetTickCount();
std::cout << "took " << (n2-n1) << " msec to acquire mutex";
int randomtime = rand()%10;
if (randomtime<1)
randomtime = 1;
Sleep(randomtime*100);
std::cout << " ... writing...\n";
if (argc>1)
file << argv[1];
else
file << "SOMETHING";
file << " This is iteration #" << i;
file << std::endl;
file.flush(); // added in case this explains the corruption, it does not.
}
}
catch(interprocess_exception &ex){
std::cout << "ERROR " << ex.what() << std::endl;
return 1;
}
return 0;
}
Console Output:
took 0 msec to acquire mutex ... writing...
took 0 msec to acquire mutex ... writing...
took 0 msec to acquire mutex ... writing...
took 0 msec to acquire mutex ... writing...
Also, the demo writes to a file, which if you run two copies of the program will be missing some data.
I expect that if I delete file_name and run two copies of the program, I should get interleaved writes to file_name containing 100 rows from each instance.
(Note, that the demo code is clearly not using an ofstream in append mode, instead it simply rewrites the file each time this program runs, so if we wanted a demo to show two processes writing to a file, I'm aware of that reason why it wouldn't work, but what I did expect is for the above code to be a feasible demonstration of mutual exclusion, which it is not. Also calls to a very handy and aptly named ofstream::flush() method could have been included, and weren't.)
Using Boost 1.53 on Visual C++ 2008
It turns out that Boost is a wonderful library, and it code examples interspersed in the documentation may sometimes be broken. At least the one for boost::interprocess::named_mutex in the docs is not functional on Windows systems.
*Always deleting a mutex as part of the demo code causes the mutex to not function. *
That should be commented in the demo code at the very least. It fails to pass the "principle of least amazement", although I wondered why it was there, I thought it must be idiomatic and necessary, it's idiotic and unnecessary, in actual fact. Or if it's necessary it's an example of what Joel Spolsky would call a leaky abstraction. If mutexes are really filesystem points under C:\ProgramData in Windows I sure don't want to know about it, or know that turds get left behind that will break the abstraction if I don't detect that case and clean it up. (Sure smells like posix friendly semantics for mutexes in Boost have caused them to use a posix-style implementation instead of going to Win32 API directly and implementing a simple mutex that has no filesystem turds.)
Here's a working demo:
#include <windows.h>
#include <boost/interprocess/sync/interprocess_mutex.hpp>
#include <boost/lambda/lambda.hpp>
#include <iostream>
#include <iterator>
#include <algorithm>
#include <boost/interprocess/sync/scoped_lock.hpp>
#include <boost/interprocess/sync/named_mutex.hpp>
#include <fstream>
#include <iostream>
#include <cstdio>
#include <windows.h>
int main (int argc, char *argv[])
{
srand((unsigned) time(NULL));
using namespace boost::interprocess;
try{
/*
// UNCOMMENT THIS IF YOU WANT TO MAKE THIS DEMO IMPOSSIBLE TO USE TO DEMO ANYTHING
struct file_remove
{
file_remove() { std::remove("file_name"); }
~file_remove(){ std::remove("file_name"); }
} file_remover;
// UNCOMMENT THIS IF YOU WANT TO BREAK THIS DEMO HORRIBLY:
struct mutex_remove
{
mutex_remove() { named_mutex::remove("fstream_named_mutex"); }
~mutex_remove(){ named_mutex::remove("fstream_named_mutex"); }
} remover;
*/
//Open or create the named mutex
named_mutex mutex(open_or_create, "fstream_named_mutex");
std::ofstream file("file_name", std::ios_base::app );
int randomtime = 0;
for(int i = 0; i < 100; ++i){
//Do some operations...
//Write to file atomically
DWORD n1,n2;
n1 = GetTickCount();
{
scoped_lock<named_mutex> lock(mutex);
n2 = GetTickCount();
std::cout << "took " << (n2-n1) << " msec to acquire mutex";
randomtime = rand()%10;
if (randomtime<1)
randomtime = 1;
std::cout << " ... writing...\n";
if (argc>1)
file << argv[1];
else
file << "SOMETHING";
file << "...";
Sleep(randomtime*100);
file << " This is iteration #" << i;
file << std::endl;
file.flush();
}
Sleep(randomtime*100); // let the other guy in.
}
}
catch(interprocess_exception &ex){
std::cout << "ERROR " << ex.what() << std::endl;
return 1;
}
return 0;
}
I would love critques and edits on this answer, so that people will have a working demo of using this named mutex .
To use the demo:
- Build it and run two copies of it. Pass a parameter in so you can see which instance wrote which lines (start myexename ABC and start myexename DEF from a command prompt in windows)
- If it's your second run, delete any stray output named "file_name" if you don't want the second run appended to the first.

Non-blocking console input C++

I'm looking for a (multiplatform) way to do non-blocking console input for my C++ program, so I can handle user commands while the program continually runs. The program will also be outputting information at the same time.
What's the best/easiest way to do this? I have no problem using external libraries like boost, as long as they use a permissive license.
Example using C++11:
#include <iostream>
#include <future>
#include <thread>
#include <chrono>
static std::string getAnswer()
{
std::string answer;
std::cin >> answer;
return answer;
}
int main()
{
std::chrono::seconds timeout(5);
std::cout << "Do you even lift?" << std::endl << std::flush;
std::string answer = "maybe"; //default to maybe
std::future<std::string> future = std::async(getAnswer);
if (future.wait_for(timeout) == std::future_status::ready)
answer = future.get();
std::cout << "the answer was: " << answer << std::endl;
exit(0);
}
online compiler: https://rextester.com/GLAZ31262
I would do this by creating separate a thread which calls normal blocking IO functions and pass it a callback function which it would call when it got input. Are you sure you need to do what you said you want to do?
As for outputting information at the same time, what would happen if the user was in the middle of typing some input and you printed something?
I've done this on QNX4.5 that doesn't support threads or Boost by using select. You basically pass select STDIN as the file descriptor to use and select will return when a new line is entered. I've added a simplified example loop below. It's platform independent, at least for Unix like systems. Not sure about Windows though.
while (!g_quit)
{
//we want to receive data from stdin so add these file
//descriptors to the file descriptor set. These also have to be reset
//within the loop since select modifies the sets.
FD_ZERO(&read_fds);
FD_SET(STDIN_FILENO, &read_fds);
result = select(sfd + 1, &read_fds, NULL, NULL, NULL);
if (result == -1 && errno != EINTR)
{
cerr << "Error in select: " << strerror(errno) << "\n";
break;
}
else if (result == -1 && errno == EINTR)
{
//we've received and interrupt - handle this
....
}
else
{
if (FD_ISSET(STDIN_FILENO, &read_fds))
{
process_cmd(sfd);
}
}
}
There is one easy way:
char buffer[512];
int point = 0;
...
while (_kbhit()) {
char cur = _getch();
if (point > 511) point = 511;
std::cout << cur;
if (cur != 13) buffer[point++] = cur;
else{
buffer[point] = '\0';
point = 0;
//Run(buffer);
}
}
No block, all in 1 thread. As for me, this works.
Non-blocking console input C++ ?
Ans: do console IO on a background thread and provide a means of communicating between threads.
Here's a complete (but simplistic) test program that implements async io by deferring the io to a background thread.
the program will wait for you to enter strings (terminate with newline) on the console and then perform a 10-second operation with that string.
you can enter another string while the operation is in progress.
enter 'quit' to get the program to stop on the next cycle.
#include <iostream>
#include <memory>
#include <string>
#include <future>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <deque>
int main()
{
std::mutex m;
std::condition_variable cv;
std::string new_string;
bool error = false;
auto io_thread = std::thread([&]{
std::string s;
while(!error && std::getline(std::cin, s, '\n'))
{
auto lock = std::unique_lock<std::mutex>(m);
new_string = std::move(s);
if (new_string == "quit") {
error = true;
}
lock.unlock();
cv.notify_all();
}
auto lock = std::unique_lock<std::mutex>(m);
error = true;
lock.unlock();
cv.notify_all();
});
auto current_string = std::string();
for ( ;; )
{
auto lock = std::unique_lock<std::mutex>(m);
cv.wait(lock, [&] { return error || (current_string != new_string); });
if (error)
{
break;
}
current_string = new_string;
lock.unlock();
// now use the string that arrived from our non-blocking stream
std::cout << "new string: " << current_string;
std::cout.flush();
for (int i = 0 ; i < 10 ; ++i) {
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << " " << i;
std::cout.flush();
}
std::cout << ". done. next?\n";
std::cout.flush();
}
io_thread.join();
return 0;
}
sample test run:
$ ./async.cpp
first
new string: first 0 1las 2t 3
4 5 6 7 8 9. done. next?
new string: last 0 1 2 3 4 5 6 7 8quit 9. done. next?
ncurses can be a good candidate.
The StdinDataIO class of the BSD-licensed MUSCLE networking library supports non-blocking reads from stdin under Windows, MacOS/X, and Linux/Unix ... you could use that (or just examine the code as an example of how it can be done) if you want.
You can use the tinycon library to do this. Just spawn a tinycon object in a new thread, and you are pretty much done. You can define the trigger method to fire off whatever you'd like when enter is pressed.
You can find it here:
https://sourceforge.net/projects/tinycon/
Also, the license is BSD, so it will be the most permissive for your needs.
libuv is a cross-platform C library for asynchronous I/O. It uses an event loop to do things like read from standard input without blocking the thread. libuv is what powers Node.JS and others.
In a sense, this answer is incomplete. But yet, I think it can be useful even for people who have different platforms or circumstances, giving the idea, what to look for in their platform.
As I just wrote some scripting engine integration into an SDL2 main event loop (which is supposed to read lines from stdin if there are lines to be read), here is how I did it (on linux (debian bullseye 64 bit)). See below.
But even if you are not on linux, but on some other posix system, you can use the equivalent platform APIs of your platform. For example, you can use kqueue on FreeBSD. Or you can consider using libevent for a bit more portable approach (still will not really work on Windows).
This approach might also work on Windows if you do some special fiddling with the rather new-ish ConPTY. In traditional windows console applications, the problem is, that stdin is not a real file handle and as such, passing it to libevent or using IOCP (IO completion ports) on it will not work as expected.
But, this approach should also work on posix systems, if there is redirection at play. As long as there is a file handle available.
So how does it work?
Use epoll_wait() to detect if there is data available on stdin. While consoles can be configured in all sorts of ways, typically, they operate on a line by line basis (should also apply for ssh etc.).
Use your favorite getline() function to read the line from stdin. Which will work, because you know, there is data and it will not block (unless your console is not defaulting to line by line handling).
Rince and repeat.
#include <unistd.h>
#include <sys/epoll.h>
#include <iostream>
#include <string>
#include <array>
using EpollEvent_t = struct epoll_event;
int main(int argc, const char* argv[]) {
//
// create epoll instance
//
int epollfd = epoll_create1(0);
if (epollfd < 0) {
std::cout << "epoll_create1(0) failed!" << std::endl;
return -1;
}
//
// associate stdin with epoll
//
EpollEvent_t ev;
ev.data.ptr = nullptr;
ev.data.fd = STDIN_FILENO; // from unistd.h
ev.data.u32 = UINT32_C(0);
ev.data.u64 = UINT64_C(0);
ev.events = EPOLLIN;
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, STDIN_FILENO, &ev) < 0) {
std::cout
<< "epoll_ctl(epollfd, EPOLL_CTL_ADD, fdin, &ev) failed."
<< std::endl;
return -1;
}
//
// do non-blocking line processing in your free running
// main loop
//
std::array<EpollEvent_t,1> events;
bool running = true;
while (running) {
int waitret = epoll_wait(epollfd,
events.data(),
events.size(),
0); // 0 is the "timeout" we want
if (waitret < 0) {
std::cout << "epoll_wait() failed." << std::endl;
running = false;
}
if (0 < waitret) { // there is data on stdin!
std::string line;
std::getline(std::cin, line);
std::cout
<< "line read: [" << line << "]" << std::endl;
if (line == "quit")
running = false;
}
// ... Do what you usually do in your main loop ...
}
//
// cleanup of epoll etc.
//
close(epollfd);
return 0;
}
You could do:
#include <thread>
#include <chrono>
#include <string>
#include <iostream>
int main() {
std::cout << "Type exit to quit." << std::endl;
// initialize other std::thread handlers here
std::string input;
while (input != "exit") {
std::getline(std::cin, input);
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
std::cout << "Cleaning up and quitting" << std::endl;
return 0;
};
A simple answer with thread/future and reading a single char at a time (you can replace getchar with cin as required)
Timeout is set to zero and a new future is created every time the previous call is completed.
Like cin, getchar requires that the user hits the RETURN key to end the function call.
#include <chrono>
#include <cstdio>
#include <future>
#include <iostream>
#include <thread>
static char get_usr_in()
{
return std::getchar();
}
int main()
{
std::chrono::seconds timeout(0);
std::future<char> future = std::async(std::launch::async, get_usr_in);
char ch = '!';
while(ch!='q') {
if(future.wait_for(timeout) == std::future_status::ready) {
ch = future.get();
if(ch!='q') {
future = std::async(std::launch::async, get_usr_in);
}
if(ch >= '!' && ch <'~')
std::cout << "ch:" << ch << std::endl;
}
std::cout << "." << std::endl;
}
exit(0);
}
Why not use promises?
#include <iostream>
#include <istream>
#include <thread>
#include <future>
#include <chrono>
void UIThread(std::chrono::duration<int> timeout) {
std::promise<bool> p;
std::thread uiWorker([&p]() {
bool running = true;
while(running) {
std::string input;
std::cin >> input;
if(input == "quit") {
p.set_value(true);
running = false;
}
}
});
auto future = p.get_future();
if (future.wait_for(timeout) != std::future_status::ready) {
std::cout << "UI thread timed out" << std::endl;
uiWorker.detach();
return;
}
uiWorker.join();
}
int main()
{
std::thread uiThread(UIThread, std::chrono::seconds(3));
std::cout << "Waiting for UI thread to complete" << std::endl;
uiThread.join();
}
online complier