views:

251

answers:

3

In File api.h i've

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

struct trytag;
typedef struct trytag try;

void trial (try *);

#endif

In file core.h, i've

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

In func.c, i've

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

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

In main.c, i've

#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. Main.c should not know the members of try structure. What am i missing?

+5  A: 

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);
}
Carl Norum
Yuck, do not do "typedef void try;". When you do this, you lose type safety (the compiler will not be able to catch such nonsense as `int x; trial(` The question technique of introducing a new struct is much better.
R Samuel Klatchko
The part about allocating an instance is right on, but I wonder how main.c is supposed to dereference the a and b when all it has is api.h, not core.h. I don't think that's ever going to work without func.c exposing some access helper methods.
Steven Sudit
Thank you.. This solved!
bluegenetic
+1 to Klatchko for noticing the type safety issue.
Steven Sudit
Oh good call. Missed that part of the show. Whoops.
Carl Norum
For real safety, you probably want to allocate and populate some identification fields in the `alloc_try()` function. That way you can check if a structure you pass in works.
Carl Norum
@Steven Sudit - Yes. We can't dereference s_tryvar in printf. It should be commented.
bluegenetic
Thank you everyone..!
bluegenetic
@Carl Norum: Yes, that's a great idea. And if the overhead offends, it can be compiled out when not in DEBUG mode. If this were C++, I'd suggest using dynamic_cast instead.
Steven Sudit
+1  A: 

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.

anon
+1  A: 

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.

R Samuel Klatchko
Or hide the fact that it's on the heap by returning a non-opaque struct that contains the opaque pointer.
Steven Sudit