cabstract-typeopaque-pointers

How to include opaque type in multiple .c files?


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.


Solution

  • 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);
        }
    }