cc-header

struct datatype is defined in the .c file and it is used in the header file while the normal situation has to be the opposite


The definition of struct datatype should be in the header file and not the opposite. This is what I understand while this project does the opposite in this special case. What drive the contributor to decide to do so and why ? I am aware it is a design decision but shall I submit a direct question to the code contributor? I was looking at the GNU masscan project and came to my attention the two file event-timeout.c which has the definition of a struct datatype

struct Timeouts {
    /**
     * This index is a monotonically increasing number, modulus the mask.
     * Every time we check timeouts, we simply move it foreward in time.
     */
    uint64_t current_index;

    /**
     * The number of slots is a power-of-2, so the mask is just this
     * number minus 1
     */
    unsigned mask;

    /**
     * The ring of entries.
     */
    struct TimeoutEntry *slots[1024*1024];
};

while in the header file event-timeout.h contains the below statement to import the "Timeouts" struct datatype

struct Timeouts;
struct Timeouts *timeouts_create(uint64_t timestamp_now);

I am not sure why the struct Timeouts is not defined in the header file ?


Solution

    1. C encourages interface and type declarations to placed in header files, and implementation the .c files. A struct may leak implementation details that you prefer to hide (to makes changes easier or even possible). You do that by only exposing a forward declaration of your struct optionally as a typedef struct or typedef struct * and functions to allocate and free an instance of the object (forcing heap allocation):
    #ifndef FOO_H
    #define FOO_H
    
    typedef struct foo foo;
    foo *foo_create();
    void foo_bar(foo *f, bar *none);
    foo_destroy(foo *f);
    
    #endif
    

    This improves incremental build time. In particular a small exposed implementation detail (that merely change the size of a struct) may have ripple effect that result in a large part of an entire code base having to be recompiled. Btw, Michael Feathers: "Effectively with Legacy Code", while dated, is a really interesting book on this subject.

    1. To link against code without having the source for the definitions. We do this all the time with libraries.

    2. Variables can only be declared once so you need to make to make them extern in a header file, and then most likely link the declaration once.