0

I'm looking for an elegant way to avoid re-writing a function, whose implementation is almost the same, but only the signature (the number of input parameters and their data types) is different. I know function overloading is not possible in C. I also know about the existence of variadic functions. But I think they won't be helpful in this situation.

Consider the following problem, where we need to calculate the area of a triangle. We have two functions implementing two different formulae: S = 1/2bh and S = sqrt(s(s-a)(s-b)(s-c)). Apart from calculating the area each of the functions also modifies a parameter nb or nthr. Finally, there is a top level routine bisect_area_... that launches a bisection procedure on a given function area_tria1 or area_tria2 optimising it for a parameter nb or nthr. Currently I explicitly implement two bisection functions: one for the signature of area_tria1 and another one for area_tria2. I feel there must be a better, more elegant way, that would allow to have a single generic bisection function bisect_area_tria(). Please note, in the real case, that I have at hand, the input parameter data types also differ.

Below is a skeleton pseudocode of the function signatures:

// Calculate area of triangle, modify and return parameter 'nb'
void area_tria1_nb(..., int *nb, double b, double h, double *S) {

    // change parameter 'nb'
    ...

    S = 0.5*b*h;
}

// Calculate area of triangle, modify and return parameter 'nthr'
void area_tria1_nthr(..., int *nthr, double b, double h, double *S) {

    // change parameter 'nthr'
    ...

    S = 0.5*b*h;
}

// Optimise calculation of area of triangle, for parameter 'nb' or 'nthr'
void bisect_area_tria1(..., double b, double h, double *S, int (*area_tria1)(double, double)) {
}

// Calculate area of triangle, modify and return parameter 'nb'
void area_tria2_nb(..., int *nb, double a, double b, double c, double *S) {

    // change parameter 'nb'
    ...

    S = sqrt(s*(s-a)*(s-b)*(s-c));
}


// Calculate area of triangle, modify and return parameter 'nthr'
void area_tria_2_nthr(..., int *nthr, double a, double b, double c, double *S) {

    // change parameter 'nthr'
    ...

    S = sqrt(s*(s-a)*(s-b)*(s-c));
}


// Optimise calculation of area of triangle, for parameter 'nb' or 'nthr'
void bisect_area_tria2(..., double a, double b, double c, double *S, int (*area_tria2)(double, double, double)) {
}

void main() {

    bisect_area_tria1(..., &nb,   b, h, &S, area_tria1_nb);
    bisect_area_tria1(..., &nthr, b, h, &S, area_tria1_nthr);

    bisect_area_tria2(..., &nb,   a, b, c, &S, area_tria2_nb);
    bisect_area_tria2(..., &nthr, a, b, c, &S, area_tria2_nthr);

}
mabalenk
  • 887
  • 1
  • 8
  • 17
  • 2
    "Avoiding code duplication" and "function overloading" have little to do with each other. – n. m. could be an AI Jun 19 '17 at 13:28
  • 2
    `void area_tria1_nb(..., int *nb, double b, double h, double *S)` sure that this is not a valid prototype. – Ajay Brahmakshatriya Jun 19 '17 at 13:28
  • 1
    As a sort of vague comment... maybe polymorphism will let you use a single function to carry out the slightly varied pieces of behaviour you want. – byxor Jun 19 '17 at 13:52
  • 1
    I'm not sure if there exists an elegant way, but what you could do is to implement inheritance/polymorphism and solve the interface differences that way. Function overloading is, arguably, a pretty dangerous and dumb thing to begin with. Often, the need to use it originates from muddy program design. So the question is perhaps not how to emulate it in other languages, but rather why no so few other languages support it. – Lundin Jun 19 '17 at 14:44

2 Answers2

7

The naive, simple way to do it:

#include <stdio.h>

void func_int (int x) { printf("%d\n", x); }
void func_char (char ch) { printf("%c\n", ch); }

#define func(param)          \
  _Generic((param),          \
    int:  func_int,          \
    char: func_char)(param)  \
                    
int main() 
{
  func(1);
  func((char){'A'});
}

This is type safe but only supports one parameter. At a glance this might seem insufficient.

If you want completely variable parameter lists, then you'd have to implement a way to parse variadic macros and __VA_ARGS__. Likely, it is possible. Likely it is ugly. Likely, this is not what you need.

The need for function overloading in general might be "an XY problem". You need to have functions with different parameter sets and you are convinced that function overloading is the best way to solve it. Therefore you ask how to do function overloading in C. Which is, as it turns out, not necessarily the best way.

A much better way would be to make a function interface with a single struct parameter, which can be adapted to contain all the necessary parameters. With such an interface you can use the above simple method based on _Generic. Type safe and maintainable.

Lundin
  • 195,001
  • 40
  • 254
  • 396
1

This we can exploit using array concepts.

1) Pass all the arguments(values) i.e. double constants as arrays, like this

double arr[]={a,b,c,h};
int trial_no; //1 or 2
bisect_area_tria2(..., &nthr,arr, &S, area_tria2_nthr,trial_no);

There in that function use array reference like this:

void area_tria2_nb(..., int *nb, double arr[], double *S,int trial_no) {

    // change parameter 'nb'
    ...
if(trial_no==2){
    S = sqrt(s*(s-arr[0])*(s-arr[1])*(s-arr[2]));
}
else
      S = 0.5*arr[1]*arr[3];
}

For 'nb' or 'nthr', simply pass the address of the corresponding variable. This is just the reference, may not be exact for your situation. If any doubt, ask again.

anil
  • 158
  • 1
  • 12
  • Yes, that's one option I have been thinking about: passing all arguments inside a data struct or an array of parameters. – mabalenk Jun 19 '17 at 14:56