0

To receive from shell an integer value comprised within the (0, 8) interval, I cin to an uint8_t element of an array, doing the following:

    char answer;

    do
    {
        // Instructions
        std::cout << "Linear actuator resolution is:\n"
                  << "\u0394x = \u03B1r/2^i, with \u03B1 = " << std::to_string(ALPHA)
                  << " degrees, r = " << std::to_string(pulleyR) << " m and i in [0 : 8]\n";

        // Parameter selection
        std::cout << "Please enter a valid value for param 'i': ";
        std::cin >> Engines_uSteppingLevel[RAIL];
        if(Engines_uSteppingLevel[RAIL] > (RES_LEVELS - 1))
            // Wrong selection, repeat question
            std::cout << '\r';
        else
        {
            // Print out the selected resolution and ask user to confirm or restart selection
            std::cout << "Selected linear resolution: " << std::fixed << std::setprecision(4)
                      << ALPHA*pulleyR/(1<<Engines_uSteppingLevel[RAIL])
                      << "m, enter 'y' to confirm, any other key to change selection ";
            std::cin >> answer;
            if(answer == 'y')
                break;
        }
    }while(true);

Even though I enter a correct value, the loop does not break.

Entering:

std::cout << Engines_uSteppingLevel[RAIL] << ' ' << (RES_LEVELS - 1) << '\n';

in place of:

   std::cout << '\r';

shell output is:

Please enter a valid value for param 'i': 0
0 8
Please enter a valid value for param 'i': 3
3 8

which does not make sense.

GioP
  • 55
  • 1
  • 10
  • Raw and cooked (line-buffered) reads from stdin don't mix well. You need to either get rid of the `_getch()` call, or get rid of `std::cin` and use only `_getch()`. – Ben Voigt May 06 '19 at 00:28
  • 1
    I think, though, that your problem is a misunderstand of exactly where the `continue` keyword jumps to. – Ben Voigt May 06 '19 at 00:30
  • Right, it jumps to the _getch() result evaluation, so the thing can work only if the value is correct at the first round. Nevertheless, if the first value entered is correct, 'continue' should not be reached – GioP May 06 '19 at 00:35
  • 1
    You have a number of questionable things in your code. One, looping until you press `y` is what this code does, I don't know why that surprises you. Two, the `std::fixed` and `std::setprecision` modifiers aren't having any effect, because you write a string to the stream, not a number. Then, what is the value of `RAIL` and how large is the array? If you access a non-existent element in your array, the program can do very strange things ("undefined behavior"). – Ben Voigt May 06 '19 at 00:44
  • RAIL comes from an enum, and is just the '0' index, not exceeding array boundary. I am not surprised that it loops until 'y' is pressed, I explained that. Thank you for pointing out the modifiers thing, I am surprised the compiler didn't. – GioP May 06 '19 at 00:49
  • Are `ALPHA` and `pulleyR` set properly? Perhaps the compiler is deciding the `else` is illegal and therefore always sending you to the other branch. – Ben Voigt May 06 '19 at 00:54
  • They are just #defines with float value of few digits – GioP May 06 '19 at 01:09
  • 1
    Please also `std::cout << +Engines_uSteppingLevel[RAIL]` (or cast to `int`). I suspect you have trigged character-based I/O and your variable holds the ASCII value `'0'` and `'3'` not the numeric value `0` and `3`. – Ben Voigt May 06 '19 at 01:11
  • Also try commenting out the loop, and the else block, and so on, so that your code is as simple as possible to produce the problem. – Ben Voigt May 06 '19 at 01:13
  • Also note that you could have found the problem much much quicker by using a debugger. – Ben Voigt May 06 '19 at 01:18

1 Answers1

1

The problem you're having here is in the way that C++ handles the uint8_t type: it thinks it's a char. If you change your debug output statement to:

std::cout << static_cast<int>(Engines_uSteppingLevel[RAIL]) << ' ' << (RES_LEVELS - 1) << '\n';

you will see that when you type a '0', and ASCII '0' is stored in your uint8_t variable, which has the value 48. Similarly, when you type '3' you're storing a 51, etc.

The simplest solution that will work for you would be to use a character conversion, like this:

Engines_uSteppingLevel[RAIL] -= '0';

Note that this solution only works for single-digit values. A more robust, extensible solution would involve a std::string or char buffer, and then a call to atoi, strtoul, std::stoul, or something similar.

  • 2
    It's actually implementation dependent whether `uint8_t` is `unsigned char`, or an "extended integral type". The simplest solution is to read into a numeric variable, validate the value, then store it into the array. – Ben Voigt May 06 '19 at 01:16
  • This answer provides a great analysis of how `(u)int8_t` can map to different extraction operators on different platforms: https://stackoverflow.com/a/16006093/103167 – Ben Voigt May 06 '19 at 01:26