是否可以将具有不同签名的2个函数作为参数传递给另一个函数?

时间:2014-10-17 06:23:32

标签: c function-pointers

目前,我有以下两个功能:

void write_to_file(FILE *fp)
{
    fprintf(fp, "stuff here: %d", 10);
    /* 1000s of similar lines below */
}

void write_to_string(char *str)
{
    sprintf(str, "stuff here: %d", 10);
    /* 1000s of similar lines below */
}

我希望将其变形为单个功能。 我想过像这样的事情:

void write_somewhere(void *ptr, int to_file)
{
    if (to_file) {
        typedef fprintf myprintf;
    } else {
        typedef sprintf myprintf;
    }
    myprintf(ptr, "stuff here: %d", 10);
}

这不起作用,看起来很难看。

由于fprintfsprintf的签名不同,如下所示,

int fprintf(FILE *stream, const char *format, …);
int sprintf(char *buffer, const char *format, …);

是否可以做类似的事情,

void write_somewhere(void *ptr, void *func)
{
    func(ptr, "stuff here: %d", 10);
}

修改 根据下面的Alter的回答,这就是我所拥有的,但它并没有按预期工作,并且在尝试打印write_somewhere()函数中的值时打印出垃圾值:

#include <stdio.h>
#include <stdarg.h>

typedef int (*myprintf_t) (void *, const char *, ...);

int myfprintf(void *ptr, const char *format, ...)
{
    va_list args;
    int ret;

    va_start(args, format);
    ret = vfprintf(ptr, format, args);
    va_end(args);
    return ret;
}

int mysprintf(void *ptr, const char *format, ...)
{
    va_list args;
    int ret;

    va_start(args, format);
    ret = vsprintf(ptr, format, args);
    va_end(args);
    return ret;
}

void write_somewhere(void *ptr, myprintf_t myprintf, const char *format, ...)
{
    va_list args;
    int ret;

    va_start(args, format);
    ret = myprintf(ptr, format, args);
    va_end(args);
    return ret;
}

int main(void)
{
    char s[100];
    int i = 100;

    /* This works */
    write_somewhere(stdout, myprintf, "Hello world");

    /* This prints out garbage */
    write_somewhere(stdout, myprintf, "Hello world, I am %d", i);
    write_somewhere(s, mysprintf);
    return 0;
}

6 个答案:

答案 0 :(得分:9)

Jen的回答是正确的,但在这种情况下,您可以使用指向函数的指针将ptr重定向到v*printf

#include <stdio.h>
#include <stdarg.h>

int myfprintf(void *ptr, const char *format, ...)
{
    va_list args;
    int ret;

    va_start(args, format);
    ret = vfprintf(ptr, format, args);
    va_end(args);
    return ret;
}

int mysprintf(void *ptr, const char *format, ...)
{
    va_list args;
    int ret;

    va_start(args, format);
    ret = vsprintf(ptr, format, args);
    va_end(args);
    return ret;
}

void write_somewhere(void *ptr, int (*myprintf)(void *, const char *, ...))
{
    myprintf(ptr, "stuff here");
}

int main(void)
{
    char s[100];

    write_somewhere(stdout, myfprintf);
    write_somewhere(s, mysprintf);
    return 0;
}

上次修改

您似乎想要将一些额外参数传递给write_somewhere,在这种情况下我建议:

#include <stdio.h>
#include <stdarg.h>

#define TO_FILE 0
#define TO_STRING 1

void write_somewhere(int where, void *ptr, const char *format, ...)
{
    #define myprintf(ptr, ...) \
    (where == TO_FILE ? vfprintf(ptr, __VA_ARGS__) : vsprintf(ptr, __VA_ARGS__))

    va_list args;
    va_start(args, format);
    myprintf(ptr, format, args);
    /* more stuff */
    va_end(args);
    #undef myprintf
}

int main(void)
{
    char s[100];

    write_somewhere(TO_FILE, stdout, "%u\n", 10);
    write_somewhere(TO_STRING, s, "Hello");
    printf("%s\n", s);
    return 0;
}

答案 1 :(得分:7)

C语言保证所有函数指针具有相同的表示。你的多态函数只需要原型化为接受任何函数指针,比如void (*funcptr)(void)。请注意,ptr-to-void 是一个函数指针(它是一个对象指针),并且可能无法保存函数指针。

当然,如果您知道它是哪种类型,您只能调用该函数。所以你需要某种方式来区别对待,就像printf通过查看格式一样。如果调用的参数与其原型不匹配,则行为未定义。

答案 2 :(得分:3)

不是您确切问题的答案,但不是像您一样编写write_something(),而是可以稍微更改结构:


void write_somewhere(void *ptr, int to_file)
{
    if (to_file) {
        fprintf( (FILE*) ptr, "stuff here");
    } else {
        sprintf( (char*) ptr, "stuff here");
    }
}

但是,要严格回答你的问题...... 如您所见,您尝试过的typedef行不起作用。 typedef是编译时操作,而不是运行时操作。

但是,您可以做的是为函数指针定义一个匹配fprintf()sprintf()函数的类型:


typedef int (*someprintf_ptr)(FILE *stream, const char *format, …);

write_somewhere()看起来像是:


void write_somewhere(void *ptr, someprintf_ptr func)
{
    func(ptr, "stuff here");
}

/* with calls looking like... */
write_somewhere( (void *)a_file_ptr, (someprintf_ptr)(fprintf));

答案 3 :(得分:2)

你的write_something函数必须是:

void write_something(void (*function)(),  int to_file) 
{
   ....
}

答案 4 :(得分:1)

尝试将myprintf设为函数

void write_somewhere(void *ptr, int to_file)
{
    myprintf(to_file, ptr, "stuff here");
    // do stuff
    /* 1000s of similar lines below */
}

void myprintf( int to_file, void *ptr, char *output )
{
    if (to_file)
        fprintf( ptr, output );
    else
        sprintf( ptr, output );
}

答案 5 :(得分:1)

让我试试:

struct target {
    int (*tgtfunction)();
    void* ptr;
}

struct target mktarget_file(FILE * fp) {
    struct target tgt = { .tgtfuntion = vfprintf, .ptr = fp };
    return tgt;
}

struct target mktarget_string(char * str) {
    struct target tgt = { .tgtfuntion = vsprintf; .ptr = str };
    return tgt;
}

void tgtprintf(struct target * target, char * fmt, ...) {
    va_list ap;
    va_start(ap, target);
    int ret = target.tgtfunction(target.ptr, fmt, ap);
    va_end(ap);
    return ret;
}

void write_stuff(struct target * target)
{
    tgtprintf(target, "stuff here");
    /* 1000s of similar lines below */
}

应该做你想做的事:为你想要的目标创建一个struct target,然后调用write_stuff来写你的东西。

请注意sprintf内容可能需要改进,因为每个字符串都写入相同的位置而不是追加,并且没有检查可用空间。但一般概念可以这样开始。