Thread: Opaque pointer in a struct factory

  1. #1
    Registered User
    Join Date
    Sep 2014
    Posts
    121

    Opaque pointer in a struct factory

    Hello, I am trying to make a factory of a struct, that will be modified in runtime, but apparently I can't figure out how to do it in compile time.
    Here is the opaque ptr header
    Code:
    #ifndef NODEOPTR
    #define NODEOPTR
    struct Node;
    #endif // NODEOPTR
    And it's implementation
    Code:
    #include "nodeoptr.h"
    #include <stdlib.h>
    #include <stdio.h>
    
    #define NODE1
    
    struct Node{
    #ifdef NODE1
        int integer;
    #elif NODE2
        char character;
    #elif NODE3
        float real;
    #endif
    };
    
    
    static struct Node* nodefactory(int type) {
        if ( type == 1 ) {
            struct Node* n = (struct Node*) malloc(sizeof(struct Node));
            n->integer = 10000;
            return n;
        }
        /* etc. ... */
    }
    
    
    int main(int argc, char** argv) {
        struct Node* n = nodefactory(1);
        printf("%d \n", n->integer);
    
    }
    The problem is that, I can't modify it in runtime. I want to enable/disable the fields in the struct in runtime... But is it really possible?

  2. #2
    Registered User
    Join Date
    Jun 2011
    Posts
    4,513
    I'm not sure I fully understand all the details your requirements, but perhaps you could look into using unions.

    A basic illustrative example:

    Code:
    #include <stdio.h>
    #include <stdlib.h>
    
    enum type_e
    {
        TYPE_INT,
        TYPE_CHAR,
        TYPE_FLOAT
    };
    
    union type_u
    {
        int i;
        char c;
        float f;
    };
    
    struct Node
    {
        union type_u data;
        int type;
    };
    
    struct Node *node_factory(int type, union type_u data);
    
    int main(void)
    {
        struct Node *n1 = node_factory(TYPE_INT, (union type_u) {.i = -1000} );
        struct Node *n2 = node_factory(TYPE_CHAR, (union type_u) {.c = 'Z'} );
        struct Node *n3 = node_factory(TYPE_FLOAT, (union type_u) {.f = 5.25} );
    
        if(n1 != NULL)
        {
            printf("%d\n", n1->data.i);
            free(n1);
        }
    
        if(n2 != NULL)
        {
            printf("%c\n", n2->data.c);
            free(n2);
        }
    
        if(n3 != NULL)
        {
            printf("%.2f\n", n3->data.f);
            free(n3);
        }
    
        return 0;
    }
    
    struct Node *node_factory(int type, union type_u data)
    {
        struct Node *n = malloc(sizeof(*n));
    
        if(n != NULL)
        {
            switch(type)
            {
                case TYPE_INT:
                    n->type = TYPE_INT;
                    n->data.i = data.i;
                    return n;
                case TYPE_CHAR:
                    n->type = TYPE_CHAR;
                    n->data.c = data.c;
                    return n;
                case TYPE_FLOAT:
                    n->type = TYPE_FLOAT;
                    n->data.f = data.f;
                    return n;
                default:
                    free(n);
            }
        }
    
        return NULL;
    }
    The type and data of an existing node can easily be changed with a single function.

    Is this along the lines of what you were thinking?

  3. #3
    Registered User
    Join Date
    Sep 2014
    Posts
    121
    Partially yes. But unions still have the sizeof the largest member which means, if I have 2 000 000 instances they will still be float size. But you refreshed me with the unions... They are the solution I guess.

  4. #4
    Registered User
    Join Date
    Sep 2014
    Posts
    121
    I kind of found a workaround:
    Code:
    #include "nodeoptr.h"
    #include <stdio.h>
    #include <stdlib.h>
    
    #define Node_(Name, Type) \
        typedef struct { \
        Type type;              \
    } Name##_##Type;
    
    
    int main(int argc, char** argv) {
        Node_(Node, int) ;
        Node_(Node, char);
        Node_(Node, float);
    
        Node_int n;
        Node_char c;
        Node_float d;
    
        n.type = 10000;
        c.type = 'c';
        d.type = 10.2f;
    
        printf("%d is node\n", n.type);
        printf("%c is node\n", c.type);
        printf("%f is node\n", d.type);
        return 0;
    }

  5. #5
    Registered User
    Join Date
    Jun 2011
    Posts
    4,513
    All you're doing is creating three different kinds of struct.

    There is nothing wrong with this approach, but this means any functions you create cannot be generalized - meaning, you will need three versions of each function you write to handle all possible cases, and you will lose any modularity (which my example attempted to preserve). It is possible to create more generalized functions, but this would require some additional overhead that you seem to be trying to avoid, and will require some pointer trickery to implement.

    Perhaps if you explain exactly what you're trying to accomplish, and why, a better solution can be devised (or an explanation of why one of the current solutions would suffice).

  6. #6
    Registered User
    Join Date
    Oct 2006
    Posts
    3,445
    Quote Originally Posted by heatblazer View Post
    if I have 2 000 000 instances they will still be float size.
    Is your environment memory-constrained? If it's running on a standard PC, then the 8MB consumed by two million floats is largely irrelevant, when you consider that most PCs have multiple gigabytes of memory.

    More often than not, clarity is to be preferred over a possible (but not guaranteed) slight performance improvement.
    What can this strange device be?
    When I touch it, it gives forth a sound
    It's got wires that vibrate and give music
    What can this thing be that I found?

  7. #7
    Registered User
    Join Date
    Sep 2014
    Posts
    121
    I`ve finalized the code, it`s not pretty and I think it can be improved for readability, but further will be a reflection emulation, I guess I don`t need overusing the macro any further:
    Code:
    #include <stdio.h>
    
    typedef struct  { int a; char b; float d; } S ;
    
    #define Node_(Name, Type) \
        typedef struct { \
        Type type;              \
    } Name##_##Type;
     
    
    
    int main(void) {
        Node_(Node, int);
        Node_(Node, S);
        Node_int n;
        n.type = 10000;
        printf("%d \n", n.type);
        
        Node_S s;
        s.type.a=10;
        s.type.b='c';
        s.type.d = 22.2f;
        
        printf("%d %c %f \n", s.type.a, s.type.b, s.type.d);
        
        return 0;
    }
    And I think this is what I need, thanks for the replies everyone. Yes the environment is memory constrained.


    And even better with variardic macro
    Code:
    #include <stdio.h>
    
    typedef struct  { int a; char b; float d; } S ;
    
    #define Struct(...) typedef struct { __VA_ARGS__ } S;
     
    
    
    int main(void) {
        Struct(int a; int b; int c;);
        S str;
        str.a = 100;
        str.b = 222;
        str.c = 3333;
        printf("%d %d %d sum is %d \n", str.a, str.b, str.c,  str.a+str.b+str.c);
        return 0;
    }
    Last edited by heatblazer; 07-15-2015 at 09:38 AM.

  8. #8
    Registered User
    Join Date
    Jun 2011
    Posts
    4,513
    Quote Originally Posted by heatblazer View Post
    I`ve finalized the code, it`s not pretty and I think it can be improved for readability, but further will be a reflection emulation, I guess I don`t need overusing the macro any further:
    You are not gaining anything using the preprocessor in such a manner. Here's what the first code pretty much amounts to after preprocessing:

    Code:
    #include <stdio.h>
    
    typedef struct { int a; char b; float d; } S ;
    
    int main(void) {
        typedef struct { int type; } Node_int;;
        typedef struct { S type; } Node_S;;
        Node_int n;
        n.type = 10000;
        printf("%d \n", n.type);
    
        Node_S s;
        s.type.a=10;
        s.type.b='c';
        s.type.d = 22.2f;
    
        printf("%d %c %f \n", s.type.a, s.type.b, s.type.d);
    
        return 0;
    }
    You have a struct that contains an int, and another struct that contains a struct that contains an int, char, and float.

    Your second code after preprocessing:

    Code:
    #include <stdio.h>
    
    typedef struct { int a; char b; float d; } S ;
    
    int main(void) {
        typedef struct { int a; int b; int c; } S;;
        S str;
        str.a = 100;
        str.b = 222;
        str.c = 3333;
        printf("%d %d %d sum is %d \n", str.a, str.b, str.c, str.a+str.b+str.c);
        return 0;
    }
    You are creating a local struct of type "S" (containing three ints) that shadows a global struct of type "S" (with different members).

    Neither of these is doing what you apparently want (based on your first post). You certainly aren't saving any space. Hint: You can't use the preprocessor to effect the program during execution (which was your originally stated goal).

    At this point, just using discrete variables of the necessary types would be a major improvement.

    Code:
    #include <stdio.h>
    
    int main(void)
    {
        int a;
        char b;
        float d;
    
        a = 10;
        b = 'c';
        d = 22.2f;
    
        printf("%d %c %f \n", a, b, d);
    
        return 0;
    }
    You haven't given us the "what" and "why" I previously asked for, and your examples only serve to confuse, so there's not much more advice I can give at this point.

  9. #9
    Registered User
    Join Date
    Sep 2014
    Posts
    121
    I am thinking of a custom mechanism to create different structs in runtime, avoiding the compiler... But it appears to be not a trivial task.

  10. #10
    Registered User
    Join Date
    May 2012
    Location
    Arizona, USA
    Posts
    956
    Try C++. It supports polymorphism natively.

    Failing that, you could try something like this:

    foo.h:
    Code:
    #ifndef FOO_H
    #define FOO_H
    
    struct foo;
    struct foo *make_foo(int type);
    
    #endif
    foo.c:
    Code:
    #include <stdlib.h>
    
    #include "foo.h"
    
    struct foo {
        enum { FOO_FLOAT, FOO_INT, FOO_CHAR } type;
    };
    
    struct foo_float {
        struct foo foo;
        float f;
    };
    
    struct foo_int {
        struct foo foo;
        int i;
    };
    
    struct foo_char {
        struct foo foo;
        char c;
    };
    
    static struct foo *make_foo_float()
    {
        struct foo_float *foo = malloc(sizeof *foo);
        foo->foo.type = FOO_FLOAT;
        foo->f = 0.0f;
    }
    
    static struct foo *make_foo_int()
    {
        struct foo_int *foo = malloc(sizeof *foo);
        foo->foo.type = FOO_INT;
        foo->i = 0;
    }
    
    static struct foo *make_foo_char()
    {
        struct foo_char *foo = malloc(sizeof *foo);
        foo->foo.type = FOO_CHAR;
        foo->c = '\0';
    }
    
    // this is our "factory" function
    struct foo *make_foo(int type)
    {
        switch (type) {
            case 0: return make_foo_char();
            case 1: return make_foo_int();
            case 2: return make_foo_float();
        }
        return NULL;
    }
    (This code should give you only a rough idea of one way to implement opaque pointers.)

    The make_foo() function gives you an opaque "foo" pointer. Only the "foo" module knows how to handle "foo" objects, which is one of the purposes of opaque pointers.

    Of course, this probably doesn't help as it requires the overhead of a "type" enumeration value, though your last example code is worse because it stores one member of each data type in the structure.

    This smells like an XY problem.

  11. #11
    Registered User
    Join Date
    Sep 2014
    Posts
    121
    Yes thats it. Thanks
    Code:
    struct foo_float {
       struct foo foo;
       floaf f;
    }
    I`ve forgot the inner polymorphic implementation... Thanks No XY problem at all... Just lazy explanation

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Replies: 1
    Last Post: 05-12-2011, 01:02 AM
  2. need help with opaque programming with struct and addresses.
    By DarkAngel4ever in forum C Programming
    Replies: 11
    Last Post: 03-30-2011, 08:19 PM
  3. 'good' smart pointer for factory?
    By KIBO in forum C++ Programming
    Replies: 13
    Last Post: 11-09-2010, 12:27 AM
  4. opaque pointer
    By George2 in forum C++ Programming
    Replies: 7
    Last Post: 03-13-2008, 09:07 PM
  5. Making an opaque texture
    By Magos in forum Game Programming
    Replies: 3
    Last Post: 02-27-2006, 12:56 PM

Tags for this Thread