N
Noob
Hello everyone,
I'm implementing a wrapper around platform-dependent "primitives",
and I'm trying to have 0 platform-dependent code (even includes)
in the "module" interface header.
For example, consider this possible interface for a semaphore
(whatever that is):
Sem_t *Sem_create(int val);
void Sem_delete(Sem_t *sem);
int Sem_signal(Sem_t *sem);
int Sem_wait(Sem_t *sem);
int Sem_wait_timeout(Sem_t *sem, unsigned wait_ms);
I could use an opaque (void *) pointer to "hide" the details of
a (Sem_t) but I do want the compiler to throw an error when an
imprudent user calls, e.g.
char *str = "booya";
Sem_signal(str);
One method I've seen a few times, is to "lie" to the user code
(and, to some level, to the compiler) by pretending "yeah, this
Sem_t is a struct, and I'll provide the definition sometime later"
(I think it's called a tentative declaration). But I've been told
that the compiler always gets its revenge when it's lied to.
Could you please confirm whether the following approach is safe,
and does what I want?
$ cat wrap_sem.h
#ifndef TRUE_SEM_T_DEFINITION
typedef struct dummy_Sem_t Sem_t;
#endif
Sem_t *Sem_create(int val);
void Sem_delete(Sem_t *sem);
int Sem_signal(Sem_t *sem);
int Sem_wait(Sem_t *sem);
int Sem_wait_timeout(Sem_t *sem, unsigned wait_ms);
Users just include "wrap_sem.h" and they're not supposed to "peek"
inside a Sem_t (in fact, they can't since Sem_t is an incomplete type).
$ cat test.c
#include "wrap_sem.h"
int main(void)
{
Sem_t *toto = Sem_create(42);
*toto;
Sem_wait("foo");
return 0;
}
$ gcc -Wall -Wextra -std=c89 test.c
test.c: In function 'main':
test.c:5:3: error: dereferencing pointer to incomplete type
test.c:6:3: warning: passing argument 1 of 'Sem_wait' from incompatible pointer type
wrap_sem.h:7:5: note: expected 'struct Sem_t *' but argument is of type 'char *'
(Side issue: there seems to be a small QoI issue with the compiler's note,
as there is no 'struct Sem_t' AFAIU, there is a 'struct dummy_Sem_t' and
a 'Sem_t'. Do you agree? End digression)
In the actual implementation file, I define TRUE_SEM_T_DEFINITION
and then define the "real" Sem_t type.
Is this an (the?) idiomatic way to hide implementation details?
Is there a chance that this could bite me?
Could this confuse other tools, such as a debugger?
Any unforeseen consequences?
Regards.
I'm implementing a wrapper around platform-dependent "primitives",
and I'm trying to have 0 platform-dependent code (even includes)
in the "module" interface header.
For example, consider this possible interface for a semaphore
(whatever that is):
Sem_t *Sem_create(int val);
void Sem_delete(Sem_t *sem);
int Sem_signal(Sem_t *sem);
int Sem_wait(Sem_t *sem);
int Sem_wait_timeout(Sem_t *sem, unsigned wait_ms);
I could use an opaque (void *) pointer to "hide" the details of
a (Sem_t) but I do want the compiler to throw an error when an
imprudent user calls, e.g.
char *str = "booya";
Sem_signal(str);
One method I've seen a few times, is to "lie" to the user code
(and, to some level, to the compiler) by pretending "yeah, this
Sem_t is a struct, and I'll provide the definition sometime later"
(I think it's called a tentative declaration). But I've been told
that the compiler always gets its revenge when it's lied to.
Could you please confirm whether the following approach is safe,
and does what I want?
$ cat wrap_sem.h
#ifndef TRUE_SEM_T_DEFINITION
typedef struct dummy_Sem_t Sem_t;
#endif
Sem_t *Sem_create(int val);
void Sem_delete(Sem_t *sem);
int Sem_signal(Sem_t *sem);
int Sem_wait(Sem_t *sem);
int Sem_wait_timeout(Sem_t *sem, unsigned wait_ms);
Users just include "wrap_sem.h" and they're not supposed to "peek"
inside a Sem_t (in fact, they can't since Sem_t is an incomplete type).
$ cat test.c
#include "wrap_sem.h"
int main(void)
{
Sem_t *toto = Sem_create(42);
*toto;
Sem_wait("foo");
return 0;
}
$ gcc -Wall -Wextra -std=c89 test.c
test.c: In function 'main':
test.c:5:3: error: dereferencing pointer to incomplete type
test.c:6:3: warning: passing argument 1 of 'Sem_wait' from incompatible pointer type
wrap_sem.h:7:5: note: expected 'struct Sem_t *' but argument is of type 'char *'
(Side issue: there seems to be a small QoI issue with the compiler's note,
as there is no 'struct Sem_t' AFAIU, there is a 'struct dummy_Sem_t' and
a 'Sem_t'. Do you agree? End digression)
In the actual implementation file, I define TRUE_SEM_T_DEFINITION
and then define the "real" Sem_t type.
Is this an (the?) idiomatic way to hide implementation details?
Is there a chance that this could bite me?
Could this confuse other tools, such as a debugger?
Any unforeseen consequences?
Regards.