Levels of visibility of symbols from a compilation unit - C - c

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

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?

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.

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.

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);
...

Related

List.h in linux files?

in linux/include/linux/list.h I found:
/**
* list_entry - get the struct for this entry
* #ptr: the &struct list_head pointer.
* #type: the type of the struct this is embedded in.
* #member: the name of the list_head within the struct.
*/
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)
What do they mean by "get the struct for this entry" can I see a usage example to understand in a better way?
This is a great example of a kind of Polymorphism in C. To borrow terminology from C++, the list_entry() macro allows you to downcast from a list_head type to any type that contains it.
Have a look at kthread.c for a simple fundamental example:
kernel/kthread.c:
struct kthread_create_info
{
/* Information passed to kthread() from kthreadd. */
int (*threadfn)(void *data);
void *data;
int node;
/* Result passed back to kthread_create() from kthreadd. */
struct task_struct *result;
struct completion *done;
struct list_head list;
};
...
int kthreadd(void *unused)
{
...
while (!list_empty(&kthread_create_list)) {
struct kthread_create_info *create;
create = list_entry(kthread_create_list.next,
struct kthread_create_info, list);
...
create_kthread(create);
By including a list_head object in the kthread_create_info struct, you can say that kthread_create_info "derives" from list_head. This allows kthread_create_info objects to be used as nodes in a list, meaning you can pass them to any of the functions declared in list.h by simply dereferencing the list member of the struct. The list_entry macro then gives you the mapping from a base class pointer to its derived start address.
In other words, given a list_head object that you know is contained within an outer kthread_create_info struct, you can recover a pointer to the kthread_create_info container.
This is an extremely common pattern in C programming, where object oriented constructs are desired, but a C++ compiler isn't available.

Typedef of a type defined in a separate header file

I have two header files:
src/util/buffer.h:
//Namespace Src Util Buffer sub
struct sub_buffer{
size_t size;
void *buf;
};
//tons of static inline functions
src/lib_context.h:
//Namespace Src Lib Context slc
typedef struct sub_buffer slc_buffer; // Is this typedef ok?
struct slc_context{
//definition
};
void slc_set_buffer(slc_buffer *buf_ptr);
//tons of other structs and functions
The thing that I was not sure about was the typedef struct sub_buffer slc_buffer;. There was a choice to include the src/util/buffer.h, but that would intoroduce tightly coupling to the header and it would be more difficult to replace it with e.g. another buffer definition containing flexible array member.
Is it common to introduce such a typedef to the structure that is defined in another header file so its implementation will be provided in the c file when including the header (but not to include one header to another header file)?
No, that would be an error.
You probably meant
typedef struct sub_buffer slc_buffer;
in which case it's fine, you can always introduce typedef aliases to types, even without having those types defined in the scope you're in.
This is the reason the classical self-referencing "node" works:
typedef struct node node;
struct node {
node *next;
void *data;
};
Notice how on the first line a typedef for an unknown type is used.

Prevent value from changing on c

I decided to make static library realising doubly linked lists with functions. Its header file is like this now:
#ifndef LISTS
#define LISTS
#define LIST {0, NULL, NULL}
typedef struct node node;
typedef struct list {
unsigned int length;
node *beginning;
node *end;
} list;
void listAppend(list *list, int value);
int listPop(list *list);
char listRemove(list *list, int value);
void listPrint(list *list);
void listClear(list *list);
#endif
i.e. user should initialize list with list myList = LIST;.
Can I prevent list.length from casual changing by user in his code like list.length++?
Usually, if you want to hide implementation from client in pure C, you might use pointers to incomplete types. To do this, you put forward declaration of your struct in .h file and its full declaration in *.c file. You can't even add literal zero to a pointer to incomplete type, not to mention dereference it and/or alter some data.
Also, if you want to go against all odds and put your lists's header on stack, you might want to write a macro around alloca(), however I'm not sure how to calculate size of your struct in *.h file without having its declaration in scope. it's possible via extern const, but IMHO it's too complicated.

Using a struct in a header file "unknown type" error

I am using Kdevelop in Kubuntu.
I have declared a structure in my datasetup.h file:
#ifndef A_H
#define A_H
struct georeg_val {
int p;
double h;
double hfov;
double vfov;
};
#endif
Now when I use it in my main.c file
int main()
{
georeg_val gval;
read_data(gval); //this is in a .cpp file
}
I get the following error:
georeg_chain.c:7:3: error: unknown type name 'georeg_val'
(This is in the georeg_val gval; line)
I would appreciate if anyone could help me resolve this error.
In C one has two possibilities to declare structure:
struct STRUCT_NAME {} ;
or
typedef struct {} STRUCT_ALIAS;
If you use first method (give struct a name) - you must define variable by marking it explicitly being a struct:
struct STRUCT_NAME myStruct;
However if you use second method (give struct an alias) then you can omit struct identifier - compiler can deduce type of variable given only it's alias :
STRUCT_ALIAS myStruct;
Bonus points:
You can declare struct with both it's name and alias:
typedef struct STRUCT_TAG {} STRUCT_TAG;
// here STRUCT_NAME == STRUCT_ALIAS
Then in variable definition you can use either first or second method. Why both of two worlds is good ? Struct alias lets you to make struct variable definitions shorter - which is a good thing sometimes. But struct name let's you to make forward declarations. Which is indispensable tool in some cases - consider you have circular references between structs:
struct A {
struct B * b;
}
struct B {
struct A * a;
}
Besides that this architecture may be flawed - this circular definition will compile when structs are declared in the first way (with names) AND struct pointers are referenced explicitly by marking them as struct.
If you have to define a new type, you have to write:
typedef struct {
int p;
double h;
double hfov;
double vfov;
} georeg_val ;
Then you can use georeg_val as a new type.
Defining a struct type (on this example, a binary search tree struct):
struct tree {
int info;
struct tree *left;
struct tree *right;
}
typedef struct tree treeNode;
Declaring a function eg.:
treeNode *insertElement(treeNode *treeA, int number);

LinkedList, struct inclusion issue

liststructs.h:
struct _data_object {
int temp;
int interval_length;
};
typedef struct _data_object temp_data_object;
struct _list_node {
data_object *temp_data;
struct _list_node *prev;
struct _list_node *next;
};
typedef struct _list_node list_node;
struct _list {
int time;
list_node *head;
list_node *tail;
};
typedef struct _list list;
list.h:
list_node *alloc_node(int temp, int interval_length);
list_node *alloc_dummy_node(void);
list *alloc_temp_list(void);
void delete_first(list *list);
void insert_node(list *list, list_node *new_node);
void insert(list *list, int temperature, int interval);
I then use this in another file called calculations.c and in main.c, but then I declare extern list *xs; in calculations.h (it is defined in calculations.c) it complains:
Error[Pe020]: identifier "list" is undefined
I have included liststructs.h and list.h in that order in calculations.c and main.c and want to use xs in calculations and main.
Also:
What is better? To have structs and listoperations declared in the same header or separate them?
Protect your include files with #include safeguards, include liststructs.h in list.h, and both files in calculations.h. Safeguards in header files are typically written as:
#ifndef _XXXX_H_ // XXXX = LIST, LISTSTRUCT etc
#define _XXXX_H_
// definitions for file XXXX.h
#endif /* _XXXX_H_ */
From what you've told us, you have extern list *xs; declared in calculations.h, but didn't mention having included liststructs.h before that line which defines the identifier list.
liststructs.h needs to be included anywhere before you use the identifier list, just as list.h must be included before you attempt to call any of the functions it declares.
As long as you have include/header guards don't worry about including header files multiple times in a translation unit.

Resources