Reputation: 1465
When writing a library, you sometimes want to hide implementation details from the user. The ideal tool for this is opaque structs or opaque pointers.
A problem arises when another source file in the library wishes to use data from this struct. For example: this is the header file apple.h
:
typedef struct apple_ Apple;
Apple* new_apple();
void delete_apple(Apple* a);
/* ... */
This is the definition of the struct Apple
:
typedef struct apple_ {
int taste; /* the higher the better */
}
Typically, this part resides in apple.c
.
But what if a Store
wishes to rank their apples according to taste? This could be store.c
:
#include "apple.h"
void rankApples(Store* store) {
int t = store->apples[0]->taste; /* unreachable field */
}
A solution could be to duplicate the struct definition (I don't want that) or to create a function in apple.[hc]
: int taste_apple(Apple* a) { return a->taste; }
, but I'd rather that users don't know how tasty apples are.
How can this be solved? Should I create another header file used by both apple.c
and store.c
? How do I stop users from including that header file?
Upvotes: 2
Views: 298
Reputation: 66194
When hiding library detail from consuming clients, it is not uncommon to do what you described in your last paragraph. But you don't need to "ship the details" with your lib and public header.
For example:
mylib.h
#ifndef MYLIB_H
typedef struct Data Data;
void Func(Data *data);
#endif
mylibint.h
#ifndef MYLIB_H_INTERNAL
#define MYLIB_H_INTERNAL
struct Data
{
// implementation goes here
int value;
};
#endif
mylib.c
#include "mylib.h"
#include "mylibint.h"
Data *GetData()
{
return calloc(1, sizeof(Data));
}
void FreeData(Data *data)
{
free(data);
}
void DoSomething(Data * data)
{
// do something with data
}
In doing so, your library will build, consuming both headers while doing so. The only header you need to ship with it is mylib.h
. Later, a client can do this:
client.c
#include "mylib.h"
int main()
{
Data *data = GetData();
DoSomething(data);
FreeData(data);
}
And they are left blissfully unaware of what Data
is besides some opaque type.
Upvotes: 3
Reputation: 6403
You want the store to know a piece of information, but you are not willing to give it to them. That just doesn't make sense.
You can either make apple
structure public to both files (that might mean separating it and including it by both, but not giving it to the user), or as you said, create a function that would retrieve the taste of an apple.
However, I'd say that if the taste is going to be used for the ranking purposes only, why don't you just create a function, let's say getPoints
a store
could use to rank the apples?
int getPoints(Apple* a) {
return a->taste*2;
}
Upvotes: 0