15

Possible Duplicate:
Typedef pointers a good idea?

I've seen this oddity in many APIs I have used:

typedef type_t *TYPE;

My point is that declaring a variable of type TYPE will not make it clear that in fact a pointer is declared.

Do you, like me, think that this brings a lot of confusion? Is this meant to enforce encapsulation, or there are other reasons as well? Do you consider this to be a bad practice?

Community
  • 1
  • 1
Blagovest Buyukliev
  • 42,498
  • 14
  • 94
  • 130
  • 1
    Only one of the answers to "Typedef pointers a good idea?" alludes to the useless interaction with `const` -- and that answer is lowly ranked. IMHO the `const` problem is the most significant problem with these sorts of typedefs,... hence not really a duplicate. – John Marshall Sep 23 '10 at 20:48
  • @John: I disagree with you conclusion. You've noted that a answer added long after the question was asked hasn't received a lot of attention, but the questions are still fundamentally the same. – dmckee --- ex-moderator kitten Sep 23 '10 at 20:56
  • @dmckee: Fair enough, I guess my real point is "shouldn't be immediately closed as a duplicate, as its answers supply interesting new information". And really my point is Blagovest should have waited a little longer so as to accept _my_ answer :-) – John Marshall Sep 23 '10 at 21:05
  • @John Marshall: I accepted your answer now because I had to sleep :-) – Blagovest Buyukliev Sep 24 '10 at 07:27
  • You see a lot of this on Windows API. I absolutely detest it. It hides the type and that leads to more confusion. I prefer `int* i` over PINT = I suppose Windows' BASIC heritage lead them to try to hide pointers. Secondly, the All-caps looks ghastly. I actually never bothered with the win32 api because It's just too messy. – W.K.S Jun 26 '12 at 05:41

8 Answers8

23

In general, it's a bad practice. The significant problem is that it does not play well with const:

typedef type_t *TYPE;
extern void set_type(TYPE t);

void foo(const TYPE mytype) {
  set_type(mytype);  // Error expected, but in fact compiles
}

In order for the author of foo() to express what they really mean, the library that provides TYPE must also provide CONST_TYPE:

typedef const type_t *CONST_TYPE;

so that foo() can have the signature void foo(CONST_TYPE mytype), and at this point we have descended into farce.

Hence a rule of thumb:

Make typedefs of structs (particularly incomplete structs), not pointers to those structs.

If the definition of the underlying struct is not to be publicly available (which is often laudable), then that encapsulation should be supplied by the struct being incomplete, rather than by inconvenient typedefs:

struct type_t;
typedef struct type_t type_t;

void set_type(type_t *);
int get_type_field(const type_t *);
John Marshall
  • 6,815
  • 1
  • 28
  • 38
  • Interesting that some types don't seem to need a const version. For example, I've never felt any desire for a `const_pthread_t` on which I can call `pthread_getschedparam` but not `setschedparam`. `const pthread_t` fails to achieve that (of course), just as the typedefed pointer fails to achieve it in your example. And `ftell` takes `FILE*`, not `const FILE*`, although I suppose that it ought to be const, to support const-safe use of FILE*. So I think this `const` problem is significant/decisive in some cases, but not always. C's pre-const legacy showing through, maybe? Or just laziness :-) – Steve Jessop Sep 23 '10 at 22:25
  • Indeed. I looked to see if anything in the standard had been retrofitted with `const FILE*`, but... not so much. (And there was me hoping to help you understand `FILE*`! :-)) Perhaps it's to do with whether something feels like an atomic type -- one doesn't much write `const int` either. Or something of a chicken-and-egg problem: if none of the inquiry functions take a `const FILE*`, why would you write your own `foo()` function to take one -- how would you use your parameter? – John Marshall Sep 23 '10 at 22:37
  • 1
    `ftell` cannot take `const FILE *` because (on POSIX or any other implementation with threads) it must obtain a mutex lock on the `FILE` object unless the current position can be obtained with a single atomic operation. The whole idea of a `const FILE *` is pretty much nonsense; it could not be used for anything. – R.. GitHub STOP HELPING ICE Sep 24 '10 at 02:26
  • 1
    +1 for "descended into farce" – olovb May 04 '12 at 19:08
  • This example of an incomplete struct (rather than a pointer to a struct) for encapsulation is important - many examples I've seen use a typedef on a pointer and this leads to problems that can be difficult to debug for programmers who are not used to the idiom. However, it would be useful to know what the downsides are to using an incomplete struct rather than a typedef'd pointer. – davidA Aug 17 '15 at 07:17
6

A common idiom is to suffix the type with _p to indicate that it's a pointer while still retaining the pointery qualities.

