Reputation: 2318
Currently, I have the following 2 functions:
void write_to_file(FILE *fp)
{
fprintf(fp, "stuff here: %d", 10);
/* 1000s of similar lines below */
}
and
void write_to_string(char *str)
{
sprintf(str, "stuff here: %d", 10);
/* 1000s of similar lines below */
}
I'd like to poly morph it into a single function. I'd thought about something like:
void write_somewhere(void *ptr, int to_file)
{
if (to_file) {
typedef fprintf myprintf;
} else {
typedef sprintf myprintf;
}
myprintf(ptr, "stuff here: %d", 10);
}
This doesn't work and looks ugly.
Since the signature of fprintf
and sprintf
are different and as follows,
int fprintf(FILE *stream, const char *format, …);
int sprintf(char *buffer, const char *format, …);
Is it possible to do something like,
void write_somewhere(void *ptr, void *func)
{
func(ptr, "stuff here: %d", 10);
}
EDIT: Based on Alter's answer below, this is what I have but it doesn't quite work as expected and prints out garbage value when trying to print out values in write_somewhere() function:
#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;
}
Upvotes: 11
Views: 1008
Reputation: 41017
Jen‘s answer is the correct one, but in this case you can redirect ptr
to v*printf
using a pointer to function:
#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;
}
For your last edit:
It seems that you want to pass some extras parameters to write_somewhere
, in this case I suggest:
#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;
}
Upvotes: 9
Reputation: 72667
The C language guarantees that all function pointers have the same representation. Your polymorphic functions simply needs to be prototyped as accepting any function pointer, say, a void (*funcptr)(void)
. Note that a ptr-to-void is not a function pointer (it's an object pointer) and may not be able to hold a function pointer.
Of course you can only call the function if you know which of the several types it is. So you need some way to discriminate, much like printf does by looking at the format. If you call a function with arguments not matching its prototype, the behavior is undefined.
Upvotes: 7
Reputation: 91059
Let me try:
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 */
}
should do what you want: create a struct target
for your wanted target, and just call write_stuff to write your stuff there.
Be aware that the sprintf stuff might need to be refined, as each string is written to the same place instead of appended, and there is no check for free space. But the general concept could start like this.
Upvotes: 1
Reputation: 40372
Not an answer to your exact question, but instead of writing write_something()
as you have, you could change the structure slightly:
void write_somewhere(void *ptr, int to_file)
{
if (to_file) {
fprintf( (FILE*) ptr, "stuff here");
} else {
sprintf( (char*) ptr, "stuff here");
}
}
However, for a strict answer to your question...
As you've found, the typedef line that you've attempted doesn't work. typedef
is a compile time operation, not a runtime operation.
What you could do, though, is to define a type for a function pointer that matches both the fprintf()
and sprintf()
functions:
typedef int (*someprintf_ptr)(FILE *stream, const char *format, …);
The write_somewhere()
would then look like:
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));
Upvotes: 3
Reputation: 158
Try making myprintf a function
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 );
}
Upvotes: 1
Reputation: 619
Your write_something function would have to be something like:
void write_something(void (*function)(), int to_file)
{
....
}
Upvotes: 2