Home > Software engineering >  C : Best way to declare a variable to hold the return value of a function?
C : Best way to declare a variable to hold the return value of a function?

Time:11-04

I believe the following is a relatively common pattern (the exact types used are not important, they're just for example):

std::vector<std::string> manufactureVector(int param1, const std::string& param2) {
    std::vector<std::string> returnValue;
    // do some calculation with param1 and param2 to fill in the vector
    return returnValue;
}

However, the return type of manufactureVector is mentioned twice, with all of the drawbacks of that sort of redundancy, e.g. any future change has to be made identically in two places, etc. What's the best way to remove this redundancy? I am aware of:

  1. Use a typedef. This seems cumbersome if the return type is a one-off, so that the typedef would not be used anywhere else; at the very least, such a typedef adds a line of code.
  2. I believe C 20 (and possibly earlier) allows you to change the first occurrence of the type (in the function declaration) to auto and it will be inferred from what is actually returned. But that doesn't seem to work for declaring the function in a header (where there is no body to infer from) and then defining it in another source file. (Perhaps this is among the reasons to switch to modules?) Moreover, to my eyes at least it's harder to see what the return type of manufactureVector actually is if you use auto as its return type: I have to find the return statement, and then figure out what the type of that expression is.

Are there other possibly better alternatives to consider? In particular is there any reasonably brief "Incantation" such that

MyComplicatedType foo(int p, double q) {
  Incantation returnValue;
  // Here returnValue is a variable of type MyComplicatedType, whatever that type was
  ...
}

Ideally, such an Incantation wouldn't have to explicitly use the function name "foo" either, as that would substitute a different (small) bit of redundancy.

(Quite) a while back, GCC had named return values like

MyComplicatedType foo(int* ptr, char c) return returnValue { 
  // here returnValue is a newly default-constructed variable
  // of type MyComplicatedType
  ...
  // And in fact having declared the return variable this way,
  // you didn't even need to have the return statement at the end.
}

which was exactly the ticket here, in my view, but unfortunately that has been left behind in the dustbin of C history. Is there a modern equivalent or substitute? (I am perfectly happy with C 20-only solutions.)

CodePudding user response:

If the function doesn't have overload. you do not need to pass the parameters to get return type.

template <auto F>
using return_type_of = decltype(std::function{F})::result_type;

int foo(int p){
    return_type_of<foo> x;
    return x;
}

* still need type foo twice btw.

** in addition to std::invoke_result which need the parameter type as well.

CodePudding user response:

However, the return type of manufactureVector is mentioned twice

It can even be more than once, once dependent types are involved:

std::vector<std::string>::iterator b, e;

for (b=returnValue.begin(), e=returnValue.end(); b != e;   b)

or something along these lines.

So now in addition to all of this, this needs changing too.

Often auto comes to the rescue here, but not all the time.

any future change has to be made identically in two places,

Well, the fact that the return type must be explicitly spelled out in more than one place is unavoidable. This is how C works.

However it's possible to future-proof changing the return type in two or more places, in most cases.

typedef std::vector<std::string> manufactured_vector_t;

// ...

manufactured_vector_t manufactureVector(int param1, const std::string& param2) {
    manufactured_vector_t returnValue;

// ...

    manufactured_vector_t::iterator b, e;

    for (b=returnValue.begin(), e=returnValue.end(); b != e;   b)

// ...

    return returnValue;
}

And now if a std::vector<std::string> doesn't fit the bill anymore, it needs to be updated in only one place.

It's very likely that some other changes will be required, too. But, at least, this'll take care of that part.

  • Related