user13243126
user13243126

Reputation:

How can i minimize the number of test expressions in if statements?

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

Answers (1)

Craig Estey
Craig Estey

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

Related Questions