116

If I have a function that produces a result int and a result string, how do I return them both from a function?

As far as I can tell I can only return one thing, as determined by the type preceding the function name.

4
  • 7
    By string do you mean "I'm using C++ and this is the std::string class" or "I'm using C and this is a char * pointer or char[] array." Commented Apr 12, 2010 at 6:15
  • well, in my particular case, they were two ints: one for the 'score' of what i was comparing, and one for the 'index' of where that max score was found. i wanted to use a string example here just for the more general case Commented Apr 12, 2010 at 6:22
  • Pass the string by reference and give back the int. Fastest way. No structs required. Commented Apr 12, 2010 at 7:34
  • 1
    Isn't a function that returns 2 results doing more than one thing? What would Uncle Bob say? Commented Apr 12, 2010 at 7:37

8 Answers 8

148

I don't know what your string is, but I'm going to assume that it manages its own memory.

You have two solutions:

1: Return a struct which contains all the types you need.

struct Tuple {
    int a;
    string b;
};

struct Tuple getPair() {
    Tuple r = { 1, getString() };
    return r;
}

void foo() {
    struct Tuple t = getPair();
}

2: Use pointers to pass out values.

void getPair(int* a, string* b) {
    // Check that these are not pointing to NULL
    assert(a);
    assert(b);
    *a = 1;
    *b = getString();
}

void foo() {
    int a, b;
    getPair(&a, &b);
}

Which one you choose to use depends largely on personal preference as to whatever semantics you like more.

Sign up to request clarification or add additional context in comments.

7 Comments

