通过三元条件选择的指针调用函数

Posted

技术标签:

【中文标题】通过三元条件选择的指针调用函数【英文标题】:Calling a function by pointer chosen by ternary conditional 【发布时间】:2017-09-25 21:28:30 【问题描述】:

我有一个从文件读取数据的函数和另一个将数据写入文件的函数。这些函数相似,不同之处仅在于被调用的函数分别为freadfwrite。所以我想把他们团结起来。但是,我不想写像if (read) fread(args); else fwrite(same args); 这样的东西。

所以一开始我尝试了这个:

enum  F_READ, F_WRITE ;
void rwFile(uint8_t rw, char *name);
...
rwFile(F_READ, name);
...
void rwFile(uint8_t rw, char *name)

    FILE *file = fopen(name, (rw == F_READ ? "rb" : "wb"));
    (rw == F_READ ? &fread : &fwrite) (&size, sizeof(size), 1, file);
    for (int i = 0; i < size.Y; i++)
        for (int j = 0; j < size.X; j++)
            (rw == F_READ ? fread : fwrite ) (data[i] + j*2, 2, 1, file);
    fclose(file);
    file = NULL;

然后我得到:

main.c: In function ‘rwFile’:
main.c:199:26: warning: pointer type mismatch in conditional expression
  (rw == F_READ ? &fread : &fwrite) (&size, sizeof(size), 1, file);
                         ^
main.c:199:26: error: called object is not a function or function pointer
  (rw == F_READ ? &fread : &fwrite) (&size, sizeof(size), 1, file);
  ~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~
main.c:202:27: warning: pointer type mismatch in conditional expression
    (rw == F_READ ? fread : fwrite ) (data[i] + j*2, 2, 1, file);
                          ^
main.c:202:27: error: called object is not a function or function pointer
    (rw == F_READ ? fread : fwrite ) (data[i] + j*2, 2, 1, file);
    ~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~

好的,让我们尝试另一种变体:

typedef size_t (*rwFunc_t)(const void *ptr, size_t size, size_t count, FILE *stream);
void rwFile(rwFunc_t rwFunc, char *name);
...
rwFile(fread, name);
...
void rwFile(rwFunc_t rwFunc, char *name)

    FILE *file = fopen(name, (rwFunc == fread ? "rb" : "wb"));
    (*rwFunc) (&size, sizeof(size), 1, file);
    for (int i = 0; i < size.Y; i++)
        for (int j = 0; j < size.X; j++)
            (*rwFunc) (data[i] + j*2, 2, 1, file);
    fclose(file);
    file = NULL;

这很有效。但是,我从 gcc 收到了一些警告:

main.c: In function ‘main’:
main.c:58:10: warning: passing argument 1 of ‘rwFile’ from incompatible pointer type [-Wincompatible-pointer-types]
   rwFile(fread, argv[1]);
          ^~~~~
main.c:18:6: note: expected ‘rwFunc_t aka long unsigned int (*)(const void *, long unsigned int,  long unsigned int,  struct _IO_FILE *)’ but argument is of type ‘size_t (*)(void * restrict,  size_t,  size_t,  FILE * restrict) aka long unsigned int (*)(void * restrict,  long unsigned int,  long unsigned int,  struct _IO_FILE * restrict)’
 void rwFile(rwFunc_t rwFunc, char *name);
      ^~~~~~
main.c: In function ‘rwFile’:
main.c:198:35: warning: comparison of distinct pointer types lacks a cast
  FILE *file = fopen(name, (rwFunc == fread ? "rb" : "wb"));
                                   ^~

这是因为freadfwrite 的参数有点不同:

size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);

那么,能不能很好的解决呢?

【问题讨论】:

没有“好”的方式。以ifelse 的方式进行操作。它可读且干净。 唯一的其他选择是为freadfwrite 编写具有兼容签名的包装函数(基本上只需要一个fwrite 的包装就足够了)。但在我看来,这仍然像是自找麻烦。 没有人愿意输入两次the_same_arguments。这就是优秀的文本编辑器的用武之地。:) What is causing "warning: pointer/integer type mismatch in conditional expression"?的可能重复 【参考方案1】:

条件运算符有以下限制(6.5.15 条件运算符)

3 对于第二个和第三个操作数应满足以下条件之一:

——两个操作数都是指向合格或不合格版本的指针 兼容类型;

函数freadfwrite 具有以下声明

size_t fread(void * restrict ptr, size_t size, size_t nmemb, FILE * restrict stream);
size_t fwrite(const void * restrict ptr, size_t size, size_t nmemb, FILE * restrict stream);

可以看出,它们的声明相对于第一个参数有所不同。这些参数具有不兼容的类型,因为它们的限定不同。因此,这些函数也不兼容,可能无法在条件运算符中使用。

【讨论】:

【参考方案2】:

fread() 的类型和 fwrite() 不同:

size_t fread(void *restrict ptr, size_t size, size_t nitems, FILE *restrict stream);

size_t fwrite(const void *restrict ptr, size_t size, size_t nitems, FILE *restrict stream);

请注意,fwrite 的第一个参数中的 constfread 中缺失。

您必须克服这种差异。也许将 I/O 函数指针传递给你的函数会更干净,但你可能仍然需要使用:

size_t fwrite_alt(void *restrict ptr, size_t size, size_t nitems, FILE *restrict stream)

    return fwrite(ptr, size, nitems, stream);

fwrite_alt()的接口与fread()的接口完全一致。

顺便提一下,如果您需要生成错误消息,您可能需要更多关于使用了哪个函数的信息——以及在错误消息中使用哪些词(这是在你担心 I18N/L10N 之前——国际化和本地化) .您还应该检查 read 或 write 函数的返回值——但是短读和短写有不同的含义,这也使错误处理和报告变得复杂。我不确定您是否会长期使用它。

【讨论】:

以上是关于通过三元条件选择的指针调用函数的主要内容,如果未能解决你的问题,请参考以下文章

C++ 选择题总结(回调函数 || 类方法(实例方法)|| )

AngularJS:三元运算符条件检查中的函数调用

如何在 JSX 的三元运算符中调用两个函数?

三元表达式递归匿名与内置函数

Golang回调函数实例二则

Javascript-回调函数浅谈