c - 定义类时宏扩展中的 GObject 编译错误

标签 c compiler-errors macros c-preprocessor gobject

问题

使用 GObject 和 C,我试图在模块“Foo”中创建名为“Bar”的类 GObject 的子类。但是,宏“G_DECLARE_FINAL_TYPE”(在 gobject/gtype.h 中定义)扩展不正确。它没有扩展到 FOO_BAR,而是什么都没有。因此,与其扩展为:

... FooBar * FOO_BAR (gpointer ptr) { ...

它扩展为:

... FooBar * (gpointer ptr) { ...

错误信息

我希望它能编译,但 gcc 给出了一个语法错误:

In file included from /usr/include/glib-2.0/gobject/gobject.h:24,
                 from /usr/include/glib-2.0/gobject/gbinding.h:29,
                 from /usr/include/glib-2.0/glib-object.h:22,
                 from foo-bar.h:4,
                 from foo-bar.c:1:
/usr/include/glib-2.0/gobject/gtype.h:1407:77: error: expected ‘)’ before ‘ptr’
 1407 | USED static inline ModuleObjName * MODULE##_##OBJ_NAME (gpointer ptr) {                       \
      |                                                                  ^~~

 foo-bar.h:10:1: note: in expansion of macro ‘G_DECLARE_FINAL_TYPE’
   10 | G_DECLARE_FINAL_TYPE (FooBar, foo_bar, FOO, BAR, GObject)
      | ^~~~~~~~~~~~~~~~~~~~

宏的定义

此定义来自 glib 2.66.7 库。我没有自己定义它。为了完整起见,我将其包括在内。

#define G_DECLARE_FINAL_TYPE(ModuleObjName, module_obj_name, MODULE, OBJ_NAME, ParentName)  \
  GType module_obj_name##_get_type (void);                                                  \
  G_GNUC_BEGIN_IGNORE_DEPRECATIONS                                                          \
  typedef struct _##ModuleObjName ModuleObjName;                                            \ 
  typedef struct { ParentName##Class parent_class; } ModuleObjName##Class;                  \
                                                                                            \
  _GLIB_DEFINE_AUTOPTR_CHAINUP (ModuleObjName, ParentName)                                  \
  G_DEFINE_AUTOPTR_CLEANUP_FUNC (ModuleObjName##Class, g_type_class_unref)                  \
                                                                                            \
  G_GNUC_UNUSED static inline ModuleObjName * MODULE##_##OBJ_NAME (gpointer ptr) {          \
    return G_TYPE_CHECK_INSTANCE_CAST (ptr, module_obj_name##_get_type (), ModuleObjName); }\
  G_GNUC_UNUSED static inline gboolean MODULE##_IS_##OBJ_NAME (gpointer ptr) {              \
    return G_TYPE_CHECK_INSTANCE_TYPE (ptr, module_obj_name##_get_type ()); }               \
  G_GNUC_END_IGNORE_DEPRECATIONS

部分预处理器输出

我包含了包含语法错误的定义并对其进行了格式化以便于阅读:

__attribute__((__unused__)) static inline FooBar * (gpointer ptr) {
  return (((FooBar*) g_type_check_instance_cast ((GTypeInstance*) (ptr), (foo_bar_get_type ()))));
}

奇怪的是下面的定义确实得到了正确的预处理:

__attribute__((__unused__)) static inline gboolean FOO_IS_BAR (gpointer ptr) {
  return ((__extension__ ({
    GTypeInstance *__inst = (GTypeInstance*) (ptr);
    GType __t = (foo_bar_get_type ());
    gboolean __r;
    if (!__inst) __r = (0);
    else if (__inst->g_class && __inst->g_class->g_type == __t) __r = (!(0));
    else __r = g_type_check_instance_is_a (__inst, __t); __r; }))); 
}

完整的最小示例

我在下面包含了一个导致我的错误的最小示例。

foo-bar.h

#ifndef FOO_BAR
#define FOO_BAR

#include <glib-object.h>

G_BEGIN_DECLS

#define FOO_TYPE_BAR (foo_bar_get_type ())

G_DECLARE_FINAL_TYPE (FooBar, foo_bar, FOO, BAR, GObject)

G_END_DECLS

#endif /* FOO_BAR */

foo-bar.c

#include "foo-bar.h"

struct _FooBar
{
    GObject parent_instance;
};

G_DEFINE_TYPE (FooBar, foo_bar, G_TYPE_OBJECT);

static void foo_bar_class_init (FooBarClass *klass)
{

}

static void foo_bar_init (FooBar *app)
{

}

生成文件

foo-bar.o: foo-bar.c
    gcc -c foo-bar.c $(shell pkg-config --cflags gobject-2.0) -o foo-bar.o
# included to debug the macro
foo-bar.pp: foo-bar.c
    gcc -E foo-bar.c $(shell pkg-config --cflags gobject-2.0) -o foo-bar.pp

版本

  • 能说会道 2.66.7
  • 海湾合作委员会 10.2.1
  • Linux (Fedora 33)

我尝试过的

我研究过glib's documentation on how to create a new class据我所知,我做得对。我还查看了 token 粘贴和宏扩展的工作原理,还尝试用谷歌搜索我的错误。它一定是显而易见的,但我无法弄清楚是什么。我还尝试用私有(private)数据声明一个可派生类,但在找到一个最小示例时,我将它更改为没有私有(private)数据的最终类,但仍然有相同的错误。我读到了 ## 运算符 here仍然无法判断出了什么问题。

相关堆栈溢出问题

最后,我查看了以下相关的 stackoverflow 帖子:

我的错误是预处理器没有发出连接的标记。在 this发布用户定义自己的宏并询问为什么一个调用有效而另一个无效。我使用的是一个库,而不是我自己的宏。

This问题是询问为什么宏在 unicode 模式和 ascii 模式下的扩展方式不同。我不处理 unicode,所以它不相关。

我没有使用 C++,只使用没有泛型语法的 C,所以 this问题不适用于我。

因为我没有使用 XCode this问题没有解决我的问题。

我理解宏扩展应该如何工作,所以 this问题确实告诉了我任何我还不知道的事情。

this 不同,我没有定义任何新的宏,也没有 namespace 冲突的问题。问题。

thisthis问题是用户不知道宏中的串联运算符。我是,我的问题是它也没有像我期望的那样工作。所以说运算符存在对我没有帮助。

This question is close to my problem , 但它正在发生,因为没有提供宏的参数。我提供了所有论据,所以它对我没有帮助。

This question the user is using the pasting operator when they don't need to. .在我的例子中,粘贴运算符是必需的。

最佳答案

嗯,这很愚蠢。发布这个以防有人遇到这个答案。

问题是我的 include guard。它应该是 FOO_BAR_H,因为文件的名称是 foo-bar.h。但是,它被定义为 FOO_BAR。当 gobject 生成 token “FOO_BAR”时,预处理器意识到它是一个已经定义的宏,但在这种情况下定义是空的。尽管如此,它还是用其定义替换了宏。

解决方案只是在包含防护中用“FOO_BAR_H”替换“FOO_BAR”。

关于c - 定义类时宏扩展中的 GObject 编译错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66212358/

相关文章:

c - 我怎么知道指针中有多少空闲位?

rust - 如何实现一个允许为字段赋值的过程宏?

c++ - 什么是 ?和 @ 符号在这个编译器错误中演示? - Visual Studio 2013 编译器

emacs - 为什么elisp宏返回的函数的应用不起作用?

c - 如何定义与结构数组 C 一起使用的宏

c - 当我使用malloc时,如何在C中获取* char的大小?

c - 请帮忙。生成随机数的程序不工作

c - Unix 系统调用名称后面的 (2) 是什么意思?

Java 9 错误 : not in a module on the module source path

c++ - 数组大小 'exceeded' ,但数组很小