0

Prototype: size_t offsetof(type, member);

I know first parameter is type, what if I have just the name in string not the type. I want to get the offsetof a member with just string literals

I want help from the community, how to achieve this.

ex:

#include <stdio.h>
#include <stddef.h>

typedef struct example_ {
    void *member1;
    void *member2;
} example;

unsigned int
offset_gen(char *ds, char *member)
{
    return (offsetof(ds, member));
}

void
main()
{
    printf ("\n %d", offset_gen("example", "member1"));
    printf ("\n %d", offset_gen("example", "member2"));
}
chqrlie
  • 131,814
  • 10
  • 121
  • 189
Anil kumar
  • 19
  • 2
  • This approach is impossible. I think that something like a type information file other than the source code is necessary. – BLUEPIXY Nov 19 '16 at 15:55
  • 1
    `offsetof` works at compile time. If you're trying to use string constants because you want to be able to access structure members by name at run time, the compiler's not going to do that for you. (You're looking for *introspection*, which C doesn't have.) It would be a nuisance, but you could maintain your own dictionary mapping member names to offsets, and look things up by name in that. – Steve Summit Nov 19 '16 at 16:01
  • And why not use the type? Your function is superfuous anyway. C is statcally typed, so you always know the type of an object at compile-time. – too honest for this site Nov 19 '16 at 16:03
  • I know offset_gen is wrong, i need to know is it possible to convert/cast string "example" to type example (which is of type struct example_), I have real use case for it.... :( – Anil kumar Nov 19 '16 at 16:26
  • Using void pointers is never a good idea. Only had to use them under duress a few times in my life. Avoid the void ! – Ed Heal Nov 19 '16 at 16:27
  • when you write a generic utility void pointer makes lot of sense... it is there for a purpose... :) – Anil kumar Nov 19 '16 at 16:37
  • @Anilkumar Nope, use void pointer must be justified with strong purpose ! What you want here is not a feature of C. C use union + enum to do what you want. There's no magic like in python. Never use void pointer like a joker... or update your question to add why you want to this we could help you to find a better way ? – Stargateur Nov 19 '16 at 16:42
  • 1
    In your running program, you could generate the C code, run the compiler, dynamically load the object file, call the function, unload the object file, and clean up. It would work. It's one of the few ways that would work. It isn't a serious suggestion — as in, I don't think that is how you should do it. But if you really, really must, it would work. But it would be better to rethink your design. – Jonathan Leffler Nov 19 '16 at 16:56
  • @JonathanLeffler it will not work. `offsetof` requires a type, not a variable. Dynamic loading can give us only variables, not actual types. – skrtbhtngr Nov 19 '16 at 17:41
  • 1
    @skrtbhtngr If we use %s to output a string into some constructed C code prior to on-the-fly complication, I daresay we can do the same thing with the type name! – Steve Summit Nov 19 '16 at 17:47
  • structure is generated from json file at compile time i really wanted it to be VOID *. from json; i can get the member name as string from json. need its offset so that i can access it. Problem statement i am trying to address is i have name of the structure and member is in string format. I need the offset. I understand it cannot be solved using offsetof macro as it expects type. there is no way i could convert a string which is exactly matching the structure name as structure type. I am trying to get work around for the problem. – Anil kumar Nov 19 '16 at 19:55
  • @Anilkumar: Can you easily generate a file with format `type member1 member2 member3 ... memberN` -- i.e. each type on one line, with all member names, space-separated, in any order? Or in some other similar simple format, like maybe each line containing one `type member` pair, with as many lines as necessary? If you can, then you can simplify my answer to something that is quite easy to use and maintain: the complex/nasty part is extracting the type and member names from C source files, you see. Avoid that, and stuff gets *easy*. – Nominal Animal Nov 20 '16 at 14:00
  • @NominalAnimal as of now i am generating... Its working fine. The problem is text segment has increased so i was looking out for alternatives – Anil kumar Nov 21 '16 at 05:32

2 Answers2

2

Here is a real-world example on how one could begin to implement this.

Note: This is not application-ready code. I wrote this from scratch, and as such, should only be considered as a proof-of-concept; something one would use as a basis for a discussion in a development team. This version does not use a proper C parser, but assumes certain conventions used in the C source.