I think it depends more on how related the return values are. If the int is an error code and the string is a result, then these should not be put together in a struct. That's just silly. In that case, I would return the int and pass the string as a char * and a size_t for the length unless it's absolutely vital for the function to allocate it's own string and/or return NULL.
@Chris I agree with you completely, but I have no idea of the usage semantics of the variables he needs.
Good point Chris. Another thing I think is worth pointing out is value vs reference. If I am not mistaken returning the struct as shown in the example means a copy will be made upon return, is that correct? (I'm a bit shaky on C) While the other method uses pass-by-reference and thus doesn't require allocating more memory. Of course, the struct could be returned via pointer and share that same benefit right? (making sure to allocate memory properly and all of that of course)
@BobVicktor: C does not have reference semantics at all (that is exclusive to C++), so everything is a value. The two pointers solution (#2) is passing copies of the pointers into a function, which getPair then dereferences. Depending on what you're doing (OP never clarified if this is actually a C question), allocation might be a concern, but it usually isn't in C++ land (return value optimization saves all of this) and in C land, data copies usually happen explicitly (via strncpy or whatever).
@TravisGockel Thanks for the correction. I was referring to the fact that pointers are used, so it isn't copying the values, just sharing what was already allocated. But you are right in saying that isn't properly called pass-by-reference in C. And thanks for the other great nuggets of knowledge as well. I love learning these little things about languages. :)
|
11

Option 1: Declare a struct with an int and string and return a struct variable.

struct foo {    
 int bar1;
 char bar2[MAX];
};

struct foo fun() {
 struct foo fooObj;
 ...
 return fooObj;
}

Option 2: You can pass one of the two via pointer and make changes to the actual parameter through the pointer and return the other as usual:

int fun(char **param) {
 int bar;
 ...
 strcpy(*param,"....");
 return bar;
}

or

 char* fun(int *param) {
 char *str = /* malloc suitably.*/
 ...
 strcpy(str,"....");
 *param = /* some value */
 return str;
}

Option 3: Similar to the option 2. You can pass both via pointer and return nothing from the function:

void fun(char **param1,int *param2) {
 strcpy(*param1,"....");
 *param2 = /* some calculated value */
}

2 Comments

Regarding option 2, you should also pass in the string's length. int fun(char *param, size_t len)
Now that depends on what the function is doing. If we know what kind of result the function stuffs into the char array, we could just allocate enough space for it and pass it to the function. No need of passing the length. Something similar to how we use strcpy for example.
8

Since one of your result types is a string (and you're using C, not C++), I recommend passing pointers as output parameters. Use:

void foo(int *a, char *s, int size);

and call it like this:

int a;
char *s = (char *)malloc(100); /* I never know how much to allocate :) */
foo(&a, s, 100);

In general, prefer to do the allocation in the calling function, not inside the function itself, so that you can be as open as possible for different allocation strategies.

Comments

8

Create a struct and set two values inside and return the struct variable.

struct result {
    int a;
    char *string;
}

You have to allocate space for the char * in your program.

Comments

6

Two different approaches:

  1. Pass in your return values by pointer, and modify them inside the function. You declare your function as void, but it's returning via the values passed in as pointers.
  2. Define a struct that aggregates your return values.

I think that #1 is a little more obvious about what's going on, although it can get tedious if you have too many return values. In that case, option #2 works fairly well, although there's some mental overhead involved in making specialized structs for this purpose.

2 Comments

C doesn't have references ;-), although since the poster used string, it might be safe to assume C++...
Totally forgot about that! I modified my answer to use pointers, but I've clearly been in C++ land for too long. :)
5

One approach is to use macros. Place this in a header file multitype.h

#include <stdlib.h>

/* ============================= HELPER MACROS ============================= */

/* __typeof__(V) abbreviation */

#define TOF(V) __typeof__(V)

/* Expand variables list to list of typeof and variable names */

#define TO3(_0,_1,_2,_3) TOF(_0) v0; TOF(_1) v1; TOF(_2) v2; TOF(_3) v3;
#define TO2(_0,_1,_2)    TOF(_0) v0; TOF(_1) v1; TOF(_2) v2;
#define TO1(_0,_1)       TOF(_0) v0; TOF(_1) v1;
#define TO0(_0)          TOF(_0) v0;

#define TO_(_0,_1,_2,_3,TO_MACRO,...) TO_MACRO

#define TO(...) TO_(__VA_ARGS__,TO3,TO2,TO1,TO0)(__VA_ARGS__)

/* Assign to multitype */

#define MTA3(_0,_1,_2,_3) _0 = mtr.v0; _1 = mtr.v1; _2 = mtr.v2; _3 = mtr.v3;
#define MTA2(_0,_1,_2)    _0 = mtr.v0; _1 = mtr.v1; _2 = mtr.v2;
#define MTA1(_0,_1)       _0 = mtr.v0; _1 = mtr.v1;
#define MTA0(_0)          _0 = mtr.v0;

#define MTA_(_0,_1,_2,_3,MTA_MACRO,...) MTA_MACRO

#define MTA(...) MTA_(__VA_ARGS__,MTA3,MTA2,MTA1,MTA0)(__VA_ARGS__)

/* Return multitype if multiple arguments, return normally if only one */

#define MTR1(...) {                                                           \
    typedef struct mtr_s {                                                    \
      TO(__VA_ARGS__)                                                         \
    } mtr_t;                                                                  \
    mtr_t *mtr = malloc(sizeof(mtr_t));                                       \
    *mtr = (mtr_t){__VA_ARGS__};                                              \
    return mtr;                                                               \
  }

#define MTR0(_0) return(_0)

#define MTR_(_0,_1,_2,_3,MTR_MACRO,...) MTR_MACRO

/* ============================== API MACROS =============================== */

/* Declare return type before function */

typedef void* multitype;

#define multitype(...) multitype

/* Assign return values to variables */

#define let(...)                                                              \
  for(int mti = 0; !mti;)                                                     \
    for(multitype mt; mti < 2; mti++)                                         \
      if(mti) {                                                               \
        typedef struct mtr_s {                                                \
          TO(__VA_ARGS__)                                                     \
        } mtr_t;                                                              \
        mtr_t mtr = *(mtr_t*)mt;                                              \
        MTA(__VA_ARGS__)                                                      \
        free(mt);                                                             \
      } else                                                                  \
        mt

/* Return */

#define RETURN(...) MTR_(__VA_ARGS__,MTR1,MTR1,MTR1,MTR0)(__VA_ARGS__)

This makes it possible to return up to four variables from a function and assign them to up to four variables. As an example, you can use them like this:

multitype (int,float,double) fun() {
    int a = 55;
    float b = 3.9;
    double c = 24.15;

    RETURN (a,b,c);
}

int main(int argc, char *argv[]) {
    int x;
    float y;
    double z;

    let (x,y,z) = fun();

    printf("(%d, %f, %g\n)", x, y, z);

    return 0;
}

This is what it prints:

(55, 3.9, 24.15)

The solution may not be as portable because it requires C99 or later for variadic macros and for-statement variable declarations. But I think it was interesting enough to post here. Another issue is that the compiler will not warn you if you assign them the wrong values, so you have to be careful.

Additional examples, and a stack-based version of the code using unions, are available at my github repository.

2 Comments

A bigger portability problem is the non-standard feature __typeof__
Instead of typeof, you could probably use sizeof. Get the size of the return values and allocate an array large enough to store them all. Then you can return it.
4

Use pointers as your function parameters. Then use them to return multiple value.

Comments

2

By passing parameters by reference to function.

Examples:

 void incInt(int *y)
 {
     (*y)++;  // Increase the value of 'x', in main, by one.
 }

Also by using global variables but it is not recommended.

Example:

int a=0;

void main(void)
{
    //Anything you want to code.
}

3 Comments

void main(void) OH HOW IT BURNS!
what do you mean? @ Chris Lutz
main is supposed to return a status code. Under *nix, it's more usual to declare it as int main(int argc, char *argv[]), I believe Windows has similar conventions.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.