Simplify nested switch statements used for calculating expression with LHS/RHS operands and test operator

Viewed 30

I am trying to write a function that calculates value of a mathematical expression expresed as LHS, RHS and test operator. Examples of such an expression are:

  • X == Y
  • Z > W
  • U < Q

where X, Y, Z, W, U, Q are arbitrary value expressed in either of the standard C types.

Below code example shows a naive implementation of such a function. What bothers me about it, and what I want to simplify, is the repetitiveness of switch statements.

  1. For every type of LHS it contains very similar code block which based on the type of RHS and test operator calculates the result,
  2. For every type of RHS it contains very similar code block which based on the test operator calculates the result.

This kind of code structure is obviously very error prone and lengthy because the same (similar) code block is repeated for every LHS type which when taken into account all of the possible standard C types (char's, int's, long's, float's, double's) amounts to 14 different switch cases. Meaning for every LHS there will be at least 14 cases and every one of those cases will contain every possible RHS type case, which is again at least 14.

How can I simplify this?

#include <stdint.h>
#include <stdbool.h>

struct operand {
    enum op_type {
        u8,i8,u16,i16,u32,i32,u64,i64,f,d
    } type;

    union op_value{
        uint8_t u8;
        int8_t i8;
        uint16_t u16;
        int16_t i16;
        /*etc...*/
    } value;
};

enum operator {
    equal, greater, lesser
};

bool test_operands(struct operand lhs, struct operand rhs, enum operator test)
{
    bool test_result;

    switch (lhs.type) {
    /* lhs */
    case u8:
        /* rhs */
        switch (rhs.type) {
        case u8:
            /* test */
            switch (test) {
            case equal:
                test_result = lhs.value.u8 == rhs.value.u8;
                break;
            case greater:
                test_result = lhs.value.u8 > rhs.value.u8;
                break;
            case lesser:
                test_result = lhs.value.u8 < rhs.value.u8;
                break;
            }
            break;
        case i8:
            /* test
             * same as above but with rhs.value.i8 used instead */
            break;
        case u16:
            /* test
             * need to promote lhs.value.u8 to u16 to avoid compiler warnings */
            switch (test) {
            case equal:
                test_result = (uint16_t)lhs.value.u8 == rhs.value.u16;
                break;
            case greater:
                test_result = (uint16_t)lhs.value.u8 > rhs.value.u16;
                break;
            case lesser:
                test_result = (uint16_t)lhs.value.u8 < rhs.value.u16;
                break;
            }
            break;
        case i16:
            break;
        /* etc...*/
        }
        break;

    /* lhs */
    case i8:
        /* same as above but with lhs.value.i8 used instead */
        break;

    /* etc... */
    }

    return test_result;
}

EDIT:

Ive found a macro implementation that simplifies this. But the problem I ran into and could not solve was type promotion. Because appropriate LHS or RHS value types need to be promoted to avoid compiler warnings.

0 Answers
Related