What is an opaque pointer in C?
Asked Answered
C

3

105

May I know the usage and logic behind the opaque pointer concept in C?

Classics answered 26/9, 2011 at 10:29 Comment(4)
Yes you may: en.wikipedia.org/wiki/Opaque_pointerJerlenejermain
I didn't understand anything from the wiki page.Classics
Hmmm; this question has about C has been duplicated to one tagged C++. That's not ideal. There is enough commonality that it isn't a major problem, but C++ has options and features that are not available in C.Clave
Related: Opaque C structs: how should they be declared?Salsify
A
157

An opaque pointer is one in which no details are revealed of the underlying data (from a dictionary definition: opaque: adjective; not able to be seen through; not transparent).

For example, you may declare in a header file (this is from some of my actual code):

typedef struct pmpi_s *pmpi;

which declares a type pmpi which is a pointer to the opaque structure struct pmpi_s, hence anything you declare as pmpi will be an opaque pointer.

Users of that declaration can freely write code like:

pmpi xyzzy = NULL;

without knowing the actual "definition" of the structure.

Then, in the code that knows about the definition (ie, the code providing the functionality for pmpi handling, you can "define" the structure:

struct pmpi_s {
    uint16_t *data;     // a pointer to the actual data array of uint16_t.
    size_t sz;          // the allocated size of data.
    size_t used;        // number of segments of data in use.
    int sign;           // the sign of the number (-1, 0, 1).
};

and easily access the individual fields of it, something that users of the header file cannot do.

More information can be found on the Wikipedia page for opaque pointers..

The main use of it is to hide implementation details from users of your library. Encapsulation (despite what the C++ crowd will tell you) has been around for a long time :-)

You want to publish just enough details on your library for users to effectively make use of it, and no more. Publishing more gives users details that they may come to rely upon (such as the fact the size variable sz is at a specific location in the structure, which may lead them to bypass your controls and manipulate it directly.

Then you'll find your customers complaining bitterly when you change the internals. Without that structure information, your API is limited only to what you provide and your freedom of action regarding the internals is maintained.

Alow answered 26/9, 2011 at 10:31 Comment(9)
Well, what is the use of this?Classics
The opaque pointer is xyzzy, yes. The opaque pointer type is pmpi and the opaque structure is struct pmpi. The main use of it is to do with encapsulation, the ability to hide implementation details from users. I'll update answer with details.Alow
Thank you very much for the help.Appreciating your co operation.Classics
@Alow What is the use of the pmpi pointer for the enduser. How end-user is going to use that opaque pointer. This can be declared inside the module who is writing the functionality, can u please quote some exampleTacye
@Alow No thanks I got an example. FILE is one of the Opaque pointeTacye
@VinothKumar, the only thing a user should do with the opaque pointer is get it from, or pass it to, functions that do know about its internals, just like fopen or fclose for FILE *. Whether FILE is truly opaque depends on what you find in stdio.h - if it's fully declared there rather than just a type similar to the one in this answer, users can get at the internals so it's opaque only by mutual consent :-)Alow
Note that typedef struct pmpi_s *pmpi; is not ideal: someone may assume that e.g. void f(const pmpi val) has no side effects on its argument which would not be true.Baguio
@DmitryGrigoryev Works, thanks! your->Genius_Level++Supertanker
@DmitryGrigoryev: then you could use type name to make it clearer that it's a pointer, such as pmpi_ptr and pmpi_cptr (for pointers to non-const and const data). But, provided it's made clear that the type is a pointer somewhere (even in the documentation), users will be on notice about how they can use an object of the type. Just keep in mind that was an example to illustrate the concept of opaque pointers, not necessarily battle-hardened code :-)Alow
B
22

Opaque pointers are used in the definitions of programming interfaces (API's).

Typically they are pointers to incomplete structure types, declared like:

typedef struct widget *widget_handle_t;

Their purpose is to provide the client program a way to hold a reference to an object managed by the API, without revealing anything about the implementation of that object, other than its address in memory (the pointer itself).

The client can pass the object around, store it in its own data structures, and compare two such pointers whether they are the same or different, but it cannot dereference the pointers to peek at what is in the object.

The reason this is done is to prevent the client program from becoming dependent on those details, so that the implementation can be upgraded without having to recompile client programs.

Because the opaque pointers are typed, there is a good measure of type safety. If we have:

typedef struct widget *widget_handle_t;
typedef struct gadget *gadget_handle_t;

int api_function(widget_handle_t, gadget_handle_t);

if the client program mixes up the order of the arguments, there will be a diagnostic from the compiler, because a struct gadget * is being converted to a struct widget * without a cast.

That is the reason why we are defining struct types that have no members; each struct declaration with a different new tag introduces a new type that is not compatible with previously declared struct types.

What does it mean for a client to become dependent? Suppose that a widget_t has width and height properties. If it isn't opaque and looks like this:

typedef struct widget {
  short width;
  short height;
} widget_t;

then the client can just do this to get the width and height:

int widget_area = whandle->width * whandle->height;

whereas under the opaque paradigm, it would have to use access functions (which are not inlined):

// in the header file
int widget_getwidth(widget_handle_t *);
int widget_getheight(widget_handle_t *);

// client code
int widget_area = widget_getwidth(whandle) * widget_getheight(whandle);

Notice how the widget authors used the short type to save space in the structure, and that has been exposed to the client of the non-opaque interface. Suppose that widgets can now have sizes that don't fit into short and the structure has to change:

typedef struct widget {
  int width;
  int height;
} widget_t;

Client code must be re-compiled now to pick up this new definition. Depending on the tooling and deployment workflow, there may even be a risk that this isn't done: old client code tries to use the new library and misbehaves by accessing the new structure using the old layout. That can easily happen with dynamic libraries. The library is updated, but the dependent programs are not.

The client which uses the opaque interface continues to work unmodified and so doesn't require recompiling. It just calls the new definition of the accessor functions. Those are in the widget library and correctly retrieve the new int typed values from the structure.

Note that, historically (and still currently here and there) there has also been a lackluster practice of using the void * type as an opaque handle type:

typedef void *widget_handle_t;
typedef void *gadget_handle_t;

int api_function(widget_handle_t, gadget_handle_t);

Under this scheme, you can do this, without any diagnostic:

api_function("hello", stdout);

The Microsoft Windows API is an example of a system in which you can have it both ways. By default, various handle types like HWND (window handle) and HDC (device context) are all void *. So there is no type safety; a HWND could be passed where a HDC is expected, by mistake. If you do this:

#define STRICT
#include <windows.h>

then these handles are mapped to mutually incompatible types to catch those errors.

Bronco answered 17/1, 2020 at 18:57 Comment(0)
O
2

Opaque as the name suggests is something we can’t see through. E.g. wood is opaque. Opaque pointer is a pointer which points to a data structure whose contents are not exposed at the time of its definition.

Example:

struct STest* pSTest;

It is safe to assign NULL to an opaque pointer.

pSTest = NULL; 
Orleans answered 27/4, 2019 at 16:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.