Thread: Output parameter practices

  1. #1
    Old Fashioned
    Join Date
    Nov 2016
    Posts
    137

    Question Output parameter practices

    Do you have any useful rules of thumb you follow when writing a function to use output parameters rather than a direct "return value?"

    For example, say we have a function like this:

    Code:
    void util_get_string_int(const char *input, int *output)
    {
        assert(input && output);
        if(!str_contains_all_digits(input))
        {
            *output = -1;
            return;
        }
        *output = atoi(input);
        return;
    }
    


    This version returns the int in an output parameter. Instead, I could just return these int values as actual ints in the return value but for this purpose, I figured I would already have an int stored in memory, so might as well just use that one.

    What types of things do you consider before you make these decisions?
    If I was homeless and jobless, I would take my laptop to a wifi source and write C for fun all day. It's the same thing I enjoy now!

  2. #2
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,660
    > *output = atoi(input);
    What is input contains "-1" ?
    How will you tell that from your error return?

    I tend to follow the model that the function return value is the success/fail status, and any additional results are returned via out parameter pointers.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  3. #3
    Registered User
    Join Date
    Feb 2019
    Posts
    1,078
    Well... I try to use the "left hand side" rule used in some C standard functions... As examples:

    Code:
    // dest is the 'output'
    char *strcpy( char *dest, char *src );
    
    // 'buffer' is the 'output'
    void *memset( void *buffer, int c, size_t size );
    But I think this is a matter of preference. ONE way to document your code is to use empty lexical symbols:
    Code:
    #define IN
    #define OUT
    
    void util_get_string_int(IN const char *input, OUT int *output)
    {
      assert(input && output); 
    
      if(!str_contains_all_digits(input))
      {
        *output = -1;
        return;
      } 
    
      *output = atoi(input);
    }

    []s
    Fred

  4. #4
    Old Fashioned
    Join Date
    Nov 2016
    Posts
    137
    Quote Originally Posted by Salem View Post
    > *output = atoi(input);
    What is input contains "-1" ?
    How will you tell that from your error return?
    Well if input contains "-1" then it won't pass my str_contains_all_digits() check which only allows chars 0-9. However, if it resolves to VALUE -1 somehow, then I need to handle that case still. This is a first iteration and I still need to write some tests.

    flp, good point, I know Windows API uses those empty symbols.
    If I was homeless and jobless, I would take my laptop to a wifi source and write C for fun all day. It's the same thing I enjoy now!

  5. #5
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by Asymptotic
    Well if input contains "-1" then it won't pass my str_contains_all_digits() check which only allows chars 0-9. However, if it resolves to VALUE -1 somehow, then I need to handle that case still. This is a first iteration and I still need to write some tests.
    My question would then be: why is this util_get_string_int when it cannot handle negative number input as would be expected for int? Perhaps it should have been util_get_string_unsigned_int, in which case your use of -1 would result in UINT_MAX, so the next question would be: what if the input contains the textual value of UINT_MAX?

    So, for this case having an error code return value in addition to an output parameter would be helpful to distinguish between an error condition and a valid value, while using the entire range of values for the output.
    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

  6. #6
    Old Fashioned
    Join Date
    Nov 2016
    Posts
    137
    Well atoi() returns 0 if a conversion cannot be made... What's to say that 0 isn't in fact the converted number? I ran into a similar issue here.

    I made these out of necessity in a situation where library functions return ints and the output of these functions need to be able to make comparisons with ints. However, you're right in that I am restricting the output of this to all integers 0 and greater, which is unsigned. But I didn't want to be coming unsigned ints with signed ints which come from other library functions. Have you ever run into this issue and if so, have a better solution? I figure I can either perform checks to make sure the int is within acceptable range OR convert the output of those library funcs to unsigned.
    If I was homeless and jobless, I would take my laptop to a wifi source and write C for fun all day. It's the same thing I enjoy now!

  7. #7
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by Asymptotic
    Well atoi() returns 0 if a conversion cannot be made... What's to say that 0 isn't in fact the converted number? I ran into a similar issue here.
    Exactly. atoi suffers from the same design flaw, which is why strtol is usually recommended as an alternative. strtol switches things around a bit to have the output be a return value and the error designated through interpreting a pointer to a pointer because it allows the caller to provide a textual integer representation followed by "invalid" input, leaving it up to the caller to decide whether the trailing input really is invalid or should be ignored.

    Note that both atoi and strtol skip leading whitespace, whereas your function considers all whitespace as invalid input.

    Quote Originally Posted by Asymptotic
    However, you're right in that I am restricting the output of this to all integers 0 and greater, which is unsigned. But I didn't want to be coming unsigned ints with signed ints which come from other library functions. Have you ever run into this issue and if so, have a better solution?
    The simple solution is to rename your function, e.g., util_get_string_nonneg_int. This way, the reader will not be misled into thinking that it will handle all int values, and will also know just just because the values are non-negative doesn't mean that the result is an unsigned int.

    But, Salem already suggested a better solution for handling the entire range:
    Quote Originally Posted by Salem
    I tend to follow the model that the function return value is the success/fail status, and any additional results are returned via out parameter pointers.
    This could be as simple as giving it an int return type:
    Code:
    int util_get_string_int(const char *input, int *output);
    then either treat the return value as a boolean (zero means conversion failure; non-zero means success), or a more comprehensive error code (in which case zero -- as the value of a named error code constant -- typically means success instead; probably overkill in this case, but it does allow the caller to differentiate between say, leading invalid input versus trailing invalid input versus out of range etc).
    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

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. C Best Practices Help
    By Asymptotic in forum C Programming
    Replies: 6
    Last Post: 12-05-2016, 04:50 PM
  2. best practices to make an app with a GUI?
    By skan in forum C++ Programming
    Replies: 4
    Last Post: 11-24-2012, 06:41 PM
  3. getchar() best practices
    By albundy in forum C Programming
    Replies: 2
    Last Post: 09-03-2011, 05:55 AM
  4. Cprogram Input/output parameter HELP
    By 123456tacos in forum C Programming
    Replies: 5
    Last Post: 03-11-2011, 10:59 PM
  5. Replies: 6
    Last Post: 01-08-2008, 10:25 AM

Tags for this Thread