Charles
Charles

Reputation: 11479

What's the right way to handle this error checking?

I have a function that should be passed a string with two numbers (as a regex: /-?[0-9]+ -?[0-9]+/) and return the second.

I've decided that the program should do error checking. First, it should test if the string is actually of the desired form; second, it should ensure that the first numbers (the ones that are not returned) are sequential.

Now I've been programming for a long time and this is not a difficult task. (It's made slightly more difficult by the fact that the numbers need not fit into a machine word.) But my question is about how I should do this rather than how I can. All of the solutions I've come up with are somewhat ugly.

So any thoughts relating to the proper way to deal with error-checking of this sort in C would be welcome.


This is related to a much more theoretical question I asked on cstheory. For reference, here is the function:

char*
scanInput(char* line)
{
    int start = 0;
    while (line[start] == ' ' || line[start] == '\t')
        start++;
    if (line[start] == '#')
        return NULL;    // Comment
    if (line[start] == '-')
        start++;
    while (line[start] >= '0' && line[start] <= '9')
        start++;
    while (line[start] == ' ' || line[start] == '\t')
        start++;
    int end = start;
    if (line[end] == '-')
        end++;
    while (line[end] >= '0' && line[end] <= '9')
        end++;
    if (start == end)
        return NULL;    // Blank line, or no numbers found
    line[end] = '\0';

    return line + start;
}

and it is called like so:

while(fgets(line, MAX_LINELEN, f) != NULL) {
    if (strlen(line) > MAX_LINELEN - 5)
        throw_error(talker, "Maximum line length exceeded; file probably not valid");
    char* kept = scanInput(line);
    if (kept == NULL)
        continue;
    BIGNUM value = strtobignum(kept);
    if (++i > MAX_VECLEN) {
        warning("only %d terms used; file has unread terms", MAX_VECLEN);
        break;
    }
    // values are used here
}

Upvotes: 2

Views: 113

Answers (2)

Jonathan Leffler
Jonathan Leffler

Reputation: 753455

Ultimately, you are going to need to isolate and convert both big numbers in each line. To check that the first number on the line is the one that follows the previous, you will have to keep a record of the last such number found. So, you will probably need a structure such as:

BIGNUM old_value = 0;  // See notes below

while (fgets(line, sizeof(line), f) != 0)
{
    BIGNUM value1;
    BIGNUM value2;
    if (ScanDoubleBigNum(line, &value1, &value2) != 0)
        ...handle line format error...
    if (old_value == 0 || are_consecutive(old_value, value1))
    {
        // OK - valid information found
        // Release old_value
        old_value = value1;
        process(value2);
        // Release value2
    }
    else
        ...handle non-consecutive error...
}

The are_consecutive() function determines whether its second argument is one greater than its first. The process() function does whatever you need to do with the second value. The ScanDoubleBigNum() function is related to your ScanInput() but it reads two values. The actual code will call another function (call it ScanBigNum()) containing about half of ScanInput() (since that contains essentially the same code twice), plus the conversion that currently occurs in your loop. The code in ScanDoubleBigNum() will call ScanBigNum() twice. Note that ScanBigNum() will need to identify where the scan finishes so that the second call can continue where the first stopped.

I'm taking the liberty of assuming that a BIGNUM is an allocated structure identified by a pointer, so the initialization BIGNUM old_value = 0; is a way of indicating there is no value yet. There is presumably a function to release a BIGNUM. If this is incorrect, then you need to adapt the proposed code to accommodate the actual behaviour of the BIGNUM type. (Is this based on OpenSSL or SSLeay code?)

Upvotes: 1

hugomg
hugomg

Reputation: 69924

The traditional solution in C is to use pass by reference (pointers) to return the values your function computes and use the return value for error handling, just like how scanf does this.

int scanInput(char **line_p int *number){
    char * line = *line_p;
    ...
    if(something bad happens){
        return 1;
    }
    ...
    *linep = line + start;
    *number = ...;
    return 0; //success
}

int main(){
    char word[100]; strcpy(word, "10 17");
    char *line = word;
    int number;
    switch(scanInput(&line, &number)){
        case 1:
        default:
    }
}

Extra points:

  • It might be a good idea to use some enum to give a meaning to the error codes.
  • If you can use C++ (or similar) exceptions are often the best solution for error handling, since you don't have to fill your code with ifs anymore
  • Global variables are generaly evil. If you are tempted to use them, consider instead encapsulating the state you need in a struct and passing a pointer to it around. Treat it as a "this" pointer, in the OO sense.

Upvotes: 2

Related Questions