views:

93

answers:

5

In theory it seems that books sugest declaring ADT in C as:

struct some_structure;
typedef some_structure *some_structure_t;

while most code uses:

struct some_structure;
typedef some_structure some_structure_t;

I know that const is not working with the first style. Is this the only reason for real libraries not to use first approach? Any further notices or suggestions?

+4  A: 

you mean abstract data type? If I abstract ;-) from the syntax errors in your code snipsets, I think, yes typedef of pointer types is frowned upon because problems with const and volatile.

BTW, another problem with your code are the names you have chosen. _t as an ending is reserved for future use in POSIX.

Jens Gustedt
And only because of this? Is `volatile` really that useful for ADT?I was not aware of `_t` being reserverd. However many libs do use this convention. Are you aware of any safer conventions (but please no `_T`).
dpc.ucore.info
@dpc.ucore.info an alternative would be to use uppercase for the types, i.e. Some_Structure and lowercase for the functions some_structure_func().
quinmars
@dpc.ucore.info: It is good practice for a library that is to be used by many to choose a prefix and stick to that for all symbols that it uses in header files and / or exports in objects. This is e.g done for POSIX threads with `pthread_` for symbols and `PTHREAD_` for macros. (well they again also have the `_t` since it is POSIX.)
Jens Gustedt
+1  A: 

I use

typedef struct some_s *some_t;

and where it matters I either use const struct some_s* or typedef const struct some_s *const_some_t, but I guess that's a question of personal taste.

hroptatyr
Declaration of another type for const is a nice idea, thanks.
dpc.ucore.info
Do you handle casting in some fancy way? The functions that promise not to change the object should accept `some_t` in place of `const_some_t`.
dpc.ucore.info
@dpc.ucore.info: you don't have to. just pass a some_t to a function whose sig is const_some_t, won't issue a warning (at least it doesn't on icc, gcc and pcc).
hroptatyr
A: 

I used to go with the first style, but I found that I tended to get confused about the levels of indirection I needed to access structure members so I started adding suffixes to the pointer type e.g.

typedef struct AStruct *AStructRef;

But then I realised I was effectively reinventing the * operator in order to stop myself from being confused by my attempt to hide the * operator. So now I go with the second style.

JeremyP
+2  A: 

An abstract data type is useful as a way of effectively performing actions on something like an "object".

If you can, in C, avoid the "typedef" keyword with structures altogether. It masks what is really happening. As a beginner I recommend you explicitly type struct AStruct wherever you would be tempted to use a typedef.

From there you perform an "action" on your "abstract data type" by declaring a function that takes a pointer to the "abstract data type" (or object, as I like to think) as the first parameter, and then the normal parameters afterwards.

e.g.

int some_func( struct AStruct *pObject, int param1, int param2 ) {
  if ( ( param1 < 0 ) || ( param2 < 0 ) )
    return( 0 );
  pObject->val = param1 + param2;
  return( 1 );
}

Then to use:

#include <stdio.h>
int main( void ) {
  struct AStruct myObject;

  if ( some_func( &myObject, 10, 12 ) ) {
    printf( "Processed: %d\n", myObject.val );
  } else {
    printf( "Failed\n" );
  }
  return( 0 );
}
PP
+1 I second the recommendation to avoid `typedef`-ing your structs merely for convenience. It is only trivially more work to write `struct name` over `name_t`. Retaining the clear indication that the data type is a `struct` can help prevent problems with `const`, as the OP notes. Being forced to write `struct name*` instead of `name_ptr_t` also avoids some of the indirection confusion that JeremyP mentions in his answer (the pointer is clearly and obviously declared as such).
bta
+2  A: 

It depends on what other people's code will do with those types. As a user of your library, if I assume that some_structure_t is a struct instead of a pointer to a struct, I might do something like this:

some_structure_t *p;

p = malloc( sizeof *p);
memcpy( p, &another_p, sizeof *p);

Maybe it's my background as an embedded programmer, but I want to know more about some_structure_t in my code that uses it. Even if it's a truly abstracted type, and it's only used as a parameter or return value in end-user code, you still have to deal with it in the library that supports the type. If I saw the following code, I'd do a double take:

some_structure_t some_function( some_structure_t foo)
{
    some_structure_t retval;

    retval = malloc( sizeof *retval);  // assigning pointer to struct?
    retval->member = foo->member;      // using -> instead of .?

    return retval;                     // returning potentially large structure on stack?
}

And maybe this is the biggest one -- copying from one some_structure_t to another. If it's an int or a float or a struct, writing foo = bar will make a copy. If some_structure_t is defined as a pointer to a struct, now foo and bar point to the same object. Maybe that's what you want, but it seems risky to me.

I wonder if MISRA has anything to say about typedefs that are pointers.

tomlogic
Exactly - it's considered bad style in C to hide the fact that a type is a pointer, since the user of the type needs to know that anyway. Hence the standard library provides `FILE *`.
caf