【问题标题】:Storing a global struct variable inside another global struct in C将全局结构变量存储在 C 中的另一个全局结构中
【发布时间】:2010-02-06 04:13:09
【问题描述】:

我正在尝试找出一种方法,将嵌套的全局结构用作我的 C 库的一种 API 命名空间。

具体来说,我想公开一个 Primary“命名空间结构”,其中包含其他此类结构(例如 Primary.Secondary),这些结构本身包含函数指针 (Primary.Secondary.a_function())。

我已经抽象出以下(相对)简单的示例来说明我想做的事情:

main.c:

#include "Primary.h"

int main () {
  Primary.Secondary.a_function();
  return 0;
}

Primary.h:

#if !defined(SECONDARY_H)
# include "Secondary.h"
#endif

struct Primary_struct {
  struct Primary__Secondary_struct  Secondary;
} extern Primary;

Primary.c:

#include "Primary.h"

struct Primary_struct Primary = {
  .Secondary = Primary__Secondary
};

Secondary.h:

struct Primary__Secondary_struct {
  void  (*a_function) (void);
  void  (*another_function) (void);
} extern Primary__Secondary;

Secondary.c:

#include "Secondary.h"

#include <stdio.h>

void  Primary__Secondary__a_function  (void);
void  Primary__Secondary__another_function  (void);

struct Primary__Secondary_struct {
  .a_function       = Primary__Secondary__a_function,
  .another_function = Primary__Secondary__another_function
} extern Primary__Secondary;

void Primary__Secondary__a_function(void) {
  Primary.Secondary.another_function();
}

void Primary__Secondary__another_function(void) {
  printf("run!\n");
}

当我尝试编译它时,我遇到了以下编译器错误:

 > C -O0 Primary.c Secondary.c main.c
Primary.c:3:33: error: initializer element is not a compile-time constant
struct Primary_struct Primary = {
                                ^
1 diagnostic generated.

我应该注意,理想情况下,PrimaryPrimary__Secondary 变量都是 const。我担心增加的复杂性会加剧问题......所以现在,我已经把这方面排除在外了。

问题似乎是,出于某种原因,即使设置为const,并且仅包含编译时存在的元素,Primary__Secondary 结构也不是编译时常量,因此不能存储在编译时的另一个结构。我可能可以通过在运行时设置所有接口来解决这个问题,但是……这似乎是一个非常老套的解决方案。我正在寻找这个问题的任何替代解决方案,你的 C-fu 比我能想出的多。

注意:这与this question 相关,但有很大不同,而且更加具体。)

【问题讨论】:

  • 最好利用您的时间来使用与 C 保持高度兼容性并为您处理所有这些细节的不同语言。如果做不到这一点,听起来你想创建自己的 C 方言,并且可以编写一个简单的解析器来进行最小的转换。
  • 你试图做的事情已经被 C++ / Objective C 解决了 ...
  • 为什么除了命名函数PrimarySecondaryAFunction()PrimarySecondaryAnotherFunction()之外,它还能保护你的命名空间?
  • Roger Pate:这可能最终成为我的最终路径;不过,目前我项目的一个设计目标是“在保持高质量 API 的同时使用 C。”因此,退出 C 并不是一个直接的选择。 Francis:是的,而且我对 Objective-C 非常熟悉。但是,它不适合这个项目。

标签: c struct constants global-variables


【解决方案1】:

您正在尝试的事情无法完成;对不起。这是一个精简的例子:

#include <stdio.h>

int a = 5;
int b = a;

int main(int argc, char *argv[])
{
  printf("Hello, world!\n"); 
  return 0;
}

编译这段代码会报错:

main.c:4: error: initializer element is not constant

因为编译器在编译时不知道如何赋值int b = a。这就是语言的工作方式!

【讨论】:

  • 认为我掌握了这背后的问题(这听起来像是讽刺。事实并非如此。)......然而,这个问题的中心是如何规避这个问题,同时仍然实现相同的目标。
  • 在运行时执行,或者不要打扰。多年来(几十年!),C 程序员一直在使用函数名前缀来解决命名空间问题,这对你来说应该没问题。
  • 标准的第 6.6 节可能对你有用,它解释了所有关于常量表达式的内容。
【解决方案2】:

您的代码中有一些奇怪的符号 - 我已将它们转换为更正统的形式。此外,作为一般规则,避免在名称中使用双下划线;在 C++ 中,这是绝对必要的。 您还需要使用指向嵌入式结构的指针 - 然后代码将运行:

Primary.h

//Primary.h:
#ifndef PRIMARY_H
#define PRIMARY_H

#include "Secondary.h"