All of the files included in this post are licensed under CC0, i.e. dedicated to public domain. Remember, however, that there are no guarantees: if it breaks or breaks something else, don't blame me.

Essentially, we use a Bash+Awk script to generate a C program, that when compiled and run, generates a hash table with precalculated data, and a member_offset() function one can use to find member offsets of structure types, with structure type and member name given as strings.

For illustration, this is a complete working example, including a Makefile.

File mytypes.h contains the types we are interested in:

#include <stdlib.h>

struct type1 {
    char         one, two[2];
    float        three;
    int        (*callback)(const char *, void *, size_t);
} __attribute__((__packed__));

struct type2 {
    char         four;
    struct type1 five;
    int          six, seven[3];
};

You don't need to stuff the types into a single header file; you only need to edit the Makefile if you have them in different files. One requirement, however, is that all types are included in header files, that can be #include'd in the intermediate C generator file, compiled and run at build time only.

For illustration, we have a main.c that lets user specify struct type and member name on the command line, with the offset printed to standard output:

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

extern size_t member_offset(const char *type, const char *name, const size_t not_found);

int main(int argc, char *argv[])
{
    int    arg;
    size_t offset;

    if (argc < 3 || !(argc & 1) || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
        fprintf(stderr, "\n");
        fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv[0]);
        fprintf(stderr, "       %s TYPE NAME [ TYPE NAME ... ]\n", argv[0]);
        fprintf(stderr, "\n");
        return EXIT_SUCCESS;
    }

    for (arg = 1; arg < argc - 1; arg += 2) {
        offset = member_offset(argv[arg], argv[arg + 1], ~(size_t)0);
        if (errno) {
            fprintf(stderr, "struct %s unknown, or has no member %s.\n", argv[arg], argv[arg + 1]);
            return EXIT_FAILURE;
        }

        printf("struct %s has member %s at offset %zu.\n", argv[arg], argv[arg + 1], offset);
        fflush(stdout);
    }

    return EXIT_SUCCESS;
}

To build the project, we use a Makefile. Note that the indents are Tabs, not spaces; make is picky that way.

CC      := gcc
CFLAGS  := -Wall -O2
LDFLAGS :=

.PHONY: all clean

all: clean example

clean:
    rm -f *.o example member-offset.c member-offset-generator.c member-offset-generator

member-offset.c: mytypes.h
    rm -f $@ member-offset-generator member-offset-generator.c
    ./member-offset-generator.bash mytypes.h:type1 mytypes.h:type2 > member-offset-generator.c
    $(CC) $(CFLAGS) member-offset-generator.c $(LDFLAGS) -o member-offset-generator
    ./member-offset-generator > $@
    rm -f member-offset-generator member-offset-generator.c

%.o: %.c
    $(CC) $(CFLAGS) -c $^

example: member-offset.o main.c
    $(CC) $(CFLAGS) $^ $(LDFLAGS) -o $@

Note the member-offset.c rule above. It refers to the autogenerated C source file, that will contain the member_offset() function. It is recompiled if it does not exist yet, and also whenever mytypes.h is modified.

The command ./member-offset-generator.bash mytypes.h:type1 mytypes.h:type2 > member-offset-generator.c uses the fourth file not shown yet (see further below), to examine mytypes.h, and include struct type1 and struct type2 in the type database hash tables. The output is member-offset-generator.c, a C program that when compiled and run, generates the C code we actually want. It might be better to split this rule into separate rules, but for now, I made it automatically compile and run member-offset-generator.c, and delete it (as it is only needed to output member-offset.c once).

The shell script that generates that intermediate C program, member-offset-generator.bash, is pretty complicated:

#!/bin/bash
export LANG=C LC_ALL=C

[ -n "$CC"     ] || export CC="gcc"
[ -n "$CFLAGS" ] || export CFLAGS="-Wall -O2"

