C 头文件 鸡还是蛋的问题
C header file The chicken or The egg problem
我正在开发一个 c 程序(一个合成器),我突然 运行 遇到了问题。
问题是:
我有两个头文件 server.h 和 seat.h,每个头文件由一个结构组成。
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);
主要问题是它创建了一个头文件循环,所以当我编译它时会导致错误。
我已经阅读了这里的问题 C header file loops。我试过它在 struct 的情况下有效但是当我调用 create_seat()
函数时它告诉我类型不匹配。在我的例子中,我也使用 typedef
,所以有点混乱。
由于实际代码在任何机器上都不好运行(因为它需要依赖等)请使用此代码作为参考,这解释了我的实际问题。
我使用介子构建系统。如果我使用 ninja 编译程序,它会以无限循环结束。
代码如下:
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 - 在 src 文件夹中
sources = files(
'main.c',
'server.c',
'seat.c'
)
executable(
'sample',
sources,
include_directories: [inc],
install: false,
)
meson.build 在项目文件夹中
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')
目录结构如下:
<project_folder>
|--->src
| |--->server.c
| |--->seat.c
| |--->meson.build
|
|--->include
| |--->server.h
| |--->seat.h
|
|--->meson.build
我已经给出了与原项目相同的目录结构
如果您只需要一个指向另一个 struct
的指针,您可以像这样转发声明那个:
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;
解决冲突的方法是添加一个不完整结构类型的前向声明。前向声明中使用的 struct
类型需要一个标签,同一标签用于同一类型的完整声明。为了对称,添加 Seat
和 Server
结构类型的前向声明是有意义的。 typedef
类型定义可以移动到 seat.h 和 server.h[ 包含的一个或多个新头文件中=40=]头文件。头文件需要定义guard macros以避免多重定义冲突。
例如:
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
新的头文件seat_server_t.h不需要main.c[=等.c文件直接包含40=]。它只能被其他头文件 seat.h 和 server.h 视为内部文件。如果需要,它也可以分成两个单独的头文件(每个头文件一个 typedef
)。
我正在开发一个 c 程序(一个合成器),我突然 运行 遇到了问题。
问题是:
我有两个头文件 server.h 和 seat.h,每个头文件由一个结构组成。
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);
主要问题是它创建了一个头文件循环,所以当我编译它时会导致错误。
我已经阅读了这里的问题 C header file loops。我试过它在 struct 的情况下有效但是当我调用 create_seat()
函数时它告诉我类型不匹配。在我的例子中,我也使用 typedef
,所以有点混乱。
由于实际代码在任何机器上都不好运行(因为它需要依赖等)请使用此代码作为参考,这解释了我的实际问题。
我使用介子构建系统。如果我使用 ninja 编译程序,它会以无限循环结束。
代码如下:
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 - 在 src 文件夹中
sources = files(
'main.c',
'server.c',
'seat.c'
)
executable(
'sample',
sources,
include_directories: [inc],
install: false,
)
meson.build 在项目文件夹中
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')
目录结构如下:
<project_folder>
|--->src
| |--->server.c
| |--->seat.c
| |--->meson.build
|
|--->include
| |--->server.h
| |--->seat.h
|
|--->meson.build
我已经给出了与原项目相同的目录结构
如果您只需要一个指向另一个 struct
的指针,您可以像这样转发声明那个:
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;
解决冲突的方法是添加一个不完整结构类型的前向声明。前向声明中使用的 struct
类型需要一个标签,同一标签用于同一类型的完整声明。为了对称,添加 Seat
和 Server
结构类型的前向声明是有意义的。 typedef
类型定义可以移动到 seat.h 和 server.h[ 包含的一个或多个新头文件中=40=]头文件。头文件需要定义guard macros以避免多重定义冲突。
例如:
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
新的头文件seat_server_t.h不需要main.c[=等.c文件直接包含40=]。它只能被其他头文件 seat.h 和 server.h 视为内部文件。如果需要,它也可以分成两个单独的头文件(每个头文件一个 typedef
)。