bluegenetic
bluegenetic

Reputation: 1435

opaque (abstract) data types in C

File api.h

#include <stdio.h>
#ifndef API
#define API

struct trytag;
typedef struct trytag try;

void trial (try *);

#endif

File core.h

#ifndef CORE
#define CORE
struct trytag
{
    int a;
    int b;
};
#endif

File func.c

#include "api.h"
#include "core.h"

void trial (try *tryvar)
{
    tryvar->a = 1;
    tryvar->b = 2;
}

File main.c

#include "api.h"

int main ()
{
    try s_tryvar;

    trial(&s_tryvar);

    printf("a = %d\nb = %d\n", s_tryvar.a, s_tryvar.b);
}

When I compile, I get:

main.c:5: error: storage size of ‘s_tryvar’ isn’t known

If I include core.h in main.c this error doesn't come as try is defined in core.h. But I want the structure try to be hidden to main.c — it should not know the members of try structure. What am I missing?

Upvotes: 0

Views: 3984

Answers (4)

mwag
mwag

Reputation: 4055

There is a way to do something that technically is not exactly what you are asking for, but should serve the same purpose of keeping your structure opaque while supporting non-heap allocation.

in api.h, you state an opaque structure as follows:

struct trytag_opaque
{
    char data[sizeof(int)*2];
};

if you wanted to be more opaque than that, you could calculate the maximum size of the structure required across any supported platform, and use:

struct trytag_opaque
{
    char data[MAX_TRYTAG_SIZE];
};

Then your api.h function declarations would look like:

int try_a(struct trytag_opaque *t)

and your function code would look like:

int try_a(struct trytag_opaque *t_opaque) {
    trytag *t = (trytag *)t_opaque;
    ...
}

and your main.c would look like:

#include "api.h"
int main() {
    struct trytag_opaque t;
    ...
    try_a(&t);
    ...
}

Upvotes: 0

Carl Norum
Carl Norum

Reputation: 225172

I don't think what you're trying to do is possible. The compiler needs to know how big a try structure is to compile main.c. If you really want it to be opaque, make a generic pointer type, and instead of declaring the variable directly in main(), make alloc_try() and free_try() functions to handle the creation and deletion.

Something like this:

api.h:

#ifndef API
#define API

struct trytag;
typedef struct trytag try;

try *alloc_try(void);
void free_try(try *);
int try_a(try *);
int try_b(try *);
void trial (try *);

#endif

core.h:

#ifndef CORE
#define CORE
struct trytag
{
    int a;
    int b;
};
#endif

func.c:

#include "api.h"
#include "core.h"
#include <stdlib.h>

try *alloc_try(void)
{
    return malloc(sizeof(struct trytag));
}

void free_try(try *t)
{
    free(t);
}

int try_a(try *t)
{
    return t->a;
}

int try_b(try *t)
{
    return t->b;
}

void trial(try *t)
{
    t->a = 1;
    t->b = 2;
}

main.c:

#include <stdio.h>
#include "api.h"

int main()
{
    try *s_tryvar = alloc_try();

    trial(s_tryvar);
    printf("a = %d\nb = %d\n", try_a(s_tryvar), try_b(s_tryvar));

    free_try(s_tryvar);
}

Upvotes: 7

R Samuel Klatchko
R Samuel Klatchko

Reputation: 76611

The problem is in main.c, the compiler hasn't seen the definition of struct try. Because of that, the compiler is limited to using pointers to struct try.

What you want to do is add two new functions to your API:

try *create_try();
void *destroy_try(try *t);

These functions will call malloc and free respectively.

If you don't want to limit your structure to only being allowed on the heap, you are going to have to give up on making it opaque.

Upvotes: 1

anon
anon

Reputation:

Think how the opaque FILE structure works in C. You only work with pointers, and you need a function like fopen() to create an instance, and a function like fclose() to dispose of it.

Upvotes: 1

Related Questions