generic function to extract an array of a specific struct data member from an array of structs

StackOverflow https://stackoverflow.com/questions/23140374

  •  05-07-2023
  •  | 
  •  

Question

If I have a struct, say

struct mystuff {
    int a;
    int b;
    int c;
}

and then I have an array of those structs,

struct mystuff array[] = {...};

now say I would like to extract the struct data member, a, into its own array,

int* get_mystuff_a(struct mystuff *array, int n)
{
    int i;
    int *a_array;

    a_array = malloc(n*sizeof(int));

    for(i=0;i<n;i++) {
        a_array[i] = array[i].a;
    }
    return a;
}

now if i want to do the same for b, I have to write another function, but it will be almost identical apart from

b_array[i] = array[i].b;

is there a way i could write one generic function and specify the data member i want to extract as a parameter?

I think that a macro function may work, but I'm lost how you would actually write it

What would be even better is if you could also specify the data type, e.g. int, float, double etc. of the data member too - to make it even more generic

Was it helpful?

Solution

Writing this as a macro is easy, not that it is always a good idea.

In this case, I think it is better to write a macro that builds a function, instead of trying to do the work directly inline:

  1. Take your non-generic code.
  2. Add \ at the end of every line, because macros are one-line only.
  3. Add #define NAME(...) \ at the beginning.
  4. Replace the macro arguments where needed.
  5. Do not forget the extra parenthesis, if needed.

The result would be along the lines of:

#define GET_MYSTUFF(TYPE, FIELD) \
TYPE* get_mystuff_##FIELD(struct mystuff *array, int n) \
{ \
    int i; \
    TYPE *res_array; \
    res_array = malloc(n*sizeof(TYPE)); \
    for(i=0;i<n;i++) { \
        res_array[i] = array[i].FIELD; \
    } \
    return res_array; \
}

Now you create all your functions with:

GET_MYSTUFF(int, a)
GET_MYSTUFF(int, b)
GET_MYSTUFF(int, c)

You can even add the input array type as another parameter for the macro, but that is left as an exercise to the reader.

UPDATE: Note that this macro expand to a function definition, and you should not define your functions in the header file or else you will have duplicated definition errors. There are several solutions, pick your best:

  1. Make your functions static (static TYPE* get_mystuff_##FIELD(...). This way every compilation unit that includes your function will get a copy of the function.
  2. Move the definition of the functions to a mystuff.c file, and write only the function prototypes in mystuff.h. You can write these prototypes manually or create a macro for that:

    #define GET_MYSTUFF_PROTO(TYPE, FIELD) TYPE* get_mystuff_##FIELD(struct mystuff *array, int n);

  3. With a bit of extra work, and if you want a bit of fun, you can use X_Macros!

mystuff.h

#ifndef GET_MYSTUFF
#define GET_MYSTUFF(TYPE, FIELD) \
    TYPE* get_mystuff_##FIELD(struct mystuff *array, int n);
#endif

GET_MYSTUFF(int, a)
GET_MYSTUFF(int, b)
GET_MYSTUFF(int, c)

mystuff.c

/* include for prototypes */
#include "mystuff.h"

#undef GET_MYSTUFF
#define GET_MYSTUFF(TYPE, FIELD) \
TYPE* get_mystuff_##FIELD(struct mystuff *array, int n) \
{ \
    int i; \
    TYPE *res_array; \
    res_array = malloc(n*sizeof(TYPE)); \
    for(i=0;i<n;i++) { \
        res_array[i] = array[i].FIELD; \
    } \
    return res_array; \
}

/* include for definitions */
#include "mystuff.h"

OTHER TIPS

I came to a conclusion similar to rodrigo's one, the easiest thing to do would be to use a macro to declare your "getters" and then you could use them like

#include <stdio.h>
#include <stdlib.h>

struct mystuff {
    int a;
    int b;
    int c;
};

#define declare_getstuff_for(f) int* get_mystuff_##f(struct mystuff *array, int n) \
{ \
    int i; \
    int *a_array; \
\
    a_array = malloc(n*sizeof(int)); \
\
    for(i=0;i<n;i++) { \
        a_array[i] = array[i].f;  \
    }  \
    return a_array; \
}

declare_getstuff_for(a)
declare_getstuff_for(b)
declare_getstuff_for(c)


int main(void) {
    int *result1, *result2, *result3;
    struct mystuff array[3];
    struct mystuff el1, el2, el3;
    el1.a = el1.b = el1.c = 1;
    el2.a = el2.b = el2.c = 5;
    el3.a = el3.b = el3.c = 3;

    array[0] = el1;
    array[1] = el2;
    array[2] = el3;

    result1 = get_mystuff_a(array, 3);
    printf("%d, %d, %d\n", result1[0], result1[1], result1[2]);

    result2 = get_mystuff_b(array, 3);
    printf("%d, %d, %d\n", result2[0], result2[1], result2[2]);

    result3 = get_mystuff_c(array, 3);
    printf("%d, %d, %d\n", result3[0], result3[1], result3[2]);

    // Now free your memory :)

    return 0;
}

Relevant pieces to keep in mind are the token-pasting operator ## used to generate the function name, the usual macro substitution and the multi-line \ marker.

Other than that: be aware that bugs in code like this are usually hard to debug. Always prefer a clean, maintainable code where possible and use macros where strictly necessary.

Use with moderation :)

Try it out: http://ideone.com/bbYsJK

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top