0

In the below code,

/**********linkedListImpl.c ***********/

#include"list/list.h"

#if defined(LINKED_LIST)



/***************** Representation - start ******************/
  /* struct members are not visible to other .c files */
  static struct DListNode{

    void *item;
    struct DListNode *next;
    struct DListNode *prev;
  };

  /* Should be used in this .c file, only, so static */
  static typedef struct DListNode DListNode;
  static DListNode* createNode(void *);


  static struct List{

    DListNode *head;
    int size; /*size attribute is not part of list definition, but quick way 
                  to help user code */
  }List;


  .....
  #endif

/************ list.h ************/
#ifndef LIST_H /* Header guard */
#define LIST_H
#include"type.h"

/***************** Usage-start ************/

#if defined(ARRAY) || (LINKED_LIST)

  typedef struct List List;

#else
  #error "Wrong list implementation macro name !!!"
#endif

 ...
#endif /* LIST_H */

List type is declared in list.h, using staticspecifier,

  typedef struct List List;

and defined in linkedListImpl.c, using staticspecifier,

 static struct List{

    DListNode *head;
    int size;
  }List;

Purpose: To make List symbol available to user(main.c) only through a pointer(List*). User(main.c) should not be able to access List members in main.c.

In linkedListImpl.c Symbol DListNode is defined using static specifier,

  static struct DListNode{

    void *item;
    struct DListNode *next;
    struct DListNode *prev;
  };

  static typedef struct DListNode DListNode;

Purpose: For symbol DListNode, user(main.c) should neither be able to access DListNode members in main.c nor access DListNode object through DListNode* pointer.

Is this the right approach to hide the symbols List & DListNode?

Note: Here is the complete code

overexchange
  • 15,768
  • 30
  • 152
  • 347

4 Answers4

4

Yes, this is it. It is called opaque pointer, and is used to hide the implementation details in the interface.

You may be interested by What is an opaque pointer in C?

Community
  • 1
  • 1
Burns
  • 146
  • 4
  • `static struct List {...}List` does 2 things: i defines a structure `List`, and it also declares a `static` variable named `List` of type `struct List`. The word `static` concerns only the variable `List`, not the structure `List`. `DListNode` is written as a structure definition only, so the `static` word is useless here, as Basile answered. – Burns Dec 23 '16 at 22:09
3

static struct DListNode { ... }; is useless (it declares no variables of struct DListNode), but do declare the struct DListNode so is exactly the same as simply struct DListNode { ... };. It is like static int; declaring a list of 0 integer variables (notice that static int x,y; is a common way of declaring a list of two variables x & y).

A very common way is to define all your struct (and their fields) in public header files. Indeed, you are showing implementation details (but the compiler needs them to emit code).

A less common way is to declare a struct in a header file, and only use pointers to it (an opaque pointer, like Burns' answer). Then you might have an implementation defining the members of that struct. See also this.

A common example is the FILE from <stdio.h>. It is very likely to be defined as some struct but the field names are conventionally hidden and unused.

C programming is a lot about having good conventions and following them.

Community
  • 1
  • 1
Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
2

What you have will work.

The typedef you have in the header file also acts as a forward declaration of the structure. This is sufficient to pass around a pointer to the struct type but prevents the pointer from being dereferenced.

This allows you to keep the implementation details of the struct private to the .c file were it is defined.

dbush
  • 205,898
  • 23
  • 218
  • 273
1

When you need to create opaque references in plain C the best way is to create 2 separate sets of headers, one with full declarations that will be used in sources that implement the 'methods', and another header with the opaque reference for user use.

This is the common strategy used in many OS's. Take a look to MS headers, they use HANDLEs to refer to objects. The HANDLES are created and passed to system functions, plain C simulation of C++ methods, that resides in modules using the full definition header.

Consider these 2 headers:

/* Internal header "InnerDList.h" */
  struct DListNode{
    void *item;
    struct DListNode *next;
    struct DListNode *prev;
  };

2nd

/* User header "DList.h" */
typedef struct DListNode DListNode;

DListNode *CreateDList(void * data, ...);
HRESULT    AddDListNode(DListNode *Dlist, void *data);
HRESULT    GetDListNode(DListNode *Dlist, void *SearchData);
...

Using the first allow the compilation of the functions that works on the DList structures, the second allows the use of DList's through an opaque object.

A variation to allow the use of full or partial definition could be the following: define a preprocessor symbol in the internal header and use this to conditionally define opaque objects.

/* Internal header "InnerDList.h" */
#define INNERDLIST 1    //We will use this symbol to modify user header

  typedef struct DListNode{
    void *item;
    struct DListNode *next;
    struct DListNode *prev;
  } DListNode;
#include "DList.h"    //Now include user header

In the User header:

/* User header "DList.h" */
#ifndef INNERDLIST
typedef struct DListNode DListNode;    //define only if not called by internal header
#endif
DListNode *CreateDList(void * data, ...);
HRESULT    AddDListNode(DListNode *Dlist, void *data);
HRESULT    GetDListNode(DListNode *Dlist, void *SearchData);
...
Frankie_C
  • 4,764
  • 1
  • 13
  • 30
  • If I even do not want use of `DListNode` objects, through opaque object, in other compilation unit, what should be the approach? – overexchange Dec 23 '16 at 21:55
  • @overexchange What you mean? That you want to have full definition in some other modules? – Frankie_C Dec 23 '16 at 21:58
  • In my code, intention is to make `List`'s available through opaque pointer in `main.c`, but `DListNode`'s should not even be available through opaque pointers(completely hidden) in `main.c`. `DListNode`'s are purely used in `linkedListImpl.c` **only**. What is the approach for setting such level of visibility for `DListNode` ? – overexchange Dec 23 '16 at 22:00
  • @overexchange You have to create appropriate headers that selectively include what you want to make available through full definition, opaque pointer or nothing at all. I have added an example on how you can manage it. Is it clear enough? – Frankie_C Dec 23 '16 at 22:08