Kcits970
Kcits970

Reputation: 642

C: How to find the greatest element of array using macros

I have 3 functions written in C.

int greatest_int(int* arr, int arr_size) {
    int g_int = arr[0]; //g_int is short for greatest_integer
    
    for (int i = 1; i < arr_size; i++)
        if (arr[i] > g_int)
            g_int = arr[i];
    
    return g_int;
}

long greatest_long(long* arr, int arr_size) {
    long g_long = arr[0];
    
    for (int i = 1; i < arr_size; i++)
        if (arr[i] > g_long)
            g_long = arr[i];
    
    return g_long;
}

double greatest_float(double* arr, int arr_size) {
    double g_float = arr[0];
    
    for (int i = 1; i < arr_size; i++)
        if (arr[i] > g_float)
            g_float = arr[i];
    
    return g_float;
}

The three functions basically follow the same process. The only difference is that they operate on different types. I want to reduce this duplicated code, and make one generic function that works for any primitive types.

One idea that I thought of is to use function macros. For example, the below macro works for any primitive types.

#define MAX(a, b) ((a) > (b)) ? (a) : (b)

Here's the macro that I tried writing.

#define GREATEST(type, arr, size) {\
    type g_value = arr[0];\
    for (int i = 1; i < (size); i++)\
        if (arr[i] > g_value)\
            g_value = arr[i];\
    return g_value;\
}

This macro does not represent any numeric value, it only expands to a bunch of statements.

How can I fix the macro so that it expands to the greatest value of the array? (Or is my macro approach flawed to begin with?)

Upvotes: 1

Views: 447

Answers (3)

niry
niry

Reputation: 3308

Each type of function must be declared seperately. You may use a macro to reduce repetition:

#include <stddef.h>
#define GREATEST(type) type greatest_##type(type* arr, size_t size) {\
  type g_value = arr[0];\
  for (size_t i = 1; i < size; i++)\
    if (arr[i] > g_value)\
      g_value = arr[i];\
  return g_value;\
}

GREATEST(int) // greatest_int
GREATEST(long) // greatest_long
GREATEST(double) // greatest_double

Or, if you prefer to be more explicit, use a macro only for the part of the code that repeats itself:

#include <stddef.h>
#define GREATEST g_value = arr[0];\
  for (size_t i = 1; i < size; i++)\
    if (arr[i] > g_value)\
      g_value = arr[i];\
  return g_value;

int greatest_int(int *arr, size_t size) { int GREATEST }
long greatest_long(long *arr, size_t size) { long GREATEST }
double greatest_double(double *arr, size_t size) { double GREATEST }

Test code:

#include <stdio.h>
#define ARR {1,2,5,8,2,9,3,0,2,-5,11,-12}
#define ARR_SIZE(a) (sizeof(a) / sizeof(a[0]))

int main() {
  int i[] = ARR;
  printf("%d\n", greatest_int(i, ARR_SIZE(i)));

  long l[] = ARR;
  printf("%ld\n", greatest_long(l, ARR_SIZE(l)));

  double d[] = ARR;
  printf("%f\n", greatest_double(d, ARR_SIZE(d)));
}

With C11, you can bring the 3 declarations into one macro, to automate type selection:

#define greatest(arr, size) _Generic(arr,\
    int*: greatest_int,\
    long*: greatest_long,\
    double*: greatest_double\
  )(arr, size)

Test code:

#include <stdio.h>
#define ARR {1,2,5,8,2,9,3,0,2,-5,11,-12}
#define ARR_SIZE(a) (sizeof(a) / sizeof(a[0]))

int main() {
  int i[] = ARR;
  printf("%d\n", greatest(i, ARR_SIZE(i)));

  long l[] = ARR;
  printf("%ld\n", greatest(l, ARR_SIZE(l)));

  double d[] = ARR;
  printf("%f\n", greatest(d, ARR_SIZE(d)));
}

Upvotes: 3

pmg
pmg

Reputation: 108986

Use the same mechanism as, for example, qsort(). You can extend the idea to time_t, struct whatever, ...

#include <stddef.h>
size_t greatest(void *arr, size_t nel, size_t width, int (*cmp)(void *a, void *b)) {
    unsigned char *a = arr;
    size_t index = 0;
    for (size_t i = 1; i < nel; i++) {
        if (cmp(a+i*width, a+index*width) > 0) index = i;
    }
    return index;
}

#include <stdio.h>

int cmp_int(void *a, void *b) { if (*(int*)a < *(int*)b) return -1; return *(int*)a != *(int*)b; }
int cmp_long(void *a, void *b) { if (*(long*)a < *(long*)b) return -1; return *(long*)a != *(long*)b; }
int cmp_double(void *a, void *b) { if (*(double*)a < *(double*)b) return -1; return *(double*)a != *(double*)b; }

int main(void) {
    int ia[] = {42, -1, 2022, -1000};
    long la[] = {42, -1, 2022, -1000};
    double da[] = {42, -1, 2022, -1000};
    printf("int: %d\n", ia[greatest(ia, 4, sizeof *ia, cmp_int)]);
    printf("long: %ld\n", la[greatest(la, 4, sizeof *la, cmp_long)]);
    printf("double: %f\n", da[greatest(da, 4, sizeof *da, cmp_double)]);
    return 0;
}

Upvotes: 1

pmg
pmg

Reputation: 108986

If you have a C11 compiler, use _Generic (though it doesn't really reduce the duplicated code)

#include <stdio.h>

#define GREATEST(a, b) _Generic((a),                   \
                               int*: greatest_int,     \
                               long*: greatest_long,   \
                               double*: greatest_float \
                               )(a, b)

int greatest_int(int* arr, size_t arr_size) {
    int g_int = arr[0]; //g_int is short for greatest_integer
    for (size_t i = 1; i < arr_size; i++) {
        if (arr[i] > g_int) g_int = arr[i];
    }
    return g_int;
}

long greatest_long(long* arr, size_t arr_size) {
    long g_long = arr[0];
    for (size_t i = 1; i < arr_size; i++) {
        if (arr[i] > g_long) g_long = arr[i];
    }
    return g_long;
}

double greatest_float(double* arr, size_t arr_size) {
    double g_float = arr[0];
    for (size_t i = 1; i < arr_size; i++) {
        if (arr[i] > g_float) g_float = arr[i];
    }
    return g_float;
}

int main(void) {
    printf("int: %d\n", GREATEST(((int[]){42, -1, 2022, -1000}), 4));
    printf("long: %ld\n", GREATEST(((long[]){42, -1, 2022, -1000}), 4));
    printf("double: %f\n", GREATEST(((double[]){42, -1, 2022, -1000}), 4));
    return 0;
}

Upvotes: 0

Related Questions