简体   繁体   中英

C - struct* cast at runtime?

Assuming I have the following code:

struct str1
{
    int common1;
    char common2;
    char *common3;
    long int aaaaaaaa;
}

struct str2
{
    char bbbb;
    char *common3;
    int common1;
    char common2;
}

struct str3
{
    char ccccccccc[200];
    int common1;
    char common2;
    int dddddddd;
    int eeeeeeee;
    char *common3;
}

void somefunc1(struct str1 var)
{
    printf("%d %c %s", var.common1, var.common2, var.common3);
}

void somefunc2(struct str2 var)
{
    printf("%d %c %s", var.common1, var.common2, var.common3);
}

void somefunc3(struct str3 var)
{
    printf("%d %c %s", var.common1, var.common2, var.common3);
}

Can I somehow avoid code duplication and use a single generic function? Function calls are to be decided at runtime, therefore a macro is irrelevant. All the difference between the functions is the structs' names, not their members.

Since the position of the "common" structure members are not consistent between the structures, the answer is no. Because of this, there is really no actual commonality between the structure.

With these structure it is hard (if not impossible) to write a single function that will handle them all. But, if you change these structure to share those common elements from another structure then it will be possible.

struct base
{
    int common1;
    char common2;
    char *common3;
};

struct str1
{
    struct base b;
    long int aaaaaaaa;
};

struct str2
{
    struct base b;
    char bbbb;
};

struct str3
{
    struct base b;
    char ccccccccc[200];
    int dddddddd;
    int eeeeeeee;
};

Note: variable of struct base structure should be the first member of each struct , otherwise this technique won't work.

Now declare a function that take pointer to struct base .

void somefunc(struct base* var)
{
    printf("%d %c %s\n", var->common1, var->common2, var->common3);
}

Usages:

struct str1 s1 = { 1, 'a', "sfad"};
struct str2 s2 = { 2, 'b', "sdfazx"};
struct str3 s3 = { 3, 'c', "oiurotu"};

somefunc((struct base*) &s1);
somefunc((struct base*) &s2);
somefunc((struct base*) &s3);

Can I somehow avoid code duplication and use a single generic function?

No. That sounds analogous to a C++ template, which of course C doesn't have and has no analog for, unless you include the preprocessor. C11 has _Generic , but that yields a kind of function overloading: one function per type, with preprocessor support to call them by a single name.

Function calls are to be decided at runtime

In C and C++, a function call -- the path of control -- is determined at compile time. There is no runtime "decision".

All the difference between the functions is the structs' names, not their members.

Actually, a key difference is in each struct's layout. The names are at different locations. The C compiler converts names to locations. After compilation, there are no names and no types, nothing to indicate that a location was named common1 or was part of a struct. There is only a reference to a memory location. It's up to you to use the language correctly, to ensure that reference is to a location you intended.

therefore a macro is irrelevant.

The preprocessor lets you manipulate names before the compiler converts them to numbers. If you want to do anything in C "by name", a macro is the only game in town.

In C89, if two or more structures start with members of matching types in matching order (they share a Common Initial Sequence) and both are part of the same union, any part of the Common Initial Sequence may be inspected using the appropriate named members of any type sharing the CIS. Your example doesn't use matching types, so it doesn't qualify, but if it were to use matching types in matching order, it would. So far as I can tell, C89 compilers through the 1990s unanimously applied the same principle with pointers to structures (so if structure types S1 and S2 have a CIS, a pointer of either type could be used to access members of the CIS). While the Standard didn't explicitly specify such treatment, the easiest way by far for a compiler to ensure that the rule would be applied in all cases involving unions was to make it apply in all cases with pointers as well, and many people (likely including the authors of the Standard) expected that compilers would naturally do so whether explicitly required or not.

C99 requires that if code is going to use a pointer of one structure type to access a member of the CIS of another, a complete definition of the union type must be visible to let the compiler know of the potential aliasing between the types. Unfortunately, although this rule has a clear and obvious purpose (allowing programmers to exploit the CIS rule while allowing compilers to assume that accesses to totally unrelated structures won't alias) some compiler writers will assume that no pointer of a structure type will be used to access any other, even when a complete union type declaration containing both types is visible, and even in cases where the structures are, in fact, members of the same union object.

If you want to exploit the Common Initial Sequence rule, it may be necessary to use the -fno-strict-aliasing flag when using compilers that have one (even when not exploiting the CIS, using the flag may provide protection against compiler bugs). Code which exploits aliasing should endeavor to make it obvious to the compiler (eg by ensuring that a suitable union type is visible) but unless or until compiler writers start paying attention to such things, -fno-strict-aliasing will be necessary to accommodate their failure to do so.

Looks like I was wrong about restricting usage of the preprocessor:

#include <stdio.h>

struct str1
{
    int common1;
    char common2;
    char *common3;
    long int aaaaaaaa;
};

struct str2
{
    char bbbb;
    char *common3;
    int common1;
    char common2;
};

struct str3
{
    char ccccccccc[200];
    int common1;
    char common2;
    int dddddddd;
    int eeeeeeee;
    char *common3;
};

#define somefunc(var) \
    printf("%d %c %s\n", var.common1, var.common2, var.common3);

int main()
{
    struct str1 var1 = {1,'a',NULL, 4};
    struct str2 var2 = {'b',NULL,2,'b'};
    struct str3 var3;
    var3.common1 = 3;
    var3.common2 = 'c';
    var3.common3 = NULL;


    somefunc(var1);
    somefunc(var2);
    somefunc(var3);

}

output:

1 a (null)
2 b (null)
3 c (null)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM