[C++] Inconsistent Output

Pages: 12
This is driving me crazy. I have a function that is written to take "A string of text." and turn it into

Other Output A string
             of text.


Very simple code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
char* multiLineString(const char* line, int lineLen, int linePadding){
    string multiLine = "";

    for(int i = 0; line[i] != '\0'; i++){
        if(i != 0 && i % lineLen == 0){
            multiLine += '\n';
            for(int j = 0; j < linePadding; j++){
                multiLine += ' ';
            }
            // Before we start printing the second line, the length needs to be
            // reduced to lineLen - linePadding to keep lines all the same length.
            // If the first character(s) of a new line is a space, don't print it.
            while(line[i] == ' '){
                // Skip character
                i++;
            }
        }
        multiLine += line[i];
    }

    char* lines = const_cast<char*>(multiLine.c_str());

    return lines;
}


It works perfectly. Now the dilemma, using it.

1
2
3
/* Other code*/
<< "  " << multiLineString(books[i].bookTitle, 55, 21) << endl
<< "  " << books[i].bookTitle << endl;


I would think that if the second line prints, the first one would, but it doesn't. What's even odder is that in the loop that that cout statement is in will sometimes output what it should, and other times it won't. And to toss even one more twist on it, two of the outputted values are the exact same. I even double checked with strcmp().

And, if you want to toss one other twist on this, I have multiple functions that print slightly different results. One prints nothing, one prints a couple, and one prints all. I compared the one's that print something and everything, and except for one additional line in the cout statement, they are basically identical.

Does anybody have any idea what might be causing this problem?

Thanks.
Last edited on
Hi meesa,

I would try a different approach : make use of stringstreams, substr function, setw and setfill.

It seems that you current approach is sort of a C approach in that you are processing 1 char at a time. IMO, if you find yourself doing this in C++ there is probably a better way ....

I also try to avoid char arrays, and use std::string exclusively.

Hope all is well.
Line 21 is completely illegal.

you do not only pretend that a constant string is modifiable, you also return a pointer to a local variable which leads to undefined behavior.
@coder777

Afaik it's not illegal to use const_cast, why else would it be in the language like that?

And you're right about the pointer; multiLine's destructor will delete [] it at scope exit. The returned pointer points to unallocated memory directly after the call.
Afaik it's not illegal to use const_cast, why else would it be in the language like that?

It isn't illegal. If the original variable was const, it results in undefined behavior. In this case, I think it's reasonable to assume the original variable was not const, but the use of it here is definitely not something I would recommend.

Afaik it's not illegal to use const_cast, why else would it be in the language like that?
it's not against the law. just consider it as if...

Try this:
1
2
3
4
5
6
7
8
9
10
#include <cstring>

int main()
{
  const char a[] = "test";

  strcpy(const_cast<char *>(a), "x"); // ok

  return 0;
}


1
2
3
4
5
6
7
8
9
10
#include <cstring>

int main()
{
  const char *a = "test";

  strcpy(const_cast<char *>(a), "x"); // CRASH

  return 0;
}


can you see what makes const_cast that awkward?
TheIdeaMan:

I'm open to options, but here's my cout statement.

1
2
3
4
cout << showpoint << setprecision(2) << fixed << left
     << " "  << setw(3)          << books[i].qty
     << "  " << setw(ISBN_WIDTH) << books[i].ISBN
     << "  " << multiLineString(books[i].bookTitle, 55, 21) << endl;


And that's inside a for loop.

Also, I used a char* because my bookTitle has to be a char[] due to me saving it to a file.

And how is char by char non-C++? Any parsing is character by character.


Coder777, const_cast is necessary for assignment, due to c_str() returning a constant variable. If I don't do it that way, I have to make several other things const. I also see what you mean about the local variable thing. Is there a good way around this?

Thanks for the input.
It occurred to me that even though this accepts a char*, it's for output. Chances are I'll never need to assign the results back to the same type of variable passes. So I just changed the function to a string, and returned multiline. Which makes the function (I think) compatible with both c strings and strings (Using c_str()). The function works perfectly now.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
string multiLineString(const char* line, int lineLen, int linePadding){
    string multiLine = "";

    for(int i = 0; line[i] != '\0'; i++){
        if(i != 0 && i % lineLen == 0){
            multiLine += '\n';
            for(int j = 0; j < linePadding; j++){
                multiLine += ' ';
            }
            // If the first character(s) of a new line is a space, don't print it.
            while(line[i] == ' '){
                // Skip character
                i++;
            }
        }
        multiLine += line[i];
    }

    return multiLine;
}
Last edited on
meesa wrote:
And how is char by char non-C++? Any parsing is character by character.



I am just saying that functions like substr do the same job, that is why they exist in the STL, so one doesn't have to reinvent the wheel.

So for this example all you want to do, is place a newline character (with insert)in the positions that are multiples of the width you want, and pad the last line so that it is right aligned, which you could do with the setfill function.

Probably could do all this in 4 or 5 LOC.

Hope this helps 8-)
Yeah, I see what you mean. I'm thinking I'm going to have to keep it like that though, as on my TODO list is to add an option so it will break it at words instead of characters. Which means I'd have to check each character for a space, so I can add word by word, so I can check each word length, etc... Unless there's a better way?
meesa wrote:
Which means I'd have to check each character for a space, so I can add word by word, so I can check each word length, etc... Unless there's a better way?


Yes, have a read of this :

http://www.cplusplus.com/reference/istream/istream/get/


The third version allows for a delimiting char, so it tokenises the string.

The example unfortunately shows char arrays & processing by individual chars, but you can still do std::string

