I unfortunately cannot think of any portable possibility. The problem is the fact that you try to read from the stream and that reading cannot be timed out or wakened - in standard c++ (or standard c).
What is possible though is to use the non portable select
. With select
you can listen for more than just one file descriptor. You can then create a pipe (also non portable) and add the read end of the pipe to the file descriptors you're listening on. That way you can wake up the thread that listens on stdin.
Maybe it's a better solution to go for the alternative mentioned in the comments, that way you just have to wait for the complete input but that might not be an issue, since you can evaluate whether the answer was provided in the given time slot or not. That way you could use portable c++.
The non portable solution could look like this:
#include <atomic>
#include <chrono>
#include <iostream>
#include <thread>
#ifdef WIN32
# include <io.h>
# include <winsock2.h>
#else
# include <sys/select.h>
# include <unistd.h>
#endif
using namespace std::chrono_literals;
struct Quiz
{
int pipe[2];
};
void countDown (const Quiz& quiz)
{
std::this_thread::sleep_for (5s);
std::cout << "Notify quiz...\n";
char trigger {'s'};
#ifdef WIN32
_write (quiz.pipe[1], &trigger, sizeof trigger);
#else
write (quiz.pipe[1], &trigger, sizeof trigger);
#endif
}
void quiz (const Quiz& quiz)
{
while (true) {
fd_set fds;
FD_ZERO (&fds);
FD_SET (0, &fds);
FD_SET (quiz.pipe[0], &fds);
std::cout << "some question?\n";
int result = select (quiz.pipe[0] + 1, &fds, nullptr, nullptr, nullptr);
if (result < 0) {
std::cerr << "Uh oh, problem in select\n";
return;
}
if (FD_ISSET (quiz.pipe[0], &fds)) {
std::cout << "time's up\n";
return;
}
else {
std::string answer;
std::cin >> answer;
std::cout << "your answer: " << answer << '\n';
}
}
std::cout << "time's up\n";
}
int main ()
{
Quiz bucket;
#ifdef WIN32
_pipe (quiz.pipe, 256, O_BINARY);
#else
pipe (bucket.pipe);
#endif
std::thread th1 {&countDown, std::cref (bucket)};
std::thread th2 {&quiz, std::cref (bucket)};
std::cout << "both threads have started\n";
th1.join ();
th2.join ();
std::cout << "both threads have ended\n";
#ifdef WIN32
_close (quiz.pipe[1]);
_close (quiz.pipe[0]);
#else
close (bucket.pipe[1]);
close (bucket.pipe[0]);
#endif
}
I want to mention that I changed the plain bool
to an atomic boolean in order to eliminate possible race conditions.