Reputation: 3613
I know this question might have been asked before but I wanted to take my approach at it and get an opinion or possibly a better way to do it.
I have three files a.h
a.c
and main.c
Prototypes of functions regarding the struct will be in a.h
while implementation will be in a.c
and called from main.c
the structure will be simple it can just look like this
struct ctx{
int x;
};
I want a.c
to be able to manipulate the contents of the struct but prevent main from having any idea of what's inside. So I figured placing the struct definition inside a.c
instead of a.h
and placing just struct ctx;
as a prototype in a.h
And this could work, however ctx can no longer be allocated on the stack in main.c
because the compiler doesn't know the size to allocate.
So this leads me to my first question: Is there a way to allocate the structure local to the stack without knowing the definition of the structure.
So I assumed if it wasn't possible on the stack then maybe I could just pass it on the heap instead by creating a simple init function that returns a pointer. And that does work but would it be over complicating the process?
a.h
#ifndef a_h
#define a_h
#include <stdio.h>
#include <stdlib.h>
struct ctx;
int incctx(struct ctx* c);
struct ctx* initctx(void);
void destroyctx(struct ctx* c);
#endif /* a_h */
a.c
#include "a.h"
struct ctx{
int x;
};
int incctx(struct ctx* c){
return ++c->x;
}
struct ctx* initctx(){
struct ctx* c = malloc(sizeof(struct ctx));
c->x = 0;
return c;
}
void destroyctx(struct ctx* c){
free(c);
}
main.c
#include "a.h"
int main(){
struct ctx* c = initctx();
printf("%d\n",incctx(c));
printf("%d\n",incctx(c));
printf("%d\n",incctx(c));
destroyctx(c);
return 0;
}
This design kind of solves the problem with a few drawbacks.
1: What if I wanted to make parts of the structure visible but not the entire thing?
2: If I wanted the structure definition to be available to other files say b.h
and b.c
would I have to redefined the structure? Do any of you have a cleaner design? I know some people say you can just place a void*
in the structure instead of specific types and just label them arbitrary names but I don't see that as a viable solution.
Upvotes: 2
Views: 285
Reputation: 25266
Abstraction hiding can be achieved by letting your module hand-out pointers to the struct to main and let your module do all the operations on it. Then it is sufficient that main only knows that ctx
is some void data type (pointer), e.g.
// main.c
void *initctx(void);
int incctx(void *c);
int main(void)
{
void *ctx= initctx();
int i= incctx(ctx);
//....
}
Upvotes: 4
Reputation: 304
You want to hide the whole struct inside a.c
When allocating a struct, don't return a pointer to it, rather return a handle, which might be an index into the array of structs you maintain in a.c
If you want to expose any part of the struct to the outside, provide functions like getSomething(int handle) or setSomething(int handle).
open(), returning an int uses this approach as opposed to fopen() which returns a FILE *
Upvotes: 0
Reputation: 409136
For the visibility problem you can use two structure in an inheritance-like way.
First you have the public structure that you define in the header file and which your API handles pointers to:
struct ctx
{
// Public data
};
Then in the source file you create a private structure, where the public structure is the first member:
struct private_ctx
{
struct ctx ctx; // The public part of the structure
// Followed by private data
};
Internally inside the API you use the private_ctx
structure, while the code using your API will only use the public ctx
structure.
Nested structures like this works similarly to inheritance, the private_ctx
structure is a ctx
structure. You can create a private_ctx
structure and return a pointer to it suitably casted to a ctx
structure.
Here's an example on how to create the structure:
struct ctx *create_struct(void)
{
// Allocate the private structure, which contains the public structure
struct private_ctx *data = = malloc(sizeof *data);
// Return the public part of the structure
return (struct ctx *) data;
}
Using the private data is equally easy with a reverse cast:
void use_data(struct ctx *data)
{
struct private_ctx *private_data = (struct private_ctx *) data;
// Here the private data can be used...
}
Upvotes: 5