if [ $# -lt 1 ] || [ "$1" = "-h" ] || [ "$1" = "--help" ]; then
    exec >&2
    printf '\n'
    printf 'Usage: %s [ -h | --help ]\n' "$0"
    printf '       %s HEADER[:TYPE] ...\n' "$0"
    printf '\n'
    printf 'This script autogenerates a C program, that when run,\n'
    printf 'emits a C implementation of function member_offset()\n'
    printf 'which returns the offset of "member" within type "struct type".\n'
    printf '\n'
    printf 'The generated C program includes all HEADER files,\n'
    printf 'but each one only once. Only the specified struct types\n'
    printf 'will be supported by the final function.\n'
    printf '\n'
    exit 1
fi

function hash_of_function() {
    sed -e 's|        ||' << END
        /* DJB2 xor hash, http://www.cse.yorku.ca/~oz/hash.html */
        size_t hash_of(const void *data, const size_t size)
        {
            const unsigned char       *p = (const unsigned char *)data;
            const unsigned char *const q = (const unsigned char *)data + size;
            size_t                     h = 5381;
            while (p < q)
                h = ((h << 5) + h) ^ (*(p++));
            return h;
        }
END
}

# Emit all headers as includes, but each one only once.
printf '%s\n' "$@" | awk \
   'BEGIN {
        RS="\n"
        FS=":"
        split("", seen)

        printf "#include <stdlib.h>\n"
        printf "#include <stddef.h>\n"
        printf "#include <string.h>\n"
        printf "#include <stdio.h>\n"
        seen["stdlib.h"] = 1
        seen["stddef.h"] = 1
        seen["string.h"] = 1
        seen["stdio.h"] = 1
    }
    {
        header = $1
        sub(/^[<"]/, "", header)
        sub(/[>"]$/, "", header)
        if (length(header) > 0 && !(header in seen)) {
            seen[header] = 1
            if (substr($1, 1, 1) == "<")
                printf "#include <%s>\n", header
            else
                printf "#include \"%s\"\n", header
        }
    }'

# emit the hash function as a string.
printf '\nstatic const char hash_of_def[] =\n'
hash_of_function | sed -e 's|\\|\\\\|g; s|"|\\"|g; s|^|    "|g; s|[\t\v\f ]*$|\\n"|g'
printf '    ;\n\n'
# and the hash function itself.
hash_of_function

# emit structures and code used by the generator itself.
sed -e 's|^    ||' <<END

    struct type_member_list {
        struct type_member_list *next;
        size_t                   offset;
        size_t                   hash;
        size_t                   namelen;
        char                     name[];
    };

    struct type_list {
        struct type_list        *next;
        struct type_member_list *members;
        size_t                   hash;
        size_t                   slots;
        size_t                   typelen;
        char                     type[];
    };

    static size_t type_list_size(const struct type_list *list)
    {
        size_t result = 0;
        while (list) {
            ++result;
            list = list->next;
        }
        return result;
    }

    static size_t type_member_list_size(const struct type_member_list *list)
    {
        size_t result = 0;
        while (list) {
            ++result;
            list = list->next;
        }
        return result;
    }


    static struct type_list *types = NULL;


    static void add_type_member(const char *type, const char *name, const size_t offset)
    {
        const size_t typelen = (type) ? strlen(type) : 0;
        const size_t namelen = (name) ? strlen(name) : 0;

        struct type_list        *list = NULL, *temp;
        struct type_member_list *member;

        if (!typelen || !namelen) {
            if (!typelen)
                fprintf(stderr, "Error: add_type_member() called with empty type.\n");
            if (!namelen)
                fprintf(stderr, "Error: add_type_member() called with empty name.\n");
            exit(EXIT_FAILURE);
        }

        /* Find the list for the specified type. */
        for (temp = types; temp != NULL; temp = temp->next)
            if (temp->typelen == typelen && !strcmp(temp->type, type)) {
                list = temp;
                break;
            } 

        /* If this is a new type, create a new list. */
        if (!list) {
            list = malloc(sizeof (struct type_list) + typelen + 1);
            if (!list) {
                fprintf(stderr, "Error: Out of memory.\n");
                exit(EXIT_FAILURE);
            }
            memcpy(list->type, type, typelen);
            list->type[typelen] = '\0';
            list->typelen = typelen;
            list->hash = hash_of(type, typelen);
            list->slots = 0;
            list->members = NULL;

            /* Prepend to global types list. */
            list->next = types;
            types = list;
        }

        /* Create a new member. */
        member = malloc(sizeof (struct type_member_list) + namelen + 1);
        if (!member) {
            fprintf(stderr, "Error: Out of memory.\n");
            exit(EXIT_FAILURE);
        }
        memcpy(member->name, name, namelen);
        member->name[namelen] = '\0';
        member->namelen = namelen;
        member->hash = hash_of(name, namelen);
        member->offset = offset;

        /* Prepend to member list. */
        member->next = list->members;
        list->members = member;
    }

    void add_types_and_members(void)
    {
END

ignorefirst=$'<"'
ignorelast=$'>"'

# Extract the member names from each structure.
for pair in "$@"; do
    name="${pair#*:}"
    [ ":$name" = ":$pair" ] && continue
    [ -n "$name" ] || continue

    file="${pair%%:*}"
    file="${file#[$ignorefirst]}"
    file="${file%[$ignorelast]}"

    $CC $CFLAGS -P -E "$file" | \
    sed -e '/#/ d' | tr -s '\t\n\v\f\r ' '      ' | \
    sed -e 's|\(struct [^ ]*\) {|\n\1 {\n|g; s|}|\n}\n|g; s| *;|\n|g; s|)([^)]*)||g' | \
    awk -v name="$name" \
   'BEGIN {
        RS = " *\n"
        FS = " *,"
        split("", members)
    }

    $0 == ("struct " name " {") {
        inside = 1
        next
    }

    $0 == "}" {
        inside = 0
        next
    }

    inside {
        for (i = 1; i <= NF; i++) {
            member = $i
            sub(/\[[^\[\]]*\]/, "", member)
            sub(/^.*[ \*(]/, "", member)
            if (!(member in members))
                members[member] = member
        }
    }

    END {
        for (member in members)
            printf "    add_type_member(\"%s\", \"%s\", offsetof(struct %s, %s));\n", name, member, name, member
    }' || exit 1
done

# emit the rest of the generator code.
sed -e 's|^    ||' <<END
    }

    size_t type_slots(struct type_list *list)
    {
        const size_t  size = type_list_size(list);
        const size_t  max_slots = 4 * size + 1;
        size_t        slots = size;
        size_t       *used, i, n;

        struct type_list *item;

        used = malloc(max_slots * sizeof used[0]);
        if (!used) {
            fprintf(stderr, "Error: Out of memory.\n");
            exit(EXIT_FAILURE);
        }

        while (1) {
            if (slots >= max_slots) {
                fprintf(stderr, "Error: Weak hash function; hash table grows too large.\n");
                fprintf(stderr, "       (Need more than %zu slots for %zu data entries.)\n", max_slots, size);
                exit(EXIT_FAILURE);
            }

            for (i = 0; i < slots; i++)
                used[i] = 0;

            for (item = list; item != NULL; item = item->next)
                ++used[item->hash % slots];

            n = used[0];
            for (i = 1; i < slots; i++)
                if (used[i] > n)
                    n = used[i];

            if (n <= 1) {
                free(used);
                return slots;
            }

            slots++;
        }
    }


    size_t generate_type(const char *type, struct type_member_list *list, const size_t size)
    {
        /* Maximum size for current hash table. */
        const size_t  max_slots = 4*size + 1;
        size_t        slots = size;
        size_t       *used, i, n;

        struct type_member_list *item;

        if (size < 1)
            return 0;

        used = malloc(max_slots * sizeof used[0]);
        if (!used) {
            fprintf(stderr, "Error: Out of memory.\n");
            exit(EXIT_FAILURE);
        }

        while (1) {

            if (slots >= max_slots) {
                fprintf(stderr, "Error: Weak hash function; hash table grows too large.\n");
                fprintf(stderr, "       (Need more than %zu slots for %zu data entries.)\n", max_slots, size);
                exit(EXIT_FAILURE);
            }

            /* Clear slot use counts. */
            for (i = 0; i < slots; i++)
                used[i] = 0;

            /* Count slot occupancies. */
            for (item = list; item != NULL; item = item->next)
                ++used[item->hash % slots];

            /* Find the maximum slot occupancy. */
            n = used[0];
            for (i = 1; i < slots; i++)
                if (used[i] > n)
                    n = used[i];

            /* Suitable size? */
            if (n <= 1)
                break;

            /* Try a larger hash table, then. */
            slots++;
        }

        free(used);

        /* Print out the contents of this hash table. */
        printf("static const struct member  struct_%s_members[%zu] = {\n", type, slots);
        for (i = 0; i < slots; i++) {
            for (item = list; item != NULL; item = item->next)
                if (item->hash % slots == i)
                    break;
            if (item) {
                printf("    { .offset  = %zu,\n", item->offset);
                printf("      .hash    = %zu,\n", item->hash);
                printf("      .namelen = %zu,\n", item->namelen);
                printf("      .name    = \"%s\" },\n", item->name);
            } else {
                printf("    { .offset  = 0,\n");
                printf("      .hash    = 0,\n");
                printf("      .namelen = 0,\n");
                printf("      .name    = NULL },\n");
            }
        }
        printf("};\n\n");

        return slots;
    }

    int main(void)
    {
        struct type_list *list;
        size_t            main_slots, i;

        add_types_and_members();

        printf("#include <stdlib.h>\n");
        printf("#include <string.h>\n");
        printf("#include <errno.h>\n");
        printf("\n");
        printf("struct member {\n");
        printf("    const size_t      offset;\n");
        printf("    const size_t      hash;\n");
        printf("    const size_t      namelen;\n");
        printf("    const char *const name;\n");
        printf("};\n");
        printf("\n");
        printf("struct type {\n");
        printf("    const size_t               hash;\n");
        printf("    const size_t               namelen;\n");
        printf("    const size_t               members;\n");
        printf("    const struct member *const member;\n");
        printf("    const char *const          name;\n");
        printf("};\n");
        printf("\n");
        printf("%s\n", hash_of_def);
        printf("\n");

        for (list = types; list != NULL; list = list->next)
            list->slots = generate_type(list->type, list->members, type_member_list_size(list->members));

        main_slots = type_slots(types);

        printf("static const size_t       num_types = %zu;\n", main_slots);
        printf("static const struct type  types[%zu] = {\n", main_slots);
        for (i = 0; i < main_slots; i++) {
            for (list = types; list != NULL; list = list->next)
                if (list->hash % main_slots == i)
                    break;

            if (list) {
                printf("    { .hash    = %zuUL,\n", list->hash);
                printf("      .namelen = %zu,\n", list->typelen);
                printf("      .members = %zu,\n", list->slots);
                printf("      .member  = struct_%s_members,\n", list->type);
                printf("      .name    = \"%s\" },\n", list->type);
            } else {
                printf("    { .hash    = 0,\n");
                printf("      .namelen = 0,\n");
                printf("      .members = 0,\n");
                printf("      .member  = NULL,\n");
                printf("      .name    = NULL },\n");
            }
        }
        printf("};\n");
        printf("\n");
        printf("size_t member_offset(const char *type, const char *name, const size_t not_found)\n");
        printf("{\n");
        printf("    const size_t  typelen = (type) ? strlen(type) : 0;\n");
        printf("    const size_t  namelen = (name) ? strlen(name) : 0;\n");
        printf("\n");
        printf("    if (typelen > 0 && namelen > 0) {\n");
        printf("        const size_t  typehash = hash_of(type, typelen);\n");
        printf("        const size_t  t = typehash %% num_types;\n");
        printf("        if (types[t].hash == typehash &&\n");
        printf("            types[t].namelen == typelen &&\n");
        printf("            !strcmp(types[t].name, type)) {\n");
        printf("            const size_t         namehash = hash_of(name, namelen);\n");
        printf("            const struct member *const member = types[t].member + (namehash %% types[t].members);\n");
        printf("            if (member->hash == namehash &&\n");
        printf("                member->namelen == namelen &&\n");
        printf("                !strcmp(member->name, name)) {\n");
        printf("                errno = 0;\n");
        printf("                return member->offset;\n");
        printf("            }\n");
        printf("        }\n");
        printf("    }\n");
        printf("    errno = ENOENT;\n");
        printf("    return not_found;\n");
        printf("}\n\n");

        return EXIT_SUCCESS;
    }
END

This version uses djb2 xor hash function. If you use some other one, write it in C after the sed ... <<END line, ending with END at the start of the line, in the hash_of_function Bash function. (The sed is there just to remove eight spaces of indentation, making the script slightly easier to read.) It is fast, and simple. Whether it suffices for any real world use cases, I don't know; for some test header files I threw at it, it worked just fine.

Both the known structure types, and the members of each known structure type, are stored in hash tables. Since the entries are small, and this is done for performance gains, the hash tables have at most one entry per hash table slot, with some empty slots. This means at most two probes (one probe per tble) per lookup. The intermediate C program searches for the smallest size (number of slots) that puts at most one type or member per entry, so that simple arrays can be used. This yields constant time ($O(1)$) complexity for the hash table search. Because we do need to calculate the hashes from the two supplied strings, technically the time complexity depends on their lengths. Which means, you do need to use a fast hash function; the hash function does not need to be perfect or cryptographically secure.

The one probe to each hash table first compares the hash, then the string length, and finally the string itself, to ensure no false matches. This means that when a match is found, exactly two strcmp()s are made.

If you know the function will never be called to find the offset of a non-existent member, or with a non-existent type, you can safely omit the strcmp() checks.

You can examine the generated intermediate program by running

./member-offset-generator.bash mytypes.h:type1 mytypes.h:type2 | less

As you probably have noticed at this point, writing a C program that generates C code is .. complicated; and that writing a script that generates a C program that generates C code is .. typically not worth the maintenance effort. However, it is definitely doable, although there is a high risk that maintaining the script requires more effort than the generated code is worth. Be aware of this risk.

The default action in the Makefile (when you run make) is the same as make clean example. If you save all the above to their respective files, and then run

make

you should see something like

rm -f *.o example member-offset.c member-offset-generator.c member-offset-generator
rm -f member-offset.c member-offset-generator member-offset-generator.c
./member-offset-generator.bash mytypes.h:type1 mytypes.h:type2 > member-offset-generator.c
gcc -Wall -O2 member-offset-generator.c  -o member-offset-generator
./member-offset-generator > member-offset.c
rm -f member-offset-generator member-offset-generator.c
gcc -Wall -O2 -c member-offset.c
gcc member-offset.o main.c  -o example

because make outputs the commands it runs, and I didn't hide any of them (by prepending the corresponding command with @).

Then, if you run

./example type1 one  type1 two  type1 three  type1 callback

the example program should output

struct type1 has member one at offset 0.
struct type1 has member two at offset 1.
struct type1 has member three at offset 3.
struct type1 has member callback at offset 7.

On x86-64, which is an LP64 architecture (int being 32-bit, and long and pointers 64-bit), running

./example type2 four type2 five type2 six type2 seven

outputs

struct type2 has member four at offset 0.
struct type2 has member five at offset 1.
struct type2 has member six at offset 16.
struct type2 has member seven at offset 20.

On x86-64, one can compile 32-bit code by using the -m32 GCC option. So, running

make CFLAGS="-Wall -O2 -m32" clean all

and then

./example type2 four type2 five type2 six type2 seven

outputs

struct type2 has member four at offset 0.
struct type2 has member five at offset 1.
struct type2 has member six at offset 12.
struct type2 has member seven at offset 16.

This can be extended to allow for some kind of introspection, if we add support for the types of the structure members in the hash table entries.

However, I cannot stress enough how important it is to consider the maintenance efforts needed to keep this working. If the codebase has a strict set of coding standards, and someone knows this code-generator-generator well enough to regularly check it parses the structures correctly, and more than one developer can maintain it long-term, then sure; I don't see why not use something like this. Otherwise, it may become a heavy burden, that may pull down the rest of the project with it. Especially if there is just one developer who has sufficient knowledge to maintain the code-generator-generator, and they happen to leave. No project should be dependent on a specific person, in my opinion.

If you have any specific questions, feel free to ask them in comments, and I'll try to explain. However, I will not explain the entire member-offset-generator.bash script line-by-line (as I have occasionally done in the past for other examples I've written), because at 435 lines, with its inherent inception-like (C code output by a C program created by the script) complexity, it is not worth the effort to anyone.

Nominal Animal
  • 38,216
  • 5
  • 59
  • 86
  • Thanks for the tremendous effort you have put to address the problem. It is much appreciated. This idea will work for me. – Anil kumar Nov 21 '16 at 05:41
1

I think you are using offsetof in the wrong way. You have to pass the structure type and the member names to the macro, and not strings containing their names.

For instance, if your struct is:

typedef struct example_ {
    void *member1;
    void *member2;
}example;

Then, you can calculate the offset of member1 as:

offsetof(example, member1)

But if you still want to use string literals as you have, then you have to compare the member parameter in your offset_gen with the struct member names manually and call the corresponding macro.

Example:

unsigned int
offset_gen(char *ds, char *member)
{
    if(!strcmp(ds,"example"))
    {
        if(!strcmp(member,"member1"))
            return (offsetof(example, member1));
        else if(!strcmp(member,"member2"))
            return (offsetof(example, member2));
    }
    return -1;       // if no match for input paramters is found
}

You can even try something like this, or even this (if you want to test your limits!).

Community
  • 1
  • 1
skrtbhtngr
  • 2,223
  • 23
  • 29
  • You may want to change `member` to either `member1` or `member2` or showing examples of both. Other than that, you are not explaining how `offsetof` is working, nor you are explaining what is the difference between compile and runtime calculations, etc. – Peter Varo Nov 19 '16 at 16:01
  • With all humbleness, I know how to use offsetof macro, But what i need is a solution which can get me offset of a member in a structure with there name in form of char array or strings – Anil kumar Nov 19 '16 at 16:28
  • I have added that part. – skrtbhtngr Nov 19 '16 at 16:29
  • This solution will not help because i have huge set of such structures, I will not be able to do strcmp... I dont want to maintain dictionary too. the whole this is needed for performance optimization... – Anil kumar Nov 19 '16 at 16:32
  • @Anilkumar: Then set up build-time scripts that generate the necessary code from the actual source files. Use a hash table, and compute the hash of the member name string supplied, and you won't need to do many `strcmp()`s; only one, if you choose a good hash function. If all member names are at most 12 characters long, and use only lowercase letters and digits and underscore, you can use base40 to encode them into `uint64_t`s, so you don't need a single `strcmp()`. – Nominal Animal Nov 19 '16 at 16:51
  • 1
    @Anilkumar You should learn abut hash tables. They can be *very* efficient. And no matter what you do, someone, somewhere, is going to have to maintain a dictionary and do lookups -- if not you, then the compiler and run-time. You should also ask whether you even need to be using structs here. If all your accesses need to be by name, you probably want an associative array. You can implement this yourself (probably using a hash table), or switch to C++ and use std::map. – Steve Summit Nov 19 '16 at 17:04
  • @NominalAnimal search its expensive, I have used tiger hash which is much efficient then any other hashing algorithms. Please help me if you guys could think of any work around to get offsetof using string(structure name, member name) it will be much appreciated. For me performance is highest priority. I am solving this problem as of now by generating .h & .c, .h contains structure definition and .c contains functions (ex: size_t examplemember1()) for each member to get its offset. I just hoped i might get better alternative solution. I am using macro to call function it uses ## to call – Anil kumar Nov 19 '16 at 20:06
  • @skrtbhtngr: Related to your last line, *"can try something like"*, I'll just add, *"or, if you are insane, or really really really need this facility, something like [my answer](http://stackoverflow.com/a/40704804/1475978)"*. – Nominal Animal Nov 20 '16 at 13:55
  • This is really insane! I've added a link. – skrtbhtngr Nov 20 '16 at 14:18
  • Returning `-1` as an `unsigned int` is a tad inconsistent and cumbersome to test, you should make the `offset_gen` return an `int` or a `ptrdiff_t`. Also make the arguments `const char *`. – chqrlie Nov 20 '16 at 15:12
  • Why is that so? We can compare the return value easily with `-1U`. – skrtbhtngr Nov 20 '16 at 15:15