There is also this :

http://www.cplusplus.com/reference/string/string/find_first_of/


Which you could use to replace spaces with newlines or whatever you want.

There is a lot of really good stuff in the string & stringstream classes, plus the algorithms section in the reference.
Well, basically I need to read the first 50 characters, unless the spaces are at positions 47 and 53, then I'd need to just read the first 47.

Of course, the only way to do that is to read word by word, and calculate to see if that new word with the space before it is longer than the length.

So I need a loop to wrap through the string.
Something to extract up to the next space
An if else statement after each word is read to calculate
Something to add X spaces before the next line starts.
Etc.

I see what you're saying that there are other functions out there, but I really wouldn't be saving any speed, or lines (One or two, maybe?) by using them.

After all, is it really easier to create a variable to hold the result of find_first_of to use it with substr rather than just looping through?

Also, a lot of the functions you're giving me is for stream objects, while I'm not using input, I'm using variables. (Get, streamstring, etc)
meesa wrote:
I see what you're saying that there are other functions out there, but I really wouldn't be saving any speed, or lines (One or two, maybe?) by using them.


Well, it is up to you, but the whole idea of the STL is so one can just use the tools, rather than write them again themselves. The find_first_of example was pretty concise & easy in my view.

The STL stuff might be optimised in ways you hadn't thought of, and do other things like throw exceptions which can be handy. They are also overloaded - if you write your own code, you would have to write more code to cope with different types of arguments.

meesa wrote:
Also, a lot of the functions you're giving me is for stream objects, while I'm not using input, I'm using variables. (Get, streamstring, etc)


But a std::string can be used to create a stream object, here is an example from the stringstream constructor :

http://www.cplusplus.com/reference/sstream/stringstream/stringstream/


The example has ints, but you can do the same for std::string - version 2 of the constructor.

Also the gcount function can be used on a stringstream to return the number of chars extracted.

http://www.cplusplus.com/reference/istream/istream/gcount/


You could use this if you wanted to count the number of chars in a word.
I'll look into it when I have time to modify the function. If I decide to go that direction, I'll post back with my new code.

Thanks for the help.
Ok, no worries - pleased to be of help :-)
Hi meesa,

I wrote some code for you.

The examples are copied from the reference material, except for the last 2. The last one is real straight forward with 4 LOC.

It is all pretty simple, but someone might have an even easier way.

HTH


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#include <iostream>
#include <sstream>
#include <string>
#include <iomanip>
#include <cstddef>

using std::cout;
using std::cin;
using std::endl;


int main()
{

   std::string MyStr = "The quick, brown fox, jumps over, the lazy, dog";
   const std::size_t FieldWidth = 10;
   std::size_t found = 0;

   // Split using the comma char


     found = MyStr.find_first_of(",");
     while (found!=std::string::npos) {
       MyStr[found]='\n';
       found=MyStr.find_first_of(",",found+1);
     }
     cout << "With comma \n\n" << MyStr << "\n\n";



     // Same again with spaces
     //reset the variables - we are using them again
     MyStr = "The quick brown fox jumps over the lazy dog";

     found = MyStr.find_first_of(" ");
          while (found!=std::string::npos) {
            MyStr[found]='\n';
            found=MyStr.find_first_of(" ",found+1);
          }
          cout << "With spaces \n\n" << MyStr << "\n\n";



     // This time with a field width and padding of the last line
     MyStr = "The quick brown fox jumps over the lazy dog";

     std::size_t i;
     for (i = 0; i < MyStr.length(); i += (FieldWidth+1)) {
        MyStr.insert(i, "\n");
     }
     i -= FieldWidth; // the pos of i before the end of the string
     std::size_t Padding = FieldWidth - (MyStr.length()-i); // Padding needed for last part

     MyStr.insert(i, Padding, ' ' );
     std::cout << "With Fieldwidth \n\n" <<MyStr << "\n\n";



     // this version doesn't modify the string
     const std::string MyConstStr = "The quick brown fox jumps over the lazy dog";

     cout << "With non modified string \n\n";

     for (size_t i =0; i < MyConstStr.length(); i+=FieldWidth) {
        cout << std::setw(FieldWidth)<< std::setfill(' ');
        cout << MyConstStr.substr(i,FieldWidth) << endl;
     }

   return 0;
}




With comma 

The quick
 brown fox
 jumps over
 the lazy
 dog

With spaces 

The
quick
brown
fox
jumps
over
the
lazy
dog

With Fieldwidth 


The quick 
brown fox 
jumps over
 the lazy 
       dog

With non modified string 

The quick 
brown fox 
jumps over
 the lazy 
       dog

@coder777

I don't understand the difference between

const char a[] = "test";
and
const char *a = "test";

Is const char a[] same as const char const *a ?
Last edited on
@abhishekm71

I don't understand the difference between
the first is an array. the other is a pointer.

the array is created on the stack with the size and the content of the string.
the pointer points to a [write protected] string.

try this:
1
2
3
4
5
const char a[] = "test1";
const char *b = "test2";

a = b; // compiler error
b = a; // ok 


an array can be implicitly converted to a pointer, but it isn't actually
Ok thanks coder777.

But is const int a [] same as const int const* a?
But is const int a [] same as const int const* a?
No, there's no difference between char and int except the type and that with char you're able to use a string for initialization.

With int:
1
2
3
4
5
const int a[] = { 1, 2, 3 };
const int *b = ...;

a = b; // compiler error
b = a; // ok  


By the way:
const int const* a is not a valid c++ expression.

You can write it like so
const int * a
or so
int const* a

Pages: 12