0

I was developing a c program (a compositor) and I suddenly ran into a problem.

Here is the problem:

I have two header files server.h and seat.h which consists of one struct each.

server.h

typedef struct
{
    const char *socket;
    struct wl_display *wl_display;
    struct wl_event_loop *wl_event_loop;
    struct wlr_backend *backend;
    struct wlr_renderer *renderer;
    struct wlr_compositor *compositor;
    struct wlr_output_layout *output_layout;

    Seat *seat; // the seat struct from seat.h

    struct wl_list outputs;

    struct wl_listener output_listener;
} Server;

bool init_server(Server *server);
void run_server(Server *server);
void destroy_server(Server *server);

seat.h

typedef struct
{
    Server *server;  // the server struct from server.h
    struct wlr_seat *wlr_seat;

    struct wl_listener input_listener;
    struct wl_listener destroy_seat;
} Seat;

Seat *create_seat(Server *server);
void handle_new_input(struct wl_listener *listener, void *data);
void destroy_seat(struct wl_listener *listener, void *data);

The main problem is that it creates a loop of header files so when I compile it causes error.

I have read about the problem in here C header file loops. And I tried this it worked in the case of struct but when I call the create_seat() function it is telling that the type is mismatched. In my case I'm using typedef too so it's a little bit confusing.

Since the actual code is not good to run on any machines (because it need dependencies and so on) please use this code as the reference, this explains my actual problem.

I uses meson build system. If I compile the program using ninja it ends in an infinite loop.

Here's the code:

main.c

#include <stdio.h>
#include "server.h"
#include "seat.h"

int main()
{
    Server server;
    server.id=10;

    Seat seat;
    seat.id=20;

    server.seat=seat;
    seat.server=server;

    printSeatId(server);
    printServerId(seat);
    return 0;
}

server.h

#include "seat.h"
typedef struct
{
    Seat seat;
    int id;
} Server;

void printSeatId(Server s);

seat.h

#include "server.h"
typedef struct
{
    Server server;
    int id;
} Seat;

void printServerId(Seat s);

server.c

#include <stdio.h>
#include "server.h"
void printSeatId(Server s)
{
    printf("%d",s.seat.id);
}

seat.c

#include <stdio.h>
#include "seat.h"
void printServerId(Seat s)
{
    printf("%d",s.server.id);
}

meson.build - in src folder

sources = files(
  'main.c',
  'server.c',
  'seat.c'
)

executable(
  'sample',
  sources,
  include_directories: [inc],
  install: false,
)

meson.build in project folder

project(
  'sample',
  'c',
  version: '1.0.0',
  meson_version: '>=0.56.0',
  default_options: ['c_std=c11','warning_level=2'],
)

add_project_arguments(
  [
    '-DWLR_USE_UNSTABLE',
    '-Wno-unused',
    '-Wno-unused-parameter',
    '-Wno-missing-braces',
    '-Wundef',
    '-Wvla',
    '-Werror',
    '-DPACKAGE_VERSION="' + meson.project_version() + '"',
  ],
  language: 'c',
)

cc = meson.get_compiler('c')
inc = include_directories('include')
subdir('src')

Here is the directory structure :

<project_folder>
     |--->src
     |     |--->server.c
     |     |--->seat.c
     |     |--->meson.build
     |
     |--->include
     |     |--->server.h
     |     |--->seat.h
     |
     |--->meson.build

I have given the same directory structure of the original project.

halfer
  • 19,824
  • 17
  • 99
  • 186
trickymind
  • 557
  • 5
  • 21

2 Answers2

2

If you just need a pointer to the other struct, you can forward-declare that one, like this:

typedef struct Server Server;

typedef struct
{
    Server *server;  // the server struct from server.h
    struct wlr_seat *wlr_seat;

    struct wl_listener input_listener;
    struct wl_listener destroy_seat;
} Seat;
the busybee
  • 10,755
  • 3
  • 13
  • 30
2

The way to resolve the conflict is by adding a forward declaration of an incomplete structure type. The struct type used in the forward declaration needs a tag and the same tag is used for the complete declaration of the same type. For symmetry, it makes sense to add forward declarations of both the Seat and Server structure types. The typedef type definitions can be moved to one or more new header files that are included by the seat.h and server.h header files. The header files need to define guard macros to avoid multiple definition conflicts.

