This is the mail archive of the
gcc-help@gcc.gnu.org
mailing list for the GCC project.
RE: Is strongly-typed programming conceivable for C code using gcc ?
- From: "Alexandre Beaugy" <beaugy dot ext at tlse dot sofreavia dot fr>
- To: <gcc-help at gcc dot gnu dot org>
- Date: Thu, 20 Sep 2007 10:55:01 +0200
- Subject: RE: Is strongly-typed programming conceivable for C code using gcc ?
> Taking Dima's solution in conjunction with the way Apple solved their
> similar problem by using opaque types in Cocoa and Carbon:
Therefore, if I understand you right, the following C source code example has
to be put in a '.h' header file?!
struct CW_C_MsgUp_s;
typedef struct CW_C_MsgUp_t* CW_C_MsgUp_t;
struct CW_C_MsgDown_s;
typedef struct CW_C_MsgDown_t* CW_C_MsgDown_t;
And the following Dima's struct declaration (completing your example) has to be
written in the '.c' source file?!
typedef struct CW_C_MsgUp_s {
void* ptr;
} CW_C_MsgUp_t;
typedef struct CW_C_MsgDown_s {
void* ptr;
} CW_C_MsgDown_t;
Is that true?
> This will insulate your implementation of those opaque types from the public
> header files (note that the structs are forward references, and not declared
> publically), and allow you some measure of type safety in C.
>
> Apple uses a factory function to create their opaque types, and for most
> opaque types the CFRelease function to destruct them (using a reference
> counting mechanism). That's the role that CW_C_MsgUp_new() and
> CW_C_MsgDown_new() provides.
Why did you talk about a "reference counting mechanism"? Because in my
implementation my C "constructor" and "destructor" functions do not have any
counting mechanism, is that a problem. Actually, I rely on the users/callers of
the functions to call the "destructor" as many times as the "constructor",
avoiding memory losses.
But, if you talked about a "reference counting mechanism", is it to prevent
memory leaks? By implementing a kind of basic "garbage collector", destroying
undeleted allocated structures? Is that what you meant?
> As far as them being pointers... I believe that you will want them to be
> pointers baked into the typedef.
>
> The one change to your code would be to change from this...
>
> CW_C_MsgUp_t *my_up_msg;
> CW_C_MsgDown_t *my_down_msg;
>
> ...to this...
>
> CW_C_MsgUp_t my_up_msg;
> CW_C_MsgDown_t my_down_msg;
Yes and no. Because if it will or will not be confused with such a typedef.
Because my "*_new" and "*_delete" functions will allocate and destroy objects
(structures, classes, int, char buf, etc, who knows, that doesn't matter) and
the allocation function on failure can return a null pointer. Therefore how
will the caller test the returned value (not considered as pointer because of
typedef) with null?
I think a workaround could be to pass the "my_up_msg" or "my_down_msg"
variables as parameter of "*_new" functions and test an integer returned value
of "*_new", telling how everything goes?
Example ;o)
<example1>
typedef struct CW_C_MsgUp_t* CW_C_MsgUp_t;
[...]
CW_C_MsgUp_t my_up_msg;
my_up_msg = CW_C_MsgUp_new();
if (my_up_msg == NULL) /* I think gcc will complain, here! */
{
return -1;
}
[...]
</example1>
<example2>
typedef struct CW_C_MsgUp_t* CW_C_MsgUp_t;
[...]
int rval = 0;
rval = CW_C_MsgUp_new(&my_up_msg);
if (rval < 0)
{
return -1;
}
[...]
</example2>
Which of example1 and example2 is better to use?
On the same kind of reflexion, how do I have to initialize my_up_msg?
1. my_up_msg = 0;
2. my_up_msg = null;
4. memset(&my_up_msg, 0, sizeof(my_up_msg))?
3. memset(my_up_msg, 0, sizeof(my_up_msg))?
The solution I feel is the good one are 2 and 4, but they mean that I do not
avoid the fact that "my_up_msg" is a typedef-ed pointer. Solutions 1 and 3 are
to be used if the user completely avoids the fact that CW_C_MsgUp_t is a
pointer type.
How could I do to not confuse the users and me? I'm aware that this solution is
very interesting because it does not allow people to dereference the variable.
But is that a recommended coding rule?
Thanks a lot for all your help.
Regards.
--
Alex.