Можно ли передать 2 функции, которые имеют различную сигнатуру в качестве аргумента другой функции?
В настоящее время у меня есть следующие 2 функции:
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);
}
Это не работает и выглядит уродливо.
Поскольку сигнатура fprintf
и sprintf
различна и следующим образом:
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);
}
EDIT:
Основываясь на ответе 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;
}
Ответы
Ответ 1
Ответ 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;
}
Ответ 2
Язык C гарантирует, что все указатели функций имеют одинаковое представление. Ваши полиморфные функции просто должны быть прототипированы как принятие любого указателя функции, скажем, void (*funcptr)(void)
. Обратите внимание, что ptr-to-void не является указателем на функцию (это указатель объекта) и может не иметь возможности удерживать указатель на функцию.
Конечно, вы можете только вызвать функцию, если знаете, какой из нескольких типов она есть. Таким образом, вам нужно каким-то образом различать, как и printf, глядя на формат. Если вы вызываете функцию с аргументами, не соответствующими ее прототипу, поведение undefined.
Ответ 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));
Ответ 4
Ваша функция write_something должна выглядеть примерно так:
void write_something(void (*function)(), int to_file)
{
....
}
Ответ 5
Попробуйте сделать 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 );
}
Ответ 6
Позвольте мне попробовать:
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 может понадобиться уточнить, так как каждая строка записывается в одно и то же место вместо добавленного, и нет проверки свободного места. Но общая концепция может начаться именно так.