Thread: C++ Classes for a simple program using stdin/stdout

  1. #1
    Registered User
    Join Date
    May 2009
    Posts
    4,183

    C++ Classes for a simple program using stdin/stdout

    I have decided to try to learn C++ the way I learned C many years, ago.
    That way was to write simple utility programs.

    The first one is an asmformat command. To replace tabs with spaces and change other white space things.

    Example usage
    Code:
    asm4mat < old.S > new.S
    Any standard C++ classes or Boost classes that would be useful for me to checkout?

    Any C++ replacement for C's isspace function?

    I plan to use the function only 4 times; so, the use of command line parameters is not needed. I am converting 2 versions of two separate assembly files to same format to see what was changed by two different forks of the same base files.

    Tim S.
    "...a computer is a stupid machine with the ability to do incredibly smart things, while computer programmers are smart people with the ability to do incredibly stupid things. They are,in short, a perfect match.." Bill Bryson

  2. #2
    Registered User
    Join Date
    May 2010
    Posts
    4,632
    Any C++ replacement for C's isspace function?
    Well isspace() is standard C++, but there may be something else if you're using std::string.

    Any standard C++ classes or Boost classes that would be useful for me to checkout?
    Perhaps you may want to show the program you're trying to convert from C to C++?

  3. #3
    Registered User
    Join Date
    May 2009
    Posts
    4,183
    Not converting any C program; I just know I could do it in C. I have finished my first try at the program. Decided to do two programs instead of just one.

    Code:
    #include <iostream>
    #include <string>
    // C++ converted C headers
    #include <cctype>
    
    using std::cin;
    using std::cout;
    using std::endl;
    
    const char startCommentChar=';';
    
    // Change multiple spaces/tabs into a single space.
    
    int main()
    {
      std::string aLine;
      int i;
    
      while(getline(cin, aLine)){
        int lineLen = aLine.length();
        bool insideSpace=false;
        bool insideComment=false;
        for(i=0; i < lineLen; i++){
          if(insideComment==false && aLine[i] == startCommentChar){
            insideComment=true;
            cout << aLine[i];
          } else if(insideComment==false && isspace(aLine[i])){
            if ( insideSpace==false){
              insideSpace=true;
              cout << ' ';
            }
          } else {
            insideSpace=false;
            cout << aLine[i];
          }
        } // end for loop
        cout << endl;
      } // end while loop
      return 0;
    }
    Second program
    Code:
    #include <iostream>
    #include <string>
    // C++ converted C headers
    #include <cctype>
    
    using std::cin;
    using std::cout;
    using std::endl;
    using std::size_t;
    
    const int instruct_start=10;
    const int operand_start=18;
    const int comment_start=36;  // Also used for directive 2nd operand
    
    const char startCommentChar=';';
    const char startDirectiveChar='.';
    const char startPreProChar='#';
    
    const int indents[]={10,18,36};
    
    int spaceout(int from, int to);
    
    int main()
    {
      std::string aLine;
      int inpos;
      int outpos;
    
      while(getline(cin, aLine)){
        int lineLen = aLine.length();
        int spaceCount=0;
        bool insideComment=false;
        bool insidePrePro=false;
        for(inpos=0; inpos< lineLen; inpos++){
          if(inpos==0 && insidePrePro==false && aLine[inpos] == startPreProChar){
            insidePrePro=true;
            cout << aLine[inpos];
            outpos++;
          } else if(insidePrePro==false && insideComment==false && aLine[inpos] == startCommentChar){
            insideComment=true;
            cout << aLine[inpos];
            outpos++;
          } else if(insidePrePro==false && insideComment==false && isspace(aLine[inpos])){
            if (spaceCount == 0) outpos = inpos;
            if (spaceCount > 0 && aLine[inpos+1] == startDirectiveChar){
              cout << ' ';
            } else {
              spaceCount++;
              cout << ' ';
              if (spaceCount < 4){
                outpos = spaceout(outpos+1, indents[spaceCount-1]);
              }
            } // != startDirectiveChar
          } else {
            cout << aLine[inpos];
            outpos++;
          }
        } // end for loop
        cout << endl;
      } // end while loop
      return 0;
    }
    
    int spaceout(int from, int to){
      for(int x = from; x < to; x++) cout << ' ';
      return to;
    }
    Last few lines of the input assembly
    Code:
    #ifdef L_seuclid
    ; signed euclidean division
    ; calling: (left / right)
    ;   push left
    ;   ldd right
    ;   jsr _seuclid
    ; quotient on the stack (left)
    ; modulus in d
      .area .text
      .globl  _seuclid
    left  = 6
    right = 2
    quo_sgn = 1
    mod_sgn = 0
    _seuclid:
      leas  -4,s    ; 3 local variables
      std right,s
      clr mod_sgn,s
      clr quo_sgn,s
      ldd left,s
      bge mod_abs
      inc mod_sgn,s ; sign(mod) = sign(left)
      inc quo_sgn,s
      bsr negd    ; abs(left) -> D
    mod_abs:
      pshs  b,a   ; push abs(left)
      ldd right+2,s ; all references shifted by 2
      bge quot_abs
      dec quo_sgn+2,s ; sign(quot) = sign(left) XOR sign(right)
      bsr negd    ; abs(right) -> D
    quot_abs:
      CALL  _euclid   ; call (unsigned) euclidean division
      std right+2,s
      puls  a,b   ; quot -> D
      tst quo_sgn,s ; all references no longer shifted
      beq quot_done
      bsr negd
    quot_done:
      std left,s    ; quot -> left
      ldd right,s
      tst mod_sgn,s
      beq mod_done
      bsr negd
    mod_done:
      leas  4,s   ; destroy stack frame
      rts
    negd:       ; self-explanatory !
      nega
      negb
      sbca  #0
      rts
    #endif
    
    
    #ifdef L_m0
      .area .direct
      .globl  m0,m1,m2,m3
    m0: .blkb 1
    m1: .blkb 1
    m2: .blkb 1
    m3: .blkb 1
    #endif
    
    
    #ifdef L_m4
      .area .direct
      .globl  m4,m5,m6,m7
    m4: .blkb 1
    m5: .blkb 1
    m6: .blkb 1
    m7: .blkb 1
    #endif
    
    
    #ifdef L_im0
      .area .direct
      .globl  im0,im1,im2,im3
    im0:  .blkb 1
    im1:  .blkb 1
    im2:  .blkb 1
    im3:  .blkb 1
    #endif
    
    
    #ifdef L_im4
      .area .direct
      .globl  im4,im5,im6,im7
    im4:  .blkb 1
    im5:  .blkb 1
    im6:  .blkb 1
    im7:  .blkb 1
    #endif
    
    
    #ifdef L_fm0
      .area .direct
      .globl  fm0,fm1,fm2,fm3
    fm0:  .blkb 1
    fm1:  .blkb 1
    fm2:  .blkb 1
    fm3:  .blkb 1
    #endif
    
    
    #ifdef L_fm4
      .area .direct
      .globl  fm4,fm5,fm6,fm7
    fm4:  .blkb 1
    fm5:  .blkb 1
    fm6:  .blkb 1
    fm7:  .blkb 1
    #endif
    Last few lines of the output assembly
    Code:
    #ifdef L_seuclid
    ; signed euclidean division
    ; calling: (left / right)
    ;   push left
    ;   ldd right
    ;   jsr _seuclid
    ; quotient on the stack (left)
    ; modulus in d
              .area .text
              .globl  _seuclid
    left      =       6
    right     =       2
    quo_sgn   =       1
    mod_sgn   =       0
    _seuclid:
              leas    -4,s              ; 3 local variables
              std     right,s
              clr     mod_sgn,s
              clr     quo_sgn,s
              ldd     left,s
              bge     mod_abs
              inc     mod_sgn,s         ; sign(mod) = sign(left)
              inc     quo_sgn,s
              bsr     negd              ; abs(left) -> D
    mod_abs:
              pshs    b,a               ; push abs(left)
              ldd     right+2,s         ; all references shifted by 2
              bge     quot_abs
              dec     quo_sgn+2,s       ; sign(quot) = sign(left) XOR sign(right)
              bsr     negd              ; abs(right) -> D
    quot_abs:
              CALL    _euclid           ; call (unsigned) euclidean division
              std     right+2,s
              puls    a,b               ; quot -> D
              tst     quo_sgn,s         ; all references no longer shifted
              beq     quot_done
              bsr     negd
    quot_done:
              std     left,s            ; quot -> left
              ldd     right,s
              tst     mod_sgn,s
              beq     mod_done
              bsr     negd
    mod_done:
              leas    4,s               ; destroy stack frame
              rts
    negd:     ; self-explanatory !
              nega
              negb
              sbca    #0
              rts
    #endif
    
    
    #ifdef L_m0
              .area .direct
              .globl  m0,m1,m2,m3
    m0:       .blkb   1
    m1:       .blkb   1
    m2:       .blkb   1
    m3:       .blkb   1
    #endif
    
    
    #ifdef L_m4
              .area .direct
              .globl  m4,m5,m6,m7
    m4:       .blkb   1
    m5:       .blkb   1
    m6:       .blkb   1
    m7:       .blkb   1
    #endif
    
    
    #ifdef L_im0
              .area .direct
              .globl  im0,im1,im2,im3
    im0:      .blkb   1
    im1:      .blkb   1
    im2:      .blkb   1
    im3:      .blkb   1
    #endif
    
    
    #ifdef L_im4
              .area .direct
              .globl  im4,im5,im6,im7
    im4:      .blkb   1
    im5:      .blkb   1
    im6:      .blkb   1
    im7:      .blkb   1
    #endif
    
    
    #ifdef L_fm0
              .area .direct
              .globl  fm0,fm1,fm2,fm3
    fm0:      .blkb   1
    fm1:      .blkb   1
    fm2:      .blkb   1
    fm3:      .blkb   1
    #endif
    
    
    #ifdef L_fm4
              .area .direct
              .globl  fm4,fm5,fm6,fm7
    fm4:      .blkb   1
    fm5:      .blkb   1
    fm6:      .blkb   1
    fm7:      .blkb   1
    #endif
    Tim S.
    "...a computer is a stupid machine with the ability to do incredibly smart things, while computer programmers are smart people with the ability to do incredibly stupid things. They are,in short, a perfect match.." Bill Bryson

  4. #4
    Registered User
    Join Date
    May 2009
    Posts
    4,183
    I just tried the two programs on the second assembly file and it worked very poorly. So, the program needs work.
    But, the second file is shorter likely do it by hand.

    The second assembly program used C style comments of /* */ and that was about one third of the issues; no idea what caused the rest of the problem, yet.

    Edit: If someone can give feedback on the C++ way of doing the program; I am afraid I might have just done C with cin and cout.
    Edit2: I decided this time to just use C++ classes instead of writing them till I get much farther along.

    Tim S.
    Last edited by stahta01; 02-27-2019 at 05:28 PM.
    "...a computer is a stupid machine with the ability to do incredibly smart things, while computer programmers are smart people with the ability to do incredibly stupid things. They are,in short, a perfect match.." Bill Bryson

  5. #5
    Registered User
    Join Date
    May 2010
    Posts
    4,632
    I just tried the two programs on the second assembly file and it worked very poorly. So, the program needs work.
    But, the second file is shorter likely do it by hand.
    I would start by simplifying with functions. And remember that isspace() tests for all whitespace characters.

    Since you're using C++ strings you may want to look at the documentation for this class and possibly use some of the class member functions like find, find_first_of, substr(), etc. to process your line.

    I would also suggest you describe the purpose of both the snippets and don't forget to describe what each section of the code is trying to do.

  6. #6
    Registered User
    Join Date
    May 2009
    Posts
    4,183
    Do you know assembly programming? The assembly is the very old Motorola MC6809 CPU from about 1980 timeframe.
    The assembly code spaces except for EOL should all be treated the same. So, isspace should cause no problem.
    I did have a version using substr; but, it seemed to be unneeded when I split the problem into using two programs.
    I will add comments in the future; but, you do understand using standard in/out programs to solve tasks? I was amazed when one of the common posters on this website did not understand this usage. I will see if I can use a few more helper functions; I was hoping that I had missed a C++ class that could solve parts of the problem.
    But, this is a very small problem; so, likely not a good example of C++ class based solutions. I will look into the string class more in depth; any good links that cover the string C++ class/functions/methods?

    Tim S.
    "...a computer is a stupid machine with the ability to do incredibly smart things, while computer programmers are smart people with the ability to do incredibly stupid things. They are,in short, a perfect match.." Bill Bryson

  7. #7
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by stahta01
    If someone can give feedback on the C++ way of doing the program; I am afraid I might have just done C with cin and cout.
    You are pretty much doing "C with cin and cout", except that you used std::string too, which abstracts away either the manual memory management or the dealing with line chunks that would otherwise be required to handle arbitrarily long lines. I think that's fine: a procedural approach is still a legitimate approach in C++. A few comments though:
    • An introductory comment header would help. jimblumberg mentioned this in post #5.
    • Instead of using comments to mark the end of loops, I would have increased the number of spaces per indent level and moved the inner loop to its own function.
    • Personally, I don't think you really need those using declarations. I would go with a using directive for std, but in a more limited scope instead.
    • Your for loop to loop over the characters of the string actually using a wrong type for the index. It should have been std::string::size_type, but it would be easier to use a range-based for loop.

    Putting these together, you might get something like:
    Code:
    /* Reads line by line from standard input and writes each line to standard
     * output such that contiguous sequences of whitespace characters have been
     * changed into a single space. Characters after the first ';' character on
     * each line are regarded as forming a "comment" and hence will be left as-is.
     */
    
    #include <cctype>
    #include <iostream>
    #include <string>
    
    const char startCommentChar = ';';
    
    void printSpaceCompressedLine(std::ostream& out, const std::string& line);
    
    int main()
    {
        using namespace std;
    
        string line;
        while (getline(cin, line)) {
            printSpaceCompressedLine(cout, line);
        }
        cout << endl;
    
        return 0;
    }
    
    void printSpaceCompressedLine(std::ostream& out, const std::string& line)
    {
        bool insideSpace = false;
        bool insideComment = false;
        for (char c : line) {
            if (insideComment) {
                out << c;
            } else if (c == startCommentChar) {
                insideComment = true;
                out << c;
            } else if (std::isspace(c)) {
                if (!insideSpace) {
                    insideSpace = true;
                    out << ' ';
                }
            } else {
                insideSpace = false;
                out << c;
            }
        }
    }
    Note that printSpaceCompressedLine is fairly reusable: instead of having two programs, you could have used it in a single program with the help of a stringstream, although that might not be the most efficient approach. Another approach would have been to have the function (template) write to an output iterator instead, upon which it could have been reused to directly append to a string via an iterator adapter (e.g., with the help of std::back_inserter).

    Quote Originally Posted by stahta01
    I was hoping that I had missed a C++ class that could solve parts of the problem.
    You're basically writing a state machine; would you really expect the standard to provide a class for that? If you really want to explore complex parsing in C++, there's Boost.Spirit.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  8. #8
    Registered User
    Join Date
    May 2009
    Posts
    4,183
    laserlight, thank you for the feedback. And, the use of range-based for loops; I do not think that existed the last time I tried to learn C++.

    Tim S.
    "...a computer is a stupid machine with the ability to do incredibly smart things, while computer programmers are smart people with the ability to do incredibly stupid things. They are,in short, a perfect match.." Bill Bryson

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Stdin stdout
    By mark707 in forum C Programming
    Replies: 3
    Last Post: 09-16-2013, 10:38 AM
  2. Recreating stdout/stdin
    By Zarniwoop in forum C Programming
    Replies: 1
    Last Post: 10-02-2008, 06:22 PM
  3. stdin --> random interference --> stdout
    By j0hnb1ank in forum C Programming
    Replies: 3
    Last Post: 01-22-2003, 11:43 AM
  4. STDIN stdout
    By GreyMattr in forum Linux Programming
    Replies: 2
    Last Post: 08-01-2002, 01:29 PM
  5. stdin/stdout
    By lmmds2 in forum C Programming
    Replies: 2
    Last Post: 12-10-2001, 02:07 PM

Tags for this Thread