C中struct的函数指针

问题描述 投票:0回答:2

我试图使用struct在C中实现类似于类的想法。我的测试代码:

#include <stdio.h>

typedef struct test_struct
{
    int data;
    void(*func)(struct test_struct *);
} test_t;

void inc_data(test_t * this)
{
    this->data++;
}

int main(void)
{
    test_t test = { 0, inc_data };

    test.func(&test);

    printf("%d\r\n", test.data);
}

它正在工作,但我还需要1件事。我想函数func不需要struct test_struct *作为参数,只需调用test.func()

我该怎么做?

我认为我们可以使用#definetest.func(&test)替换为test.FUNC(),但我不知道该怎么做。

还有另一个问题this pointer in C, not C++读取类似,但它实际上是不同的。我知道C中没有this指针。我的问题是:是否有任何“技巧”可以做到这一点?喜欢使用#define或其他东西。使用全局变量不是一个好的答案,因为我的程序中会有很多对象。

c c-preprocessor function-pointers
2个回答
2
投票

正如评论中所述,当问题被标记为重复时,您可以使用预处理器并且非常接近您想要的内容。它不完全是您指定的符号,但C不是C ++。

您可以使用宏来解决问题:

#define SCALL0(s, m)        ((s).m(&(s)))
#define SCALLn(s, m, ...)   ((s).m(&(s), __VA_ARGS__))
#define PCALL0(p, m)        ((p)->m(p))
#define PCALLn(p, m, ...)   ((p)->m((p), __VA_ARGS__))

在所有这些宏中,m参数是要调用的“成员函数”。当s参数是实际结构时,S-宏是用于的;当p参数是指向结构的指针时,P宏是用于的。 0-宏用于当成员函数只接受this指针时;当成员函数接受一个或多个其他参数时,n-macros用于。你可能会写,例如:

SCALL0(test, func);

通过将address-of运算符应用于实际结构,您可以只使用两组宏中的一组,例如:

PCALL0(&test, func);

您可能愿意对variadic macros的标准行为使用特定于GCC的扩展,以避免需要0-vs-n区分:

#define PCALL(p, m, ...) ((p)->m((p), ## __VA_ARGS))

因此,如果您接受减少的可移植性并且需要将&应用于结构,并且如果您接受宏表示法来调用成员函数,则PCALL宏可能是您需要的唯一宏。它与C ++成员函数调用不同,但C不是C ++。

示例代码:

#include <stdio.h>

typedef struct test_struct
{
    int data;
    void (*func0)(struct test_struct *);
    void (*func1)(struct test_struct *, int);
} test_t;

#define SCALL0(s, m)        ((s).m(&(s)))
#define SCALLn(s, m, ...)   ((s).m(&(s), __VA_ARGS__))
#define PCALL0(p, m)        ((p)->m(p))
#define PCALLn(p, m, ...)   ((p)->m((p), __VA_ARGS__))
#define PCALL(p, m, ...)    ((p)->m((p), ## __VA_ARGS__))

static void inc_data(test_t *this) { this->data++; }
static void dbl_data(test_t *this) { this->data *= 2; }
static void add_data(test_t *this, int add) { this->data += add; }
static void mul_data(test_t *this, int mul) { this->data *= mul; }

int main(void)
{
    test_t test = { 0, inc_data, add_data };
    test_t *pt1 = &(test_t){ 100, inc_data, add_data };
    test_t *pt2 = &(test_t){ 10, dbl_data, mul_data };
    test_t *pt3 = &(test_t){ -27, dbl_data, add_data };

    printf("Test SCALL0, SCALLn on structure:\n");
    printf("%d\n", test.data);
    test.func0(&test);
    printf("%d\n", test.data);
    test.func1(&test, 7);
    printf("%d\n", test.data);
    SCALL0(test, func0);
    printf("%d\n", test.data);
    SCALLn(test, func1, 100);
    printf("%d\n", test.data);

    printf("Test PCALL0, PCALLn on pointer:\n");
    printf("%d\n", pt1->data);
    pt1->func0(pt1);
    printf("%d\n", pt1->data);
    pt1->func1(pt1, 22);
    printf("%d\n", pt1->data);
    PCALL0(pt1, func0);
    printf("%d\n", pt1->data);
    PCALLn(pt1, func1, test.data);
    printf("%d\n", pt1->data);

    printf("Test PCALL0, PCALLn on another pointer:\n");
    printf("%d\n", pt2->data);
    pt2->func0(pt2);
    printf("%d\n", pt2->data);
    pt2->func1(pt2, 22);
    printf("%d\n", pt2->data);
    PCALL0(pt2, func0);
    printf("%d\n", pt2->data);
    PCALLn(pt2, func1, pt1->data);
    printf("%d\n", pt2->data);

    printf("Test PCALL on structure:\n");
    printf("%d\n", test.data);
    test.func0(&test);
    printf("%d\n", test.data);
    test.func1(&test, 22);
    printf("%d\n", test.data);
    PCALL(&test, func0);
    printf("%d\n", test.data);
    PCALL(&test, func1, pt1->data);
    printf("%d\n", test.data);

    printf("Test PCALL on pointer:\n");
    printf("%d\n", pt3->data);
    pt3->func0(pt3);
    printf("%d\n", pt3->data);
    pt3->func1(pt3, pt1->data);
    printf("%d\n", pt3->data);
    PCALL(pt3, func0);
    printf("%d\n", pt3->data);
    PCALL(pt3, func1, pt2->data);
    printf("%d\n", pt3->data);

    return 0;
}

样本输出:

Test SCALL0, SCALLn on structure:
0
1
8
9
109
Test PCALL0, PCALLn on pointer:
100
101
123
124
233
Test PCALL0, PCALLn on another pointer:
10
20
440
880
205040
Test PCALL on structure:
109
110
132
133
366
Test PCALL on pointer:
-27
-54
179
358
205398

您可以创建并使用SCALL宏,但最好使用PCALL宏代替。


0
投票

在C ++中 - 你试图模仿 - 有一个隐含的this指针作为每个方法的参数。如果在结构中没有这样的类似指针,则在调用方法时必须访问全局数据,这是一个坏主意。

你的功能

void inc_data(test_t * this)
{
  this->data++;
}

有这个参数,但它依赖于你在调用函数时提供参数。如果你要删除它

void inc_data()
{...}

然后函数需要对全局数据进行操作,因为函数和结构的内容之间没有明确的关系。

我建议 - 正如其他人已经做过的尝试C ++而不是重新发明轮子。

© www.soinside.com 2019 - 2024. All rights reserved.