Home > OS >  Writing a custom input manipulator
Writing a custom input manipulator

Time:06-08

I need to make a custom istream manipulator that reads 5 characters from input, then skip 5 characters from input, and does it to the end of the string. Example:

string line;
cin >> skipchar >> line;

This is that I did, but it doesn't work for some reason. Also, it would be better, if I don't use <sstream>

struct membuf : streambuf
{
    membuf(char* begin, char* end) {
        this->setg(begin, begin, end);
    }
};
istream& skipchar(istream& stream)
{
    string temp;
    getline(stream, temp);
    char *str = new char[temp.size()   1];
    strcpy(str, temp.c_str());//get string and convert to char*

    char* res = new char[strlen(str)];
    for (int i=0,j=0;i<strlen(str);i  )
        if ((i / 5) % 2 == 0) //get first 5, then 5 skip, etc
            res[j  ] = str[i];

    membuf b(res, res   strlen(res)); //copy str to buffer
    stream.rdbuf(&b);
    return stream;
}
int main()
{
    string str;
    cout << "enter smth:\n";
    cin >> skipchar >> str;
    cout << "entered string: " << str;
    return 0;
}  

CodePudding user response:

You did not show your input, but I don't think getline() would be appropriate to use in this situation. operator>> is meant to read a single word, not a whole line.

In any case, you are leaking both char[] arrays that you allocate. You need to delete[] them when you are done using them. For the str array (which FYI, you don't actually need, as you could just copy characters from the temp string directly into res instead), you can just delete[] it before exiting. But for res, the membuf would have to hold on to that pointer and delete[] it when the membuf itself is no longer being used.

But, more importantly, your use of membuf is simply wrong. You are creating it as a local variable of skipchar(), so it will be destroyed when skipchar() exits, leaving the stream with a dangling pointer to an invalid object. The streambuf* pointer you assign to the stream must remain valid for the entire duration that it is assigned to the istream, which means creating the membuf object with new, and then the caller will have to remember to manually delete it at a later time (which kind of defeats the purpose of using operator>>). However, a stream manipulator really should not change the rdbuf that the stream is pointing at in the first place, since there is not a good way to restore the previous streambuf after subsequent read operations are finished (unless you define another manipulator to handle that, ie cin >> skipchar >> str >> stopskipchar;).

In this situation, I would suggest a different approach. Don't make a stream manipulator that assigns a new streambuf to the stream, thus affecting all subsequent operator>> calls. Instead, make a manipulator that takes a reference to the output variable, and then reads from the stream and outputs only what is needed (similar to how standard manipulators like std::quoted and std::get_time work), eg:

struct skipchars
{
    string &str;
};

istream& operator>>(istream& stream, skipchars output)
{
    string temp;
    if (stream >> temp) {
        for (size_t i = 0; i < temp.size(); i  = 10) {
            output.str  = temp.substr(i, 5);
        }
    }
    return stream;
}

int main()
{
    string str;
    cout << "enter smth:\n";
    cin >> skipchars{str};
    cout << "entered string: " << str;
    return 0;
}

Online Demo

Alternatively:

struct skipcharsHelper
{
    string &str;
};

istream& operator>>(istream& stream, skipcharsHelper output)
{
    string temp;
    if (stream >> temp) {
        for (size_t i = 0; i < temp.size(); i  = 10) {
            output.str  = temp.substr(i, 5);
        }
    }
    return stream;
}

skipcharsHelper skipchars(string &str)
{
    return skipcharsHelper{str};
}

int main()
{
    string str;
    cout << "enter smth:\n";
    cin >> skipchars(str);
    cout << "entered string: " << str;
    return 0;
}

Online Demo

  • Related