C结构体-函数指针-作为参数传递自身引用

4
我希望能够运行由我的结构体指向的具有自动填充功能的函数。
这是我正在处理的部分:
#include <stdio.h>
#include <stdlib.h>

struct Fra { //Fraction
  int n;     //Numerator
  int d;     //Denominator
  void (*p)(struct Fra*);
  void (*sD)(int, struct Fra*);
  void (*sN)(int, struct Fra*);
};

void print(struct Fra*);
void setDenom(int, struct Fra*);
void setNum(int, struct Fra*);

int main() {

  struct Fra* fraA = 0;

  fraA = (struct Fra*) malloc(sizeof(struct Fra));

  fraA->sN = setNum;
  fraA->sN(2, fraA);
  fraA->sD = setDenom;
  fraA->sD(3, fraA);
  fraA->p = print;
  fraA->p(fraA);

  return 0;
}

我一直在努力实现这个目标

来自:

fraA->sN(2, fraA);
fraA->sD(3, fraA);
fraA->p(fraA);

致:

fraA->sN(2);
fraA->sD(3);
fraA->p();

在进行了一些试错后,我得出结论需要在这方面寻求帮助。我尝试过浏览,但似乎没有使用正确的关键字,因此无法确定这个问题是否重复。

非常感谢您的帮助。


9
在C语言中,简单而唯一的答案是这是不可能实现的。 - Some programmer dude
那个声明很难接受,但还是谢谢你让我知道了。我想我不能绕过这个特定的难题。 - user3163916
C++有类,使您所需的语法变得容易实现。此外,所有类都只是带有函数的C结构体的语法糖,这些函数需要指向它的指针。也就是说,使用类,fraA->print(fraA) 变成了 fraA->print()。我很快会发布一个比较。 - Braden Best
这里是程序相关内容的翻译:在这里!希望你会觉得它很有启发性。 - Braden Best
3个回答

2
您可以声明一些宏来确保始终传递正确的引用,这就是您能做的所有事情:
#define FRA_NEW(this, sN sD, sP) \
{ \
  (this) = calloc(sizeof(*(this))) \
  if (this) \
  { \
    (this)->sN = (sN); \
    (this)->sN = (sD); \
    (this)->sN = (sP); \
  } \
}

#define FR_DELETE(this) \
  free(this)

#define FRA_PRINT(this) \
  (this)->print(this)

#define FRA_SETNUM(this, num) \
  (this)->setNum(this, num)

#define FRA_SETDENOM(this, denom) \
  (this)->setDenom(this, denom)

我建议将“this”作为“成员函数”的第一个参数。

int main(void) 
{
  struct Fra * fraA = NULL;

  FRA_NEW(fraA, setNum, setDenom, print);
  if (NULL == fraA)
  {
    perror("FRA_NEW() failed");
    return 1;
  }

  FRA_SETNUM(fraA, 2);
  FRA_SETDENOM(fraA, 3);
  FRA_PRINT(fraA);

  FRA_DELETE(fraA);

  return 0;
}

感谢您对宏命名的建议。起初,我将“this”作为第一个参数放置,但由于我考虑到要初始化默认值,所以我将“this”作为最后一个参数设置了,哈哈。但是,后来我意识到在C语言中,我试图做的事情是不可能实现的。因此,我又回到了最初的结构,将“this”作为第一个参数。好的,再次感谢你,alk。 - user3163916
1
“FRA_PRINT(fraA)”不就是“print(fraA)”吗?只不过多了些工作吗? - Braden Best
@B1KMusic:只有当struct Fra的成员print确实被初始化为(并且在运行时没有更改)指向print()时,才会这样。 - alk
那么,在结构体中拥有一个函数指针就没有意义了,对吗?更好的选择是将函数“命名空间化”(使用fra_<function_name>(){...}而不是<function_name>(){...}),或者只需使用C++的class数据类型,该数据类型专门为此目的设计了一个self关键字。或者我漏掉了什么? - Braden Best
如果您想要创建一种通用数据类型(在初始化时进行配置)或在运行时更改函数指针的值,则这不是毫无意义的。 @B1KMusic - alk
显示剩余2条评论

1

我能想到的唯一有用的方法就是提供执行任务的函数:

void call_print(struct Fra* fra)
{
    fra->p(fra);
}

void call_setDenom(int val, struct Fra* fra)
{
    fra->sD(val, fra);
}

void call_setNum(int val, struct Fra* fra);
{
    fra->sN(val, fra);
}

并使用这些:

call_setNum(2, fraA);
call_setDenom(3, fraA);
call_print(fraA);

2
call_print(fraA)和简单的print(fraA)有什么不同?所有额外的努力都是为了做一些完全无用的事情。 - Braden Best
如果您不知道(或希望保持灵活性)fra->p指向哪里,那么采用这种方式有助于封装两次访问fra的需要。 - glglgl
是的,当你在制作一个库(或者像dwm这样的配置文件)时,能够在运行时设置一个函数是有用的。但在这种情况下,它是没有用的。试图将C用作OOP是无用的。询问者的问题可以很容易地通过使用C++类来解决,在其中实例具有内置的指向自身的指针,且您可以获得所需的语法糖,如 fraA->print() - Braden Best
1
试图将C语言用作面向对象编程是没有用的。我不同意。这绝对不是无用的,而是一种有效的方法。 - glglgl
上下文、约定和情况。函数指针并不是无用的,但 fraA->print (fraA) 是。这是不必要的数据复制,而 print(fraA) 可以同样地达到目的。或者更好地说,如果你需要类,那就使用类。 - Braden Best

0
有一个对我有效的例子:
typedef struct _log_t
{
    log_level_t threshold;
    void (*logger)(struct _log_t*, log_level_t, char *, ...);
} 
log_t;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接