Sometimes it is necessary to use only the pointer type if the struct that it is pointing to is not publicly available. This helps facilitate data hiding. I.e.

typedef struct hidden_secret_object * object;
void change_object(object foo);

this allows you to change the way that hidden_secret_object is structured without breaking external code.

cobbal
  • 69,903
  • 20
  • 143
  • 156
  • 3
    Why do you need the pointer in the typedef for data hiding? `typedef struct h_s_o object; void change_object(object *foo);` would accomplish the same thing. The example looks irrelevant to the question. – schot Sep 23 '10 at 20:12
  • Sometimes a type is intended to be opaque, and on different systems a different type would be used. On some systems this type could be big, so passing it by pointer is more efficient, while on other systems it may simply be a token passed as an integer. – nategoose Sep 23 '10 at 20:26
5

I don't find it clear either. I'm not fond of full capitalised types either (I try to reserve those for #defines).

This way makes it easy to kid oneself by thinking it is in fact a value type, while we're talking about a pointer type. The pointer type can be completely abstracted away with smart pointers, but that isn't common practise in C.

Suffixing with (as mentioned previously) _p, _ptr, Pointer or anything along those lines creates clarity; increases typing, that's true, but will prevent you from silly mistakes (such as using '.' instead of '->', ...) costing you valuable developing time.

Taco de Wolff
  • 1,682
  • 3
  • 17
  • 34
5

It depends on what you are trying to achieve. There is no meaningful "yes or no" answer to your question the way it is stated.

  • If you are trying to create an abstract handle kind of type, implying that the user is not supposed to know or care what is hiding behind the type, then typedef-ing a pointer type is perfectly fine. The whole point is that today it might be a pointer type, and tomorrow it might become an integer type, and later it might become something else. This is exactly what pointer type typedefs are normally used for in most library interfaces.

You are saying that sometimes it is "not clear that a pointer is declared". But under this usage model that's exactly the point! It is supposed to be "not clear". The fact that the type happens to be an obfuscated pointer is none of your business. It is something that you don't need to know and not supposed to rely upon.

A classic example of this usage model is the va_list type in the standard library. In some implementation it might easily be a typedef for a pointer type. But that's something you are not supposed to know or rely upon.

Another example would be the definition of HWND type in Windows API. It is a typedef for pointer type as well, but that's none of your business.

  • A completely different situation is when you are typedef-ing a pointer type as a form of shorthand, just to make the declarations shorter for not having to type the * character every time. In this case the fact that the typedef is (and will always be) standing for a pointer type is exposed to the user. Normally this usage is not a good programming practice. If the users will want to create an alias to avoid typing * every time, they can do it by themselves.

This usage model usually leads to more obfuscated code for the reasons you already mentioned in your OP.

Example of this bad usage of typedefs can also be found in Windows API. Typedef names like PINT follow exactly that flawed usage model.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
1

I don't think it's bad practice if the it's a pointer to an incomplete type, or if for any other reason the user isn't expected to dereference it. I never understood FILE*.

I also don't think it's bad practice if you're doing it because you have several levels of indirection, and you want to use it in situations where some of them are irrelevant. typedef char **argarray, or something.

If the user is expected to dereference it then in C, I think it's probably best to retain the *. In C++, people are used to user-defined types with overloaded operator*, such as iterators. In C that's just not normal.

Steve Jessop
  • 273,490
  • 39
  • 460
  • 699
1

Storage-class qualifiers like 'const' will work differently with typedef'ed pointers than with 'natural' ones. While this isn't typically a good thing with 'const', it can be very useful with compiler-specific storage classes like "xdata". A declaration like:

xdata WOKKA *foo;

will declare "foo" to be a pointer, stored in the default storage class, to a WOKKA in xdata. A declaration:

xdata WOKKA_PTR bar;
would declare "bar" to be a pointer, stored in xdata, to a WOKKA in whatever storage class was specified in WOKKA_PTR. If library routines are going to expect pointers to things with a particular storage class, it may be useful to define those storage classes within the pointer types.
supercat
  • 77,689
  • 9
  • 166
  • 211
1

It's bitten me in the ass on occasion:

for (vector<typedef_name_that_doesnt_indicate_pointerness_at_all>::iterator it;
    it != v.end(); ++it)
{
   it->foo(); // should have been written (*it)->foo();
}

The only time it's acceptable is if the type is meant to be truly opaque and not accessed directly at all. IOW, if someone's going to have to dereference it outside of an API, then the pointerness should not be hidden behind a typedef.

John Bode
  • 119,563
  • 19
  • 122
  • 198
0

Maybe a way to make it more specific would be to call the new pointer type type_ptr or something like that:

 typedef type_t* type_ptr;
jbernadas
  • 2,540
  • 18
  • 12