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

Calling a function by pointer chosen by ternary conditional

我有一个从文件中读取数据的函数和另一个将数据写入文件的函数。这些函数的相似之处仅在于被调用的函数,分别为 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);

那么,有没有好的办法解决呢?

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 之前)国际化和本地化)。您还应该检查读取或写入函数中的 return 值——但是短读取和短写入有不同的含义,这也使错误处理和报告变得复杂。我不确定您是否愿意长期使用它。

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

3 One of the following shall hold for the second and third operands:

— both operands are pointers to qualified or unqualified versions of compatible types;

函数 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);

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