C++ write to stringstream not working properly

877 views Asked by At

I've been making an assembler for the CHIP-8 these days, and today I tried implementing arguments for the opcodes to work properly. However, when I have to write 2 arguments to complete the opcode, I have this code:

// Chip8Instruction is a struct I've made that holds the final machine code to be written, the mnemonic,
// the argument count and the position for each argument in hex
void writeTwoArguments(Chip8Instruction instruction, const std::string& line, int lineNum, const std::string& romName) {
    unsigned int arg1;
    unsigned int arg2;
    
    std::string remainingLine = line;
    std::string testStr;

    std::stringstream stringStream;


    // Arguments are comma and then space separated, assume I give 3, FF
    std::string::size_type commaPos = line.find(',');
    
    if (commaPos != std::string::npos) {
        stringStream << std::hex << remainingLine.substr(0, commaPos); // This writes 3, like it should
        testStr = stringStream.str(); // Holds "3", like it should
        stringStream >> arg1; // This holds 0x3, like it should
        stringStream.str("");

        remainingLine.erase(0, commaPos+2); // FF remains, like it should
        stringStream << std::hex << remainingLine;
        testStr = stringStream.str(); // This holds nothing but it should have "FF", if I don't empty the stream it holds "3" from before
        stringStream >> arg2; // This also holds nothing but it should have 0xFF, holds 0x3 if not empty stream

        instruction.machineCode = instruction.start + (arg1 * instruction.arg1Pos) + (arg2 * instruction.arg2Pos);
    }

    writeMultipleDigitsToROM(romName, instruction.machineCode, lineNum);

}

Minimum reproducible example:

#include <iostream>
#include <sstream>

int main() {
   std::string line = "3, FF";

   std::stringstream stringStream;

   unsigned int int1;
   unsigned int int2;

   // Get position of comma
   std::string::size_type commaPos = line.find(',');

   // Get everything up to the comma
   stringStream << std::hex << line.substr(0, commaPos);
   stringStream >> int1; // This holds 0x3, like it should
   stringStream.str(""); 
   line.erase(0, commaPos+2); // "FF" remains, like it should
   stringStream << std::hex << line;
   stringStream >> int2; // This is empty but should be 0xFF
   
}

As the code comments describe, the held values are either wrong or outdated. What might be the problem, why, and how can I fix it?

All help is appreciated, thank you!

2

There are 2 answers

2
neal On

I tried isolating the problem with this code:

#include <iostream>
#include <string>
#include <sstream>

using namespace std;

int main() {
    string input = "3, FF";
    stringstream ss;
    ss << input.substr(0, 1);
    cout << ss.str() << endl;
    input.erase(0, 3);
    ss.str("");
    cout << input << endl;
    ss << std::hex << input;
    cout << ss.str() << endl;

    return 0;
}

However, it appears to work correctly on my end. Not sure what the difference is, sorry, unless I made a mistake in the input format. BTW, I’d recommend, that instead of extracting to string stream, extract from it by wrapping your input in a string stream, like so:

int arg1, arg2;
string comma;
stringstream ss(input);
ss >> std::hex >> arg1 >> comma >> arg2;
cout << arg1 << endl;
cout << arg2 << endl;

Better yet, you could overload the stream extraction operator operator>> for your type, so it could be something like this:

Chip8Instruction c;
ss >> c;
// do stuff now that c is filled in

You can do that by declaring something like:

istream& operator>>(istream& in, Chip8Instruction& out) {
   // where your code to parse the string could go
}

Sorry I can’t help with the actual parsing problem, but it isn’t clear to me what I did differently from you.

0
KamilCuk On

First: std::hex changes the way integers are printed or scanned. It has no affect on line - line is a string. stringStream << std::hex << line; is the same as stringStream << line;.

After you do stringStream >> int1; then eof bit is set inside stringStream. To scan anything else, you have to clear it first. Please research how to reset a stringstream.

To read hex number you have to tell your stream you want to read a hex number. F is not a digit!

But overall the usage is odd - just read the stuff from a stringstream it's literally the thing to read data from. Forget about your string - work on the stream. Remember to handle errors. I could see:

#include <iostream>
#include <sstream>
#include <iomanip>

int main() {
    std::string line = "3, FF";

    std::istringstream ss(line);
    unsigned int int1;
    unsigned int int2;
    if (!(
            (ss >> std::dec >> int1) &&
            ss.get() == ',' &&
            (ss >> std::hex >> int2)
            )) {
        std::cerr << "read failed\n";
        abort();
    }
    std::cout << int1 << " " << int2 << "\n";
}