4

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?

minh anh b
  • 51
  • 4
  • Yes, of course -- but you will still need a `switch()` (or some other means of mapping character values to operators). When you read user-input, you are reading **character** input. To use the character set, see [ASCII Table and Description](https://www.asciitable.com/) for how to map character values to integer indexes. – David C. Rankin Sep 07 '21 at 01:14
  • @David C. Rankin - You're correct: the OP *will* ultimately need to map "operator" to "action". There are *several* of things the OP needs to worry about to design an "RPN calculator", incuding 1) parse the input, 2) differentiate "operators" from "operands", 3) calculate the results. Both [klutt](https://stackoverflow.com/a/69080167/421195) and [arfneto](https://stackoverflow.com/a/69080962/421195) show how to do a "table lookup" instead of a switch/case block to map "operator" to "action". – paulsm4 Sep 07 '21 at 03:26

3 Answers3

10

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.

klutt
  • 30,332
  • 17
  • 55
  • 95
5

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
*/
arfneto
  • 1,227
  • 1
  • 6
  • 13
  • Nice answer overall, but the beginning is a bit strange. *"I believe it is possible, and a bit easy."* Well, this is not easy and what you wrote is not what OP wants, which is to use a varaible as an operator. This is a good way to implement a polish calculator, but it's not what OP wants. You should change that sentence to something else. – klutt Sep 06 '21 at 23:29
  • I think it is easy, I wrote in minutes and I think that it is close to what the user wants. But let us forget that sentence. Deleted. Thanks! – arfneto Sep 06 '21 at 23:56
  • Great reply! You showed a good illustration of [function pointers](https://www.geeksforgeeks.org/function-pointer-in-c/). You also noted the importance of some kind of [stack](https://www.geeksforgeeks.org/stack-data-structure-introduction-program/) in the overall solution. FYI, the OP could absolutely use *ANY* of switch() or if/else, or function pointers. It's just a matter of " preference". The important thing is for the OP to look at the "big picture" (look at different tools and algorithms) instead of focusing on "minutiae" (like switch vs. function pointers). – paulsm4 Sep 07 '21 at 00:12
  • Well, the picture as I saw when the OP wrote __ Is it possible to input an arithmetic operator and use it as such inside the language itself?__ gave me the impression that a look-up table would practically fit the bill, as in `the_fs[oper[ix]](pop(), pop())` , since in the example the operator is used to select, well, the operation. Sure we can use `if` or `switch` or any other way – arfneto Sep 07 '21 at 00:28
  • The lookup table wasn't a bad idea. One should always consider using a table (vs. if/else or switch block) for performance. But "performance" isn't "critical" here - any of the approaches are likely to have similar (identical?) timing. Personally, I'd use a "switch" block. The key thing as that you *ALSO* discussed how to handle the operands (the important of a "stack" in the overall solution). One thing nobody's discussed yet is how to parse user input (tokenize the input into "operators" and "operands"). – paulsm4 Sep 07 '21 at 01:33
  • 1
    @paulsm4 you are right, but I believe that discussing parsing input would diverge from the OP question a bit. This is why I wrote a _ghost stack_ just to have a running code :) As for the options, like the `switch`, note that the code is cleaner using a VTABLE. And you can implement more or less operations with no change in code. At the cost of a single indirection. This is why people tend to use tables in filters and crypto. – arfneto Sep 07 '21 at 03:34
  • I strongly believe every question is asked in a broader "context", and that it's often important to address not just the question, but also the broader context. An old joke that illustrates the point: https://www.reddit.com/r/Jokes/comments/5z0gue/a_helicopter_with_a_pilot_and_a_single_passenger/. Anyway - +1 for your great answer :) – paulsm4 Sep 07 '21 at 03:56
  • I agree, @paulsm4. Sometimes is hard to find an adequate "zoom level" anyway. I used a timer here: no more than 30' but with a running, complete code – arfneto Sep 07 '21 at 04:14
1

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.

paulsm4
  • 114,292
  • 17
  • 138
  • 190