How to include opaque type in multiple .c files?
Asked Answered
N

2

0

I am supposed to make a program based on a header file and some further description. The problem working with opaque type is needed. Opaque struct is declared in header file with some other functions. But each function is supposed to have its own .c file and here comes the question. What should I do or where should I define the opaque struct so my functions can work with that?

I have files like:

header.h source.c(main) function1.c fuction2.c etc.

And in this situation I have no idea what to do.

Nabal answered 16/4, 2019 at 12:43 Comment(3)
I saw this one but my confusion is coming from that I dont have 1 header and 1 .c fileNabal
Shared header for the opaque type should contain a forward declaration (i.e. struct my_type;, without actual contents) and prototypes for functions which will know its contents. Other .c files can then reference it as a pointer (struct my_type*) and use the mentioned functions to access it contents indirectly.Echino
See also: Opaque C structs: various ways to declare them and How to hide the struct implementation and avoid variable has incomplete type at the same time in c?.Austreng
S
3

Your global header file should define a type name without its implementation, along with prototypes of functions to create, destroy and handle data of that type.

header.h

#ifndef HEADER_H_
#define HEADER_H_

struct XyzData;

typedef struct XyzData XYZDATA;
typedef struct XyzData *P_XYZDATA;

P_XYZDATA CreateEmptyXyzData();
P_XYZDATA CreateFilledXyzData(int param1, int param2);
void DestroyXyzData(P_XYZDATA pData);
int ModifyXyzData(P_XYZDATA pData, int action, int param);

#endif

Then you need an internal header, which provides an implementation for the structure.

xyzintheader.h

#ifndef XYZINTHEADER_H_
#define XYZINTHEADER_H_

#include "header.h"    // include the XyzData declaration

struct XyzData {       // and add its definition
    int   par1;
    int   par2;
};

#endif

Then files with implementations for specific routines will use the internal header, because they need to know the structure details to access them.

xyzcreate.c

#include "xyzintheader.h"
#include <stdlib.h>

P_XYZDATA CreateEmptyXyzData() {
    return calloc(1, sizeof(XYZDATA));
};

xyzcreatf.c

#include "xyzintheader.h"
#include <stdlib.h>

P_XYZDATA CreateFilledXyzData(int param1, int param2) {
    if (P_XYZDATA pData = malloc(sizeof(XYZDATA))) {
        pData->par1 = param1;
        pData->par2 = param2;
        return pData;
    }
    return NULL;
};

xyzdestr.c

#include "xyzintheader.h"
#include <stdlib.h>

void DestroyXyzData(P_XYZDATA pData) {
    free(pData);
}

xyzmodif.c

#include "xyzintheader.h"

int ModifyXyzData(P_XYZDATA pData, int action, int param) {
    if (! pData)
        return -1;
    switch (action) {
    case 0:
        // do something
        pData->par1 = pData->par2 = 0;
        return 0;

    case 1:
        // do something
        pData->par1 += param;
        pData->par2 -= param;
        return 0;
    }

    return -2;
}

And external modules will use the public header, because they only need to know the structure exists and what tools to use to handle it.

source.c

#include "header.h"

void SomeExternalLogic()
{
    if (P_XYZDATA pData = CreateEmptyXyzData()) {
        ModifyXyzData(pData, 0, 0);
        ModifyXyzData(pData, 1, 3);
        if (ModifyXyzData(pData, 13, 17) < 0)
            WriteSomeErrorMessage();

        DestroyXyzData(pData);
    }
}
Stepdaughter answered 16/4, 2019 at 13:42 Comment(0)
S
5

I would create a private (internal) header file, separate from the public header.h file, which contains the definition of the struct.

For consistency it should probably include the public header. Something like:

#ifndef INTERNAL_HEADER_H_
#define INTERNAL_HEADER_H_

#include "header.h"  // contains 'struct foo;'

struct foo {
    int data;  // or whatever
};

#endif

Then each of your .c files can include the internal_header.h and access the struct members.

Stage answered 16/4, 2019 at 12:53 Comment(2)
so I will have 2 header files and every .c file will include both of them. Am I right?Nabal
@Nabal You will have 2 header files, but your .c files only need to include one of them because the internal header includes the public header automatically, at least if you do it the way described above.Stage
S
3

Your global header file should define a type name without its implementation, along with prototypes of functions to create, destroy and handle data of that type.

header.h

#ifndef HEADER_H_
#define HEADER_H_

struct XyzData;

typedef struct XyzData XYZDATA;
typedef struct XyzData *P_XYZDATA;

P_XYZDATA CreateEmptyXyzData();
P_XYZDATA CreateFilledXyzData(int param1, int param2);
void DestroyXyzData(P_XYZDATA pData);
int ModifyXyzData(P_XYZDATA pData, int action, int param);

#endif

Then you need an internal header, which provides an implementation for the structure.

xyzintheader.h

#ifndef XYZINTHEADER_H_
#define XYZINTHEADER_H_

#include "header.h"    // include the XyzData declaration

struct XyzData {       // and add its definition
    int   par1;
    int   par2;
};

#endif

Then files with implementations for specific routines will use the internal header, because they need to know the structure details to access them.

xyzcreate.c

#include "xyzintheader.h"
#include <stdlib.h>

P_XYZDATA CreateEmptyXyzData() {
    return calloc(1, sizeof(XYZDATA));
};

xyzcreatf.c

#include "xyzintheader.h"
#include <stdlib.h>

P_XYZDATA CreateFilledXyzData(int param1, int param2) {
    if (P_XYZDATA pData = malloc(sizeof(XYZDATA))) {
        pData->par1 = param1;
        pData->par2 = param2;
        return pData;
    }
    return NULL;
};

xyzdestr.c

#include "xyzintheader.h"
#include <stdlib.h>

void DestroyXyzData(P_XYZDATA pData) {
    free(pData);
}

xyzmodif.c

#include "xyzintheader.h"

int ModifyXyzData(P_XYZDATA pData, int action, int param) {
    if (! pData)
        return -1;
    switch (action) {
    case 0:
        // do something
        pData->par1 = pData->par2 = 0;
        return 0;

    case 1:
        // do something
        pData->par1 += param;
        pData->par2 -= param;
        return 0;
    }

    return -2;
}

And external modules will use the public header, because they only need to know the structure exists and what tools to use to handle it.

source.c

#include "header.h"

void SomeExternalLogic()
{
    if (P_XYZDATA pData = CreateEmptyXyzData()) {
        ModifyXyzData(pData, 0, 0);
        ModifyXyzData(pData, 1, 3);
        if (ModifyXyzData(pData, 13, 17) < 0)
            WriteSomeErrorMessage();

        DestroyXyzData(pData);
    }
}
Stepdaughter answered 16/4, 2019 at 13:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.