3

A thread where it prints values infinitely to console and a main thread which is taking user input from the console, but the input values are being mixed with the output of that thread.

C++ : cin while cout

Did give me a hint on how to move further but I was not able to come up with a solution of my own(as I am new to c++).

using namespace std;

mutex mtx;

void foo()
{
    while (1)
    {
        usleep(1000000);
        cout << "Cake\n";
    }
}

int main()
{
    mtx.lock();
    thread t1(foo);
    string x;
    while (true)
    {
        cin >> x;
        edit//fflush(stdin);
        cout << x << "\n";
    }
    t1.join();
    mtx.unlock();
    return 0;
}

edit 1:

OK to be more precise what i really want is,

IN terminal(presently)

output:cake (which prints every second)
output:cake 
output:cake 
output:cake 
input:hi
output:hicake (still yet to give the enter it echo's the input to console)
output:cake 

what i actually want in terminal is input being independent of output

output:cake 
output:cake 
output:cake 
input:hi
output:cake 
output:cake 
input:hi(waiting still for enter)

//and when enter is pressed it should print to the console
output:hi
output:cake

NOTE: disabling echo didn't help.

Edit 2: The answer posted by me has data processing where the concurrent operation stops on the given commands.

4
  • you lock your mutex before a while and you unlock inside the while .... you should unlock/lock the same number of time Commented Mar 29, 2019 at 10:30
  • sorry for that i have edited the code now, but i was not able to get the required solution even after that Commented Mar 29, 2019 at 12:01
  • fflush(stdin); is undefined behaviour and makes no sense. Never do that. A mutex that is only used in one thread makes no sense. You need to lock the mutex around each access to your resource in every thread. Commented Mar 29, 2019 at 12:07
  • 1
    thanks for the correction i will keep that in mind @n.m Commented Mar 29, 2019 at 12:15

2 Answers 2

4

Was able to come up with a solution using all the given inputs i hope this helps someone.

#include <termios.h>
#include <unistd.h>
#include <stdio.h>
#include <cstring>
#include <mutex>
#include <string>
#include <chrono>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <thread>
/// reads a character from console without echo.
int getChar()
{
    struct termios oldattr;
    tcgetattr(STDIN_FILENO, &oldattr);
    struct termios newattr = oldattr;
    newattr.c_lflag &= ~(ICANON | ECHO);
    tcsetattr(STDIN_FILENO, TCSANOW, &newattr);
    const int ch = getchar();
    tcsetattr(STDIN_FILENO, TCSANOW, &oldattr);
    return ch;
}

class Console
{
  private:
    // mutex for console I/O
    std::mutex _mtx;
    // current input
    std::string _input;
    // prompt output
    std::string _prompt;

  public:
    Console() {}

    Console(const Console &) = delete;
    Console &operator=(const Console &) = delete;

    std::string read();

    void write(const char *text, size_t size);
    void write(const char *text) { write(text, strlen(text)); }
    void write(const std::string &text) { write(text.c_str(), text.size()); }
};

std::string Console::read()
{
    { // activate prompt
        std::lock_guard<std::mutex> lock(_mtx);
        _prompt = "> ";
        _input.clear();
        std::cout << _prompt << std::flush;
    }
    enum
    {
        Enter = '\n',
        BackSpc = 127
    };
    for (;;)
    {
        switch (int c = getChar())
        {
        case Enter:
        {
            std::lock_guard<std::mutex> lock(_mtx);
            std::string input = _input;
            _prompt.clear();
            _input.clear();
            std::cout << std::endl;
            return input;
        } // unreachable: break;
        case BackSpc:
        {
            std::lock_guard<std::mutex> lock(_mtx);
            if (_input.empty())
                break;
            _input.pop_back();
            std::cout << "\b \b" << std::flush;
        }
        break;
        default:
        {
            if (c < ' ' || c >= '\x7f')
                break;
            std::lock_guard<std::mutex> lock(_mtx);
            _input += c;
            std::cout << (char)c << std::flush;
        }
        break;
        }
    }
}

void Console::write(const char *text, size_t len)
{
    if (!len)
        return;
    bool eol = text[len - 1] == '\n';
    std::lock_guard<std::mutex> lock(_mtx);
    // remove current input echo
    if (size_t size = _prompt.size() + _input.size())
    {
        std::cout
            << std::setfill('\b') << std::setw(size) << ""
            << std::setfill(' ') << std::setw(size) << ""
            << std::setfill('\b') << std::setw(size) << "";
    }
    // print text
    std::cout << text;
    if (!eol)
        std::cout << std::endl;
    // print current input echo
    std::cout << _prompt << _input << std::flush;
}

struct Flags //this flag is shared between both the threads
{
    // flag: true then exit communication thread and main loop
    bool exit;
    // flag: true then start data processing
    bool start;
    // the mini console
    Console console;

    // constructor.
    Flags() : exit(false), start(true) {}
};

void dataProc(Flags &shared)
{
    int i = 0;
    while (!shared.exit)
    {
        while (!shared.start)
        {
            if (shared.exit)
                return;
            std::this_thread::sleep_for(std::chrono::milliseconds(100));
        }
        shared.console.write("Starting data processing.");
        for (;;)
        {
            if (!shared.start || shared.exit)
            {

                shared.console.write("Data processing stopped.");
                while (!shared.start || shared.exit)
                {
                    if (shared.exit)
                        return;
                    std::this_thread::sleep_for(std::chrono::milliseconds(100));
                }
                shared.console.write("Data processing restarted.");
            }
            std::this_thread::sleep_for(std::chrono::milliseconds(250));
            {
                std::ostringstream fmt;
                fmt << "Cake " << ++i;
                shared.console.write(fmt.str());
            }
        }
        shared.console.write("Data processing done.");
        shared.start = false;
    }
}

void processInput(const std::string &input, Flags &shared)
{

    if (strcasecmp(input.c_str(),"start")==0)
        shared.start = true;
    else if (strcasecmp(input.c_str(),"stop")==0)
        shared.start = false;
    else if (strcasecmp(input.c_str(),"exit")==0)
        shared.exit = true;
    else if (input.size())
        shared.console.write("Wrong command!");
}




int main()
{
    Flags shared;
    std::thread threadProc(&dataProc, std::ref(shared));

    while (!shared.exit)
    {
        shared.console.write("Commands accepted: start stop exit");
        std::string input = shared.console.read();
        processInput(input, shared);
    }
    threadProc.join();
    return 0;
}
Sign up to request clarification or add additional context in comments.

1 Comment

It's great, you can actually use this to make a terminal text edtior like Vim. I learned this from this tutorial: Build Your Own Editor
0

I recommend locking your mtx for the cout as well as the cin.

using namespace std;

mutex mtx;

void foo()
{
    while (1)
    {
        usleep(1000000);
        mtx.lock();
        cout << "Cake\n";
        mtx.unlock();
    }
}

int main()
{
    thread t1(foo);
    string x;
    while (true)
    {
        cin >> x;
        mtx.lock();
        cout << x << "\n";
        mtx.unlock();
    }
    t1.join();
    return 0;
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.