Search code examples
cpointersopaque-pointers

Understanding Opaque Pointers


I apologize if this is an obvious question, but in C, are Opaque Pointers similar to Java Interfaces? As I'm trying to understand them, I'm interpreting them similarly to Java interfaces in the sense that they establish a mandatory way to interact with the method/function? So if I wanted to use an Opaque Pointer, I would need to define it in a .h file while I would call it in a .c file? Opaque pointers just define the signature of the method/function (in the .h file) while the the .c file takes those functions with those signatures and implements them.


Solution

  • I'm interpreting them similarly to Java interfaces in the sense that they establish a mandatory way to interact with the method/function

    Your interpretation is in the right path, but it is missing a key point: The reason we use opaque pointers is so that we can keep the details of the structure's implementation hidden, so that we can modify this structure in a future version of our software without breaking existing code that uses it.

    Essentially, the analogy with Java is as follows:

    • The declaration of the opaque pointer, and of each function that accepts (or returns) such an opaque pointer, corresponds to an interface in Java. (Note: the declarations only. Not their actual definitions.)

    • The structure pointed by the opaque pointer, and the set of function definitions, are more like a concrete Java class which implements the interface, containing nothing but private member fields, so that nobody can access them.

    Another concept very similar to opaque pointers is handles. The open() function returns an int which is a file handle, the read(), write(), and close() methods accept such a handle as a parameter. This number could be anything, you are not supposed to interpret it in any way or expect its value to be within any particular range. In theory, on a system where an int is large enough to hold a pointer to structure, a file handle could in fact be a struct pointer that has been cast to int. In practice, it is an index into some table of FILE (or similar) which is maintained entirely by the standard library (or the operating system) so you don't get to see it and therefore cannot foul it up.

    These are all very primitive mechanisms for achieving encapsulation that people were using before Object-Oriented Programming was invented. And since there were no guiding principles and no standard way of doing things, people were doing hacks here and there, which were violating the encapsulation in various ways.

    • For example, the C standard library could be treating FILE as an opaque pointer, but for some reason most implementations choose not to, because they define the struct for FILE in a header file that you can actually #include. (See Stack Overflow: Where is FILE defined in Unix)

    • As another example, you are not supposed to interpret the value of a file handle, but the file handles for the standard streams (input, output, error) do have very publicly known values, which are, essentially, nothing but magic numbers.