For example:

seat_server_t.h

#ifndef SEAT_SERVER_T_H__INCLUDED
#define SEAT_SERVER_T_H__INCLUDED

typedef struct Seat_s Seat;
typedef struct Server_s Server;

#endif

seat.h

#ifndef SEAT_H__INCLUDED
#define SEAT_H__INCLUDED

#include "seat_server_t.h"

/* Other includes for struct wl_listener, etc. here... */

struct Seat_s
{
    Server *server;
    struct wlr_seat *wlr_seat;

    struct wl_listener input_listener;
    struct wl_listener destroy_seat;
};

Seat *create_seat(Server *server);
void handle_new_input(struct wl_listener *listener, void *data);
void destroy_seat(struct wl_listener *listener, void *data);
#endif

server.h

#ifndef SERVER_H__INCLUDED
#define SERVER_H__INCLUDED

#include "seat_server_t.h"

/* Other #include's for struct wl_display, etc. here... */

struct Server_s
{
    const char *socket;
    struct wl_display *wl_display;
    struct wl_event_loop *wl_event_loop;
    struct wlr_backend *backend;
    struct wlr_renderer *renderer;
    struct wlr_compositor *compositor;
    struct wlr_output_layout *output_layout;

    Seat *seat; // the seat struct from seat.h

    struct wl_list outputs;

    struct wl_listener output_listener;
};

bool init_server(Server *server);
void run_server(Server *server);
void destroy_server(Server *server);

#endif

The new header file seat_server_t.h does not need to be included directly by the .c files such as main.c. It can be treated as for internal only by the other header files, seat.h and server.h. It could also be split into two separate header files (one for each typedef) if desired.

Ian Abbott
  • 15,083
  • 19
  • 33
  • Let me check this – trickymind Jan 08 '21 at 10:59
  • but this one gives a list of errors especially : invalid use of incomplete typedef ‘Server’ {aka ‘struct Server_s’} – trickymind Jan 08 '21 at 11:06
  • @trickymind Does that error occur within the code shown above or elsewhere? And is there a `#include "server.h"` in the code that contains the error? There are extra `#include`s needed to make it work, such as `#include ` and whatever defines all that `struct wl...` stuff. – Ian Abbott Jan 08 '21 at 11:19
  • see this for the log : https://pastebin.com/Q5Wwxk4M – trickymind Jan 08 '21 at 11:24
  • @trickymind Does server.c include both server.h and seat.h? – Ian Abbott Jan 08 '21 at 11:27
  • yes it needed both because the create_seat() function in seat.h is called from server.c. i have added the link to the code here: please have a look into it : https://firebasestorage.googleapis.com/v0/b/mycampus-release-version.appspot.com/o/boxspace.zip?alt=media&token=8005cb1d-3d22-4a35-b9f1-2c118b9e0115 – trickymind Jan 08 '21 at 11:36
  • @trickymind But that link seems to be the original source without any modifications similar to what I described above. – Ian Abbott Jan 08 '21 at 11:53
  • Yes that is the original source.when I applied your code I get the error that I shown above – trickymind Jan 08 '21 at 12:06
  • I managed to build it myself. I added "include/seat_server_t.h" and modified "include/seat.h" and "include/server.h" as above. (Both of them need `#include "seat_server_t.h"`.) I also needed to add `#include "server.h"` to "src/seat.c" since it dereferences pointers of type `Server *`. – Ian Abbott Jan 08 '21 at 12:30
  • Let me check it onesmore – trickymind Jan 08 '21 at 12:37
  • @trickymind Perhaps you forgot to change `typedef struct { ... } Server;` to `struct Server_s { ... };` in "server.h" (and a similar change for "seat.h")? – Ian Abbott Jan 08 '21 at 12:40
  • Thank you it worked ! ,i am also building an api for the compositor so i need to keep the names for struct as small as i can and so i came up with typedef. Once again thank you verymuch – trickymind Jan 08 '21 at 12:43
  • Just a note: AFAIK the names of `struct`s are separated from the names of types. So the _**compiler**_ does not need to get different names, but _**we**_ as the readers of the source. A `typedef struct AnyName AnyName;` is correct C. – the busybee Jan 08 '21 at 13:54