-3

I want a C function that I can pass a structure to and it will return the number of fields/members within the structure.

At the moment I've found the easiest way is to just make a table and add to the table anytime I add a struct to any of the programs.

I guess I'm experiencing an XY problem (per the suggestions) So anyway, the reason why I would have use for this type function is because I'm wanting to send structures over a socket and to avoid the whole serialization process the way in which i'm trying to do this is by adding delimiters to the structure's field members. Like this:

0x001 --> hdrdata --> 0x003 --> hdrdata --> 0x017 --> 0x002 --> bodydata --> 0x003 --> bodydata --> 0x003 --> bodydata --> 0x004

As you can see there may be 1 bodydata or there may be 10 bodydata etc. The only other thing I can quickly think to do is add a member to my structs that would inform my header how many 'bodydata' transmissions will be coming through so the client knows how many 0x003's to expect.

Or (and i'm not asking for an answer to this but feel free if you are so inclined) can anyone recommend a better protocol design for sending structures over socket in unix? Most of this project is for my own learning so i'm not interested in using an already complete library/package solution otherwise i'd just be writing this in another language in the first place.

melpomene
  • 84,125
  • 8
  • 85
  • 148
PLuv
  • 9
  • 2
  • 2
    What would be the use of such a function? – melpomene May 20 '19 at 20:19
  • 6
    I don't think there's any way to do this in a function. C has no runtime structure introspection mechanisms. – Barmar May 20 '19 at 20:20
  • 1
    What problem are you really trying to solve? This sounds like an XY problem. – Barmar May 20 '19 at 20:21
  • There is no way to do this since a structure is just continues memory sometimes with padding. But really there is no use for this. How would you not know the number of fields in your structure? – Irelia May 20 '19 at 20:21
  • 2
    I think maybe you're experiencing a [XY problem](https://en.wikipedia.org/wiki/XY_problem). Your "solution" to X involves Y, so you ask for help with Y. Try asking for help with X :) – pmg May 20 '19 at 20:24
  • Show us what the struct look like. – klutt May 20 '19 at 20:28
  • 1
    You can't do what you're trying to do in C. If you want to use true C structs, you will have to write a dedicated serialization/deserialization routine for each struct -- there's no way to write one, generic one, that can automatically operate on any struct you pass it. (And even if you could write something like `for(i = 0; i < num_elements_of(structure); i++) { ... }`, your next question would be, what would you put in the `{ ... }`, how can you find out the name (and type) of the `i`th member of an arbitrary struct, and the answer is, there's no way to do that either.) – Steve Summit May 20 '19 at 20:53
  • Looking at problem requirements, sounds like a job for flexible array members. Have a size in your struct, end with variable-sized array at the end. See these answers (and comments about flexible array members in C99): https://stackoverflow.com/a/247040/4238087 – russianfool May 20 '19 at 21:31
  • 1
    Why would you ever come across a situation where you have access to members of a struct, but don't know how many they are? Just read the source and find out, structs exist exclusively for the programmer - neither the user nor the machine code cares about them. – Lundin May 21 '19 at 08:08

1 Answers1

1

This is very very ugly, but the only thing I can think of is an X-Macro:

#include <stdio.h>

#define MEMBERS \
    X(char a)   \
    X(float b)  \
    X(double c)

struct T {
#define X(x) x;
    MEMBERS
#undef X
};

int main(void)
{
    size_t n = 0
#define X(x) +1
    MEMBERS
#undef X
    ;

    printf("%zu members\n", n);
    return 0;
}

This expands to:

#include <stdio.h>

struct T {
    char a;
    float b;
    double c;
};

int main(void)
{
    size_t n = 0+1+1+1;

    printf("%zu members\n", n);
    return 0;
}

As pointed out by @HAL9000 in comments, you can generate both (the struct and the counter) at the same time in this way:

#include <stdio.h>

#define GENERATE_STRUCT(s) s;
#define GENERATE_SCOUNT(s) +1

#define set_struct(name)            \
struct name {                       \
    name##_members(GENERATE_STRUCT) \
};                                  \
                                    \
size_t name##_count(void)           \
{                                   \
    return 0                        \
    name##_members(GENERATE_SCOUNT) \
    ;                               \
}

#define foo_members(X)  \
    X(char a)           \
    X(float b)          \
    X(double c)

set_struct(foo)

int main(void)
{
    printf("%zu members\n", foo_count());
    return 0;
}

In this case is expanded to:

#include <stdio.h>

struct foo {
    char a;
    float b;
    double c;
};

size_t foo_count(void)
{
    return 0+1+1+1;
}

int main(void)
{
    printf("%zu members\n", foo_count());
    return 0;
}

Finally, another implementation provided by @Lundin using an enum instead of a variable:

#include <stdio.h>

#define MEMBERS       \
/*    type    name */ \
    X(char,   a)      \
    X(float,  b)      \
    X(double, c)      \

struct T {
  #define X(type, name) type name;
    MEMBERS
  #undef X
};

enum
{
  #define X(type, name) dummy_##name,
    MEMBERS
  #undef X
  MEMBER_COUNT
};

int main(void)
{
    printf("%d members\n", MEMBER_COUNT);
    return 0;
}

Expands to:

#include <stdio.h>

struct T {
    char a;
    float b;
    double c;
};

enum {
    dummy_a,
    dummy_b,
    dummy_c,
    MEMBER_COUNT
};

int main(void)
{
    printf("%d members\n", MEMBER_COUNT);
    return 0;
}
David Ranieri
  • 39,972
  • 7
  • 52
  • 94
  • Ugly yes, but so is writing failure prone repetitive code.. It could be improved by having a macro `make_struct(NAME, MEMBERS)` which generates both `struct foo {...}` and `const size_t foo_members = ...;` – HAL9000 May 20 '19 at 21:32
  • @HAL9000 thanks, what you are suggesting could be an improvement, the bad is that generating both at the same time will generate `foo_members` at global scope or `struct foo` at local scope, the other way you can put them where you want. – David Ranieri May 20 '19 at 21:40
  • A less ugly use of X macros IMO would be this: https://godbolt.org/z/7f8_ii. Also comes with compile-time calculation using a dummy enum. Feel free to use in your answer. – Lundin May 21 '19 at 08:28
  • @Lundin, the `enum` is a very good idea (added to my answer, thanks), what I don't like too much is to provide the type and the name of the member separated by a comma, is good for this example but not so good when using a more complex member, i.e. `int (*callback)(const void *, const void *)`, in this case your approach seems broken. – David Ranieri May 21 '19 at 09:46
  • 1
    Always separate X macro parameters by comma - which ones to actually use in each definition of X is optional. For your complex type scenario, the real problem is the lack of proper typedef. `typedef int (callback_t)(const void *, const void *)` then simply `X(callback_t*, d)`. – Lundin May 21 '19 at 10:03
  • @Lundin yes, a `typedef` will fix that issue ... still very ugly but more elegant :P – David Ranieri May 21 '19 at 10:15