>If you will need to release it, then you also need to know it is a pointer that would afterward refer to freed storage.
You really don't. If the documentation says that after calling release_fluor(), the fluor_t that was passed to it can't be passed to get_fluor_mass(), it doesn't matter whether fluor_t is a pointer or not.
If you are relying on everybody who reads code that uses your type to have thoroughly scoured all the documentation, you are Doing It Wrong.
Programmers understand pointers and pointer semantics: when they see a pointer, they know what is required of them. An opaque type that has pointer semantics you can't tell without reading up on documentation is, exactly, an abstraction that costs more than it delivers.
> If you are relying on everybody who reads code that uses your type to have thoroughly scoured all the documentation, you are Doing It Wrong.
As fluoridation indicated, this is the norm in C programming. By means of example, in the OpenCL C library, bitfields all use the same type. You have to consult a function's documentation to know what you're allowed to pass. [0]
I do agree that the 'Ada-style' approach to types is far superior to the 'C-style', but Doing It Wrong seems a bit strong, unless you want to condemn all C code.
With the right library you can use the 'Ada-style' in C++. Specifically, using a 'strong typedef' library which prevents implicit conversions, such as [1], but not Boost's strong_typedef which permits implicit conversion back to the underlying type. Unfortunately this kind of thing isn't possible in C.
> Programmers understand pointers and pointer semantics: when they see a pointer, they know what is required of them
Not really. Should you call free on the pointer once you're done with it? The type alone doesn't tell you. You need to consult the documentation to know how you are meant to use the value, whether or not a pointer type is used.
People know they need to find out whether and when they can or must free a pointer. If it looks enough like a file descriptor, they know they might need to call close() on it. For other types, there are no standard conventions.
I know why programs other people write keep crashing. They are coding them in C (or bad C++; but I repeat myself), and are relying on abstractions that conceal exactly that which they should instead expose.
You really don't. If the documentation says that after calling release_fluor(), the fluor_t that was passed to it can't be passed to get_fluor_mass(), it doesn't matter whether fluor_t is a pointer or not.
I'm sorry, but you're just plainly wrong.