Reputation:
So i have 4 variables that i need to check in order for my program to work, it gets to 16 test expressions in one if statement if hard coded. How could i minimize it ? The only way i thought of going was boolean algebra and i was very wrong. Anyone have any suggestions or something i would appreciate. The function is something like a printf function.
string = form("%d %s %f %lf %c", 1, "ASA", 4.12345, 1.123421, 'A');
char * form(char * format, ...){
char * result = (char *)calloc(1, 1), val_int[12], * val_str, * pos_int, * pos_str, val_double[12], * pos_double, * pos_char, * pos_float, val_float[12];
va_list arguments; va_start(arguments, format);
do
{
pos_int = strstr(format, "%d"); pos_str = strstr(format, "%s"); pos_double = strstr(format, "%lf"); pos_float = strstr(format, "%f"); pos_char = strstr(format, "%c");
if(pos_int && (pos_int < pos_str && pos_int < pos_float && pos_int < pos_double && pos_int < pos_char))
{
sprintf(val_int, "%d", va_arg(arguments, int));
result = (char *)realloc(result, strlen(val_int) + strlen(result) + (pos_int - format) + 1);
strncat(result, format, pos_int - format);
strcat(result, val_int);
format = pos_int + 2;
}
else if(pos_str && ( pos_str < pos_int && pos_str < pos_float && pos_str < pos_double && pos_str < pos_char))
{
val_str = va_arg(arguments, char *);
result = (char *)realloc(result, strlen(val_str) + strlen(result) + (pos_str - format) + 1);
strncat(result, format, pos_str - format);
strcat(result, val_str);
format = pos_str + 2;
}
else if(pos_double && (pos_double < pos_float && pos_double < pos_char && pos_double < pos_str && pos_double < pos_int))
{
sprintf(val_double, "%lf", va_arg(arguments, double));
result = (char *)realloc(result, strlen(val_double) + strlen(result) + (pos_double - format) + 1);
strncat(result, format, pos_double - format);
strcat(result, val_double);
format = pos_double + 3;
}
else if(pos_float && (pos_float < pos_int && pos_float < pos_double && pos_float < pos_str && pos_float < pos_char))
{
sprintf(val_float, "%f", (float)va_arg(arguments, double));
result = (char *)realloc(result, strlen(val_float) + strlen(result) + (pos_float - format) + 1);
strncat(result, format, pos_float - format);
strcat(result, val_float);
format = pos_float + 2;
}
else if(pos_char && (pos_char < pos_int && pos_char < pos_str && pos_char < pos_float && pos_char < pos_double))
{
char val_char[2] = "\0";
val_char[0] = (char)va_arg(arguments, int);
result = (char *)realloc(result, strlen(val_float) + strlen(result) + (pos_float - format) + 1);
strncat(result, format, pos_float - format);
strcat(result, val_float);
format = pos_float + 2;
}
}while(pos_int || pos_str || pos_double || pos_float || pos_char);
va_end(arguments);
result = (char *)realloc(result, strlen(result) + strlen(format) + 1);
strcat(result, format);
return result; }
In these if statements is only one test expression besides the first which just sees if the main variable for the testing is initialized, and i need to do another 15 for making every possible situation for each if statement.
Upvotes: 1
Views: 66
Reputation: 33631
If we try to use your general structure, this can be made simpler by a struct
table/array with one element for each data type.
However, doing repeated strstr
is a bit wasteful and having all the pos_*
variables et. al. is needlessly complicated.
The usual, simpler, and faster method is to scan the format string char-by-char and find the next format specifier (e.g. %d
or %f
) and then do a switch
on type.
Also, each type was doing its own realloc/strcat/strncat
. Better to add a helper function or two to maintain a buffer/length along with a struct
for append control.
At first, I tried to merely clean up your code a bit. But, I soon realized that the only thing that made sense was to heavily refactor it.
This works. It could use a bit more error/underflow/overflow checking. And, is simpler and [again] faster:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
struct out {
int len;
char *buf;
};
static void
append_str(struct out *out,const char *str,int ilen)
{
int olen;
if (ilen < 0)
ilen = strlen(str);
out->buf = realloc(out->buf,out->len + ilen + 1);
memcpy(&out->buf[out->len],str,ilen);
out->len += ilen;
out->buf[out->len] = 0;
}
static void
append_char(struct out *out,int chr)
{
char str[2];
str[0] = chr;
str[1] = 0;
append_str(out,str,1);
}
char *
form(char *format, ...)
{
struct out out;
char tmp[100];
int chr;
va_list arguments;
int ilen;
char *cp;
va_start(arguments, format);
out.buf = NULL;
out.len = 0;
while (*format != 0) {
chr = *format++;
// ordinary char
if (chr != '%') {
append_char(&out,chr);
continue;
}
// grab format specifier
int lflg = 0;
while (*format != 0) {
chr = *format++;
if (chr != 'l')
break;
lflg = 1;
}
if (chr == '%') {
append_char(&out,chr);
continue;
}
ilen = -1;
switch (chr) {
case 'd':
ilen = sprintf(tmp, "%d", va_arg(arguments, int));
break;
case 'f':
if (lflg)
ilen = sprintf(tmp, "%lf", va_arg(arguments, double));
else
ilen = sprintf(tmp, "%f", va_arg(arguments, double));
break;
case 'c':
chr = va_arg(arguments,int);
append_char(&out,chr);
break;
case 's':
cp = va_arg(arguments, char *);
append_str(&out,cp,-1);
break;
default:
exit(1);
break;
}
if (ilen > 0)
append_str(&out,tmp,ilen);
}
va_end(arguments);
return out.buf;
}
int
main(void)
{
char *string;
string = form("%d %s %f %lf %c", 1, "ASA", 4.12345, 1.123421, 'A');
printf("%s\n",string);
free(string);
return 0;
}
Upvotes: 1