struct Primary_struct {
  struct Primary_Secondary_struct *Secondary;
};

extern struct Primary_struct Primary;

#endif // PRIMARY_H

Secondary.h

//Secondary.h:
#ifndef SECONDARY_H
#define SECONDARY_H

struct Primary_Secondary_struct {
  void  (*a_function)(void);
  void  (*another_function)(void);
};

extern struct Primary_Secondary_struct Primary_Secondary;

#endif // SECONDARY_H

Primary.c

//Primary.c:

#include "Primary.h"

struct Primary_struct Primary = {
  .Secondary = &Primary_Secondary
};

Secondary.c

//Secondary.c:

#include "Secondary.h"
#include "Primary.h"
#include <stdio.h>

void Primary_Secondary_a_function(void);
void Primary_Secondary_another_function(void);

struct Primary_Secondary_struct Primary_Secondary = {
  .a_function       = Primary_Secondary_a_function,
  .another_function = Primary_Secondary_another_function
};

void Primary_Secondary_a_function(void) {
  Primary_Secondary.another_function();
  printf("hide!\n");
}

void Primary_Secondary_another_function(void) {
  printf("run!\n");
}

main.c

//main.c:
#include "Primary.h"

int main () {
  Primary.Secondary->a_function();
  return 0;
}

这会生成:

run!
hide!

【讨论】:

  • 谢谢!是的,请原谅奇怪的符号……我显然是这里的新手 d-;所以,至于解决方案……我没有想到这个。我真的希望坚持使用非指针,这样我就可以使用Foo.Bar.gaz() 样式......但我想Foo-&gt;Bar-&gt;gaz()(如果我在一个地方使用指针,我可能会将它们全部设为指针,用于 API一致性)真的不是那么糟糕。与此同时,我自己设计了另一种解决方案,虽然它需要运行时操作,这可能是一个坏事™……我将在下面发布。
  • 我还应该补充一点,真诚地感谢您花时间提供建设性的答复,而不是告诉我我不能/不应该做我想做的事。感谢您的宝贵时间 (-:
【解决方案3】:

我最终选择了运行时方法,至少现在是这样。稍后我可能会尝试一种指针方法(上面的 Jonathan Leffler 建议),看看我最终是否会得到一个不那么复杂/更易于理解的代码库……但现在这可行。

我使用clang(和gcc)的__attribute__((constructor))扩展在运行时设置结构的关系;使用main() 中的一些代码可以更便携(但不太干净)实现相同的目的。

我会提供更多解释,但现在是凌晨 4 点……呵呵。我花了一整天的时间>,

main.c

#include "Package.h"

int main () {
  Package.One.a_function();
  Package.One.another_function();

  Package.Two.a_function();
  Package.Two.another_function();

  return 0;
}

Package.h

#define PACKAGE_H

#if !defined(ONE_H)
# include "One.h"
#endif
#if !defined(TWO_H)
# include "Two.h"
#endif

// It seems this is broken, at least in `clang`
// #if __has_feature(attribute_constructor)
# define constructor __attribute__((constructor))
// #endif

struct Package_struct {
  struct Package__One_struct  One;
  struct Package__Two_struct  Two;
};

struct Package_struct extern Package;

Package.c

#include "Package.h"

struct Package_struct Package = {};

One.h

#define ONE_H

struct Package__One_struct {
  void  (*a_function)       (void);
  void  (*another_function) (void);
};

struct Package__One_struct extern Package__One;

One.c

#include "One.h"
#include "Package.h"

#include <stdio.h>

void  Package__One__a_function        (void);
void  Package__One__another_function  (void);

struct Package__One_struct Package__One = {
  .a_function       = Package__One__a_function,
  .another_function = Package__One__another_function
};
void constructor Package__register_One(void) {
  Package.One = Package__One; }

void Package__One__a_function(void) {

  Package.One.another_function();

}

void Package__One__another_function(void) {

  printf("one!\n");

}

Two.h

#define TWO_H

struct Package__Two_struct {
  void  (*a_function)       (void);
  void  (*another_function) (void);
};

struct Package__Two_struct extern Package__Two;

Two.c

#include "Two.h"
#include "Package.h"

#include <stdio.h>

void  Package__Two__a_function        (void);
void  Package__Two__another_function  (void);

struct Package__Two_struct Package__Two = {
  .a_function       = Package__Two__a_function,
  .another_function = Package__Two__another_function
};
void constructor Package__register_Two(void) {
  Package.Two = Package__Two; }

void Package__Two__a_function(void) {

  Package.Two.another_function();

}

void Package__Two__another_function(void) {

  printf("two!\n");

}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多