Now, the interface that I had in mind was the GNU MP C API.
Have you ever read the rationale for those decisions from long past?
They wanted a means of creating a stack value having no secondary allocation cost with reference semantics in C for the sake of simplicity. (I don't know how it works these days.) They didn't want clients to pay the cost of understanding where and when the different semantics were at play in function parameters and stack variables. They bought simplicity with, what I believe to be, a very poor interface decision.
The variable `s' is intended to be a definition of a stack variable; it has no secondary allocation cost in that `s' is not a pointer so the components of the underlying structure are immediately available as would be true for any other stack variable of a structure type allowing us to implement the interface functions to request a single chunk of memory of the array on which the type of `s' depends.
The variable `f' is intended to provide reference semantics; as the type of `f' has both partial value and partial reference semantics one need not chase a pointer or request the address of the type of the `f' variable somewhat reducing the burden on client code.
Yes, I'm familiar with the technique. (Everyone following along with the discussion should have some familiarity with it.)
Has that actually bought anything of real value?
Is the extra allocation that costly? If it is costly, as we may freely assume for the purposes of discussion, can't we just use the classic "struct hack" under the hood to still allocate the necessary components of the structure and the underlying array which is necessary in a single pass? (For those interested, the "struct hack" is not standard, but it is actually extremely portable and very reliable.) If we can't, or don't want, to rely on the "struct hack", can't we still just assume that people will follow the required protocol of an original initialization routine? If that isn't appropriate due to the cost of a dumb initialization, can't we bind the size of the underlying array to initialization for a hidden routine which all implementations of the interface may rely upon for case specific initialization?
If all of that is inappropriate, is the cost of using `&' and `*' really so great that sacrificing normal, canonical value and reference semantics reasonable?
You have to follow the API in any event. I have no interest in discussing the case of people not following the API; those sorts of people get the undefined behavior they deserve.
Consequently, I think that a typedef for an opaque pointer is fine.
My problem here is not an attempt to magic away API requirements or give a pass to lacking documentation.
Test & GetSingleton();
Well, yeah, I guess I'd know if I read the documentation and used the completely idiotic API properly I'd understand the code. (For those not following, feel free to search the forum for cases of this idiotic notion of binding a reference to null.) Is it reasonable? No. It violates reference (C++) guarantees.
Test * s(&GetSingleton());
// Use the singleton variable.
Now, I am not intending to argue that a pointer `typedef' is this bad. I only chose this because I know you'll agree that this is horrible. I would find a `typedef' to an opaque pointer similarly distasteful, just not to so great an extent, because they far too often similarly violate normal value and/or reference semantics.