3

I currently use three different functions to return a numeric value (one returns a double, the other two return a long):

int main(void)
{
   // lots of code

    dRate = funcGetInterestRate();
    lMonths = funcGetTerm();
    lPrincipal = funcGetPrincipal();

    // lots of code

    return 0;
}

The three functions code is about 90% the same so I would like to consolidate into 1 function. I want to pass a value flag to a single function something like this:

  1. if "1" passed, determine interest rate, return a double
  2. if "2" passed, determine term of loan, return a long
  3. if "3" passed, determine principal of loan, return a long

I only want to return 1 value ever from the function when it is called, but the value I want to return can be either a double or a long. I want to do something like this:

void funcFunction(value passed to determine either long or double)
{
   // lots of code

   if (foo)
      return double value;
   else
      return long value;
}

Is there an easy way to do this?

dbush
  • 205,898
  • 23
  • 218
  • 273
Jman
  • 187
  • 1
  • 3
  • 12

6 Answers6

10

A function's return type is fixed at compile time. You can't change the return type based on the parameters you pass in.

However, since your main goal is to remove repeated code in the functions and consolidate them, this can be addressed in a different way. In other words, this is an XY problem.

What you can do in your case is extract the common code in each of your three functions into a separate function, then the three original functions can call the common function to do most of the work, then extract the part they need and return that.

For example:

struct loan {
    double rate;
    long term;
    long principal;
};

void calcLoan(struct loan *loan)
{
    // do stuff
    loan->rate = // some value        
    loan->term = // some value        
    loan->principal = // some value        
}

double funcGetInterestRate()
{
    struct loan loan;
    calcLoan(&loan);
    return loan.rate;
}

long funcGetTerm()
{
    struct loan loan;
    calcLoan(&loan);
    return loan.term;
}

long funcGetPrincipal()
{
    struct loan loan;
    calcLoan(&loan);
    return loan.principal;
}
dbush
  • 205,898
  • 23
  • 218
  • 273
  • 1
    Kudos for the common code refactoring suggestion, this is how good, modularised software should be written. – paxdiablo Dec 30 '18 at 13:48
  • Why not use a `union`? The result type is known due to the input. Not withstanding @paxdiablo's remark, the question was whether it was possible to vary the return type based on the input. With a union you can. – Paul Ogilvie Dec 30 '18 at 13:55
  • 1
    @Paul, sometimes the best answer to `How do I do A?` is `Don't do A, do B instead.` :-) – paxdiablo Dec 30 '18 at 13:58
  • @PaulOgilvie - A struct has the benefit of allowing to calculate all three values at once (note that this is what the OP's main needs). So this way, we can even save on work. – StoryTeller - Unslander Monica Dec 30 '18 at 13:58
  • @StoryTeller Sometimes that makes sense, in other cases it becomes a cumbersome dependency. There are other ways to eliminate duplicate evaluation. Besides, OP requested: "I only want to return 1 value ever from the function when it is called." – Ruud Helderman Dec 30 '18 at 14:13
  • @dbush Thanks very much for your answer! After much googling prior to posting this I was getting the point that what I wanted to do could not be done directly. Your answer confirms that. Having a common function for the repetitive code does seem to be the way to approach this. – Jman Dec 30 '18 at 18:20
1

No, C does not allow this. The return type is in the function declaration (which you have as void).

Slightly easier is to provide two pointers to variables and indicate which one to use in the return value:

int funcFunction(yourArgument, long *longResult, double *dblResult)
{
   // lots of code

   if (foo)
   {
      *dblResult = value;
      return 1;
   } else
   {
      *longResult = otherValue;
      return 0;
   }
}

(And possibly you can even use a union.) However ... I had to use value and otherValue as inside the function you cannot use the same variable to hold either a long or a double. You can – again, with a union – but this is stressing the eaze of having only one single function to the breaking point.

Jongware
  • 22,200
  • 8
  • 54
  • 100
  • You may as well pass one `void*` and cast to the required pointer type. That way at least the call syntax will be consistent and not littered with `NULL` when one pointer is unused. – StoryTeller - Unslander Monica Dec 30 '18 at 13:53
  • @StoryTeller: sure. But I feel all solutions only move the problem (which variable gets used) to another part of the code. I'd rather advise the OP to revise the rest of the code so these constructions are not necessary. – Jongware Dec 30 '18 at 13:59
1

You might consider returning some tagged union. The Glib GVariant type could be inspirational, and since Glib is free software, you could study its source code. See also this answer.

So you would declare some public struct with an anonymous union inside:

struct choice_st {
   bool islong;
   union {
     long i; // valid when islong is true
     double d; // valid when islong is false
   }
}

and you could return a value of that struct choice_st.

struct choice_st res;
if (foo) {
  res.islong = true;
  res.i = somelong;
  return res;
} 
else {
  res.islong = false;
  res.d = somedouble;
  return res;
}

You might also decide to use C dynamic memory allocation, return a freshly malloc-ed pointer to struct choice_st, and adopt a convention about who is responsible of free-ing that pointer. (BTW, GVariant is doing something similar to this).

Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
0

You sort of can do this. It's easier to show than explain, but I'll add an explanation if this isn't clear:

void add_or_divide_by_xor(unsigned int a, unsigned int b, unsigned short c,
                          unsigned long *add, unsigned double *divide) {
    unsigned int xor = a ^ b;
    if (add != NULL) {
        *add = xor + c;
    }
    if (divide != NULL) {
        *divide = (double)xor / (double)c;
    }
}

Called:

unsigned long add;
unsigned double divide;
add_or_divide_by_xor(5, 17, 4, &add, NULL);
add_or_divide_by_xor(17, 6, 4, NULL, &divide);
wizzwizz4
  • 6,140
  • 2
  • 26
  • 62
0

Depending on your platform, double might be a superset of long. You should already know whether this is true (because you know what your algorithms do, and what their possible output values are); if you don't, consider the following:

  • double can represent integers up to 253 exactly
  • long is either a 32-bit or a 64-bit type

So if your long values are 32-bit, you can just always return double from your functions, and cast it to long outside your functions, where needed.

anatolyg
  • 26,506
  • 9
  • 60
  • 134
  • 2
    There's nothing is the standard requiring a `long` to be one of those sizes. It's possible that it may be some 2048-bit behemoth, or even larger :-) – paxdiablo Dec 30 '18 at 13:52
0

You could try something like this.

#include <stdio.h>

void* func1(int key){
  static double interest;
  static long term;
  static long principle;

  //your common code to modify values of interest, term and principle
  //
  //Let us give them some dummy values for demonstration
  interest = 34.29347;
  term = 5843535;
  principle = 7397930;

  //conditions
  if(key == 1){
    return &interest;
  }
  else if(key == 2){
    return &term;
  }else if(key == 3){
    return &principle;
  }else printf("%s\n","error!!");
}

int main()
{
    printf("%f\n",*(double*)func1(1));
    printf("%ld\n",*(long*)func1(2));
    printf("%ld\n",*(long*)func1(3));
    func1(4);
    return 0;
}

Output

34.293470

5843535

7397930

error!!

rsonx
  • 436
  • 7
  • 16