0

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;
}  
nameses
  • 3
  • 2

1 Answers1

1

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

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • Thanks! But I need to use a manipulator in code and input should be like this: cin >> skipchar >> line; That is why I started to use streams and membuf. – nameses Jun 07 '22 at 22:52
  • "*input should be like this: `cin >> skipchar >> line;`*" - sorry, but there is no safe way to implement that syntax the way you want (that I know of, anyway). What is wrong with the approach I suggested? This is the way the standard library implements such manipulators, so why can't you follow the same pattern? – Remy Lebeau Jun 07 '22 at 23:00
  • Your suggestion is perfect, but this is how the task was given. Well, if you say that it's the only one way of solving this problem, I will follow your suggestion. Thanks again) – nameses Jun 07 '22 at 23:01
  • "*this is how the task was given*" - then your task is misguided. That way is not how stream manipulators are meant to be used. – Remy Lebeau Jun 07 '22 at 23:13