【问题标题】:How can I make a function return its own type in C?如何让函数在 C 中返回自己的类型?
【发布时间】:2021-08-23 03:15:09
【问题描述】:

TL;DR 如果x = x(); 在 C 中有效,x 的类型应该是什么?


整个故事:

我正在开发一个包含多个场景的简单游戏。起初我的代码是这样的:

enum {kSCENE_A, kSCENE_B} ecene = kSCENE_A;
int main() {
    while(1) {
        switch(scene) {
          case kSCENE_A:
            // Renders scene a
            // And possibly modifies `scene`
            break;
          case kSCENE_B:
            // Renders scene b
            // And possibly modifies `scene`
            break;
        }
    }
}

但是main 函数过于冗长。我将渲染部分提取到不同的函数中,但switch 仍然使代码难看。我为此定义了一个scene_map

typedef enum {
    kSCENE_A,
    kSCENE_B,

    kN_SCENES
} Scene;
Scene RenderSceneA();
Scene RenderSceneB();
int main() {
    Scene scene = kSCENE_A;
    Scene (*scene_map[kN_SCENES])();
    scene_map[kSCENE_A] = SceneA;
    scene_map[kSCENE_B] = SceneB;
    while(1) scene = scene_map[scene]();
}

但我想知道在 C 中是否可以用这种方式或类似的方式编写代码:

SomeType RenderSceneA();
SomeType RenderSceneB();
int main() {
    SomeType scene = RenderSceneA;
    while(1) scene = scene();
}

那么 SomeType 应该是什么类型,或者我只能使用void * 吗?如果后者是真的,我怎样才能写出比第二个代码块更清晰的代码?

【问题讨论】:

  • AFAICR,你不能在(标准 C)中这样做。我似乎记得在 90 年代曾在这个问题上花费时间,但从那以后没有任何重大变化会影响这一点。 x 必须是函数指针;它必须是指向函数的指针,该函数返回指向函数的指针。这很快就会变得很棘手——请参阅signal() 以获取此类函数的示例。请注意,signal() 不会返回指向其自身类型的指针。
  • @JonathanLeffler 我似乎记得早在 90 年代就在这个问题上花费了时间,但从那时起没有任何重大变化会影响 C++ 成功的原因之一。顺便说一句,我并不真的对多态性过敏
  • @0___________ 如果这在 C++ 中是可能的,那么代码应该如何?我学到了一些关于std::function 的知识,但它似乎对此没有帮助。我试过using SomeType = std::function<SomeType()>;,但它当然不能编译。

标签: c function types


【解决方案1】:

这是一个可以解决问题的解决方案。请注意,它使用void (*)(void) 作为通用函数指针类型,而不是void *,它不能保证对函数指针起作用:

#include <stdio.h>

void (*f(void))(void);
void (*g(void))(void);

void (*f(void))(void)
{
    printf("This is f.\n");
    return (void (*)(void)) g;
}

void (*g(void))(void)
{
    printf("This is g.\n");
    return (void (*)(void)) f;
}

#define SRCALL(p)   ((void (*(*)(void))(void)) p())

int main(void)
{
    void (*(*p)(void))(void);

    p = f;

    p = SRCALL(p);
    p = SRCALL(p);
    p = SRCALL(p);

    return 0;
}

函数指针转换很难看,所以我将它们封装在宏SRCALL(self-ref-call)中。输出是:

This is f.
This is g.
This is f.

【讨论】:

  • 虽然void * 的使用仍然存在,但SRCALL 让它看起来好多了。谢谢。
猜你喜欢
  • 1970-01-01
  • 2019-05-04
  • 1970-01-01
  • 2020-02-28
  • 1970-01-01
  • 2016-01-27
  • 1970-01-01
  • 2019-10-29
  • 1970-01-01
相关资源
最近更新 更多