Can you implement arithmetic operator as variables in C?

Viewed 284

I'm trying to program a reverse polish calculator and I didn't wanted to write switches for every single arithmetic operator inputted. Is it possible to input an arithmetic operator and use it as such inside the language itself?

For example, the program could prompt the user for an arithmetic operator, for:
a = b [whatever operator here] c;

and some variable would store the operator. Let's say it's +, we get:
a = b + c;

Is this possible or should I just suck it up and write the switches for the operators?

3 Answers

Short answer:

No, it's not possible. At least not the way you want.

Long answer:

No, you cannot. C simply does not support things like the eval function in Python. For those who does not know what it is, this will print "Hello":

s = "print('Hello')" # A string with the code we want to execute
eval(s)              # Evaluate the string s as python code and execute it

If you want to do something like that in C, well just forget it. It's not possible.

You can achieve something similar with function pointers. It will look really awkward if you're not used to function pointers, but it mimics what you're talking about. Although quite badly.

int add(int a, int b) { return a+b; }
int sub(int a, int b) { return a-b; }

// Function taking two int and returning int
typedef int (operation)(int, int);

int main(void) {
    operation *ops[UCHAR_MAX+1];
    ops['+'] = add;
    ops['-'] = sub;
    printf("Sum:  %d\nDiff: %d\n", ops['+'](5,3), ops['-'](5,3));    
}

This prints:

Sum:  8
Diff: 2

ops is an array of function pointers. More precisely, an "array 256 of pointer to function (int, int) returning int". So we're using a single character directly to index it.

One thing to look out for here is to make sure that no negative values are passed to ops. This could happen on a machine where char is signed as default.

If you want some safety in form of error handling, you could do something like this:

int error(int a, int b) {
    fprintf(stderr, "Function not implemented");
    exit(EXIT_FAILURE);
}

and then do:

operation *ops[UCHAR_MAX+1];
for(int i=0; i < sizeof ops/sizeof *ops; i++) 
    ops[i] = error;

ops['+'] = add;
ops['-'] = sub;

This method is not worth all this extra hassle if you only want to support four operations, but it can actually come in quite handy if you're writing an emulator. I watched a very interesting youtube playlist about writing a NES emulator. It's in C++, but very oldschool so if you know C, it's not hard to follow. He talks about function pointers in part 2.

https://youtu.be/F8kx56OZQhg

Note: Not my channel. I have absolutely nothing to do with it. Was hesitating because it could look like spam, but those videos are really interesting for a coder.

It is possible. See an example below

RPN calculator implies in some sort of stack. On identifying an operand you pop up two operands and does the operation.

I want to let a short example so I will shortcut some things...

our "stack"

I will assume integer operands only, but I think it is not hard to make it generic, by using the usual suspects, void*

This is the stack in the example

// stack
int pop()
{
    int val = 1 + rand()%1000 / (-1)*(rand()%2);
    printf("pop(): operand %d from stack\n", val);
    return val;
}

It just gets a random number in [-998,999] inclusive :)

the operations

// operations 
int divd(int A, int B) { return A / B; };
int mult(int A, int B) { return A * B; };
int sub(int A, int B)  { return A - B; };
int sum(int A, int B)  { return A + B; };

So we have something to test.

the syntax glue

Assuming the operator is a single char we can build a lookup-table indexed by the operator and put the functions there.

C was created precisely to write this kind of things.

    int (*the_fs[256])(int,int) = {0};
    the_fs['+'] = sum;
    the_fs['-'] = sub;
    the_fs['*'] = mult;
    the_fs['/'] = divd;

a test

    // test for some cases
    const char oper[] = "+-*/";
    const int N = 8; 
    for (int i = 0; i < N; i += 1)
    {
        // do one test
        int ix = rand() % 4;
        printf("using '%c'\n", oper[ix]);
        a = pop();
        b = pop();
        printf("%d %c %d = %d\n", a, oper[ix], b, 
            the_fs[oper[ix]](a,b));
    }

It is just a matter of calling the_fs[X] where X is the operand. No need for if, no need for switch(). It can the used alo for unary operators, just PUSHing back the second operator in the function.

output for a random test with random numbers and random operations

using '+'
pop(): operand -624 from stack
pop(): operand -906 from stack
-624 + -906 = -1530
using '*'
pop(): operand -724 from stack
pop(): operand 1 from stack
-724 * 1 = -724
using '*'
pop(): operand -733 from stack
pop(): operand -807 from stack
-733 * -807 = 591531
using '-'
pop(): operand -938 from stack
pop(): operand 1 from stack
-938 - 1 = -939
using '*'
pop(): operand 0 from stack
pop(): operand -121 from stack
0 * -121 = 0
using '*'
pop(): operand 1 from stack
pop(): operand 1 from stack
1 * 1 = 1
using '-'
pop(): operand 1 from stack
pop(): operand -396 from stack
1 - -396 = 397
using '+'
pop(): operand -130 from stack
pop(): operand -372 from stack
-130 + -372 = -502

the complete test program

#include <stdio.h>
#include <stdlib.h>

int pop(); // stack simulator :)

int sum(int,int);
int sub(int,int);
int mult(int,int);
int divd(int,int);

int main(void)
{
    int        a      = 0;
    int        b      = 0; // the operands
    srand(210907);
    int (*the_fs[256])(int,int) = {0};
    the_fs['+'] = sum;
    the_fs['-'] = sub;
    the_fs['*'] = mult;
    the_fs['/'] = divd;

    // so you have an operator, and it is RPN
    // the stack must have 2 operands
    
    // test for some cases
    const char oper[] = "+-*/";
    const int N = 8; 
    for (int i = 0; i < N; i += 1)
    {
        // do one test
        int ix = rand() % 4;
        printf("using '%c'\n", oper[ix]);
        a = pop();
        b = pop();
        printf("%d %c %d = %d\n", a, oper[ix], b, 
            the_fs[oper[ix]](a,b));
    }
    return 0;
}

// stack
int pop()
{
    int val = 1 + rand()%1000 / (-1)*(rand()%2);
    printf("pop(): operand %d from stack\n", val);
    return val;
}

// operations 
int divd(int A, int B)  { return A / B; };
int mult(int A, int B) { return A * B; };
int sub(int A, int B)  { return A - B; };
int sum(int A, int B)  { return A + B; };

/*
https: //stackoverflow.com/questions/69080130/
can-you-implement-arithmetic-operator-as-variables-in-c
*/

Q: Does C support operator overloading?

A: No: only C++

Q: I want to branch on different operators ("+", "-", "/", etc.). Do I absolutely need a switch()/case block?

A: No. You can always use if/else equivalents. But a switch/case block is arguably "cleaner".

FINALLY:

What you're really trying to do is parse an input string into "tokens", then process the tokens. There are many different tools you could use (everything from calling "strtok()" to using lexx/yacc); there are many different algorithms. SUGGESTION: Google for "C parsing", look at Shunting Yard algorithm.

Related