1

I'm doing a parsing function in C++, which takes a string and a double as arguments and returns the "value" of the string.

Here's the code :

double evaluate (char * toParse, int length, double x)
{

    // Case 'x'
    if ((toParse[0] == 'x') &&
        (length == 1))
    {
        return x;
    }

    // Case value
    char * endptr;
    double num = strtod(toParse, &endptr);
    if(endptr - toParse == length)
    {
        return num;
    }

    // Parsing
    int nBrackets = 0;
    for (int i = 0; i < length; i++)
    {
        if (toParse[i] == '(')
        {
            nBrackets++;
        }
        else if (toParse[i] == ')')
        {
            nBrackets--;
        }

        // Remove brackets.
        double _x = (toParse[0] == '(' && toParse[i-1] == ')' ) ?
                        evaluate(&toParse[1], i-2, x) : evaluate(toParse, i, x);
        double _y = (toParse[i+1] == '(' && toParse[length-1] == ')' ) ?
                        evaluate(&toParse[i+2], length - (i+1) - 2, x) : evaluate (&toParse[i+1] , length - (i+1), x);

        // Supports +, -, * and /
        if (nBrackets == 0 &&
            toParse[i] == '+')
        {
            return _x + _y;
        }
        else if (nBrackets == 0 &&
            toParse[i] == '-')
        {
            return _x - _y;
        }
        else if (nBrackets == 0 &&
            toParse[i] == '*')
        {
            return _x * _y;
        }
        else if (nBrackets == 0 &&
            toParse[i] == '/')
        {
            return _x / _y;
        }
    }
    return 0.;
}

int main()
{
    cout << evaluate("((4*x)+7)-x", 11, 5.) << endl;
    // Outputs 22, which sounds correct.
    return 0;
}

It's far from being flawless (no priority on operators, doesn't work if the string contains too much brackets, etc.), but I want to remove the double x argument, and to work on functions directly. (because I want to plot the function, and if I don't work on functions, I will have to parse the same string for each value of x...)

Is it possible ? I mean, doing something like :

double (double) operator+ (double f(double), double g(double))
{
    double h (double x)
    {
        return f(x)+g(x);
    }
    return h;
}

But it doesn't work, of course. Any ideas ? (class, etc.)

Thanks.

James M
  • 18,506
  • 3
  • 48
  • 56
  • 1
    It is illegal to call that function with a string literal in C++11. – chris Jul 09 '14 at 19:30
  • Both C and C++ allow passing pointers to other functions. – Jongware Jul 09 '14 at 19:30
  • Hi and thanks you three ! @chris : Couldn't I just use std::string to comply with C++11 then ? I don't mind changing it ! – user3821901 Jul 09 '14 at 19:34
  • @Jongware : Can you explain me better please ? I can't see which pointer I should pass to which function actually :( – user3821901 Jul 09 '14 at 19:37
  • 2
    It's possible to `return [=](double x) {return f(x)+g(x);};` as a `std::function` or something, but you can only overload an operator when one type is a user-defined type. – chris Jul 09 '14 at 19:37
  • If you are interested in improving/robustifying your solution, you should research recursive descent parsers -- very cool and fun to write! If you want to take the next step in adding to your programming arsenal, look into lex/yacc and flex/bison. Happy coding. – Steger Jul 09 '14 at 21:31
  • I think the question is overlooking something. If you parse "f(x) + g(x)", those are usually two seperate x's, and the resulting equation should have _two_ inputs, not just one. – Mooing Duck Jul 09 '14 at 21:34
  • If so... http://coliru.stacked-crooked.com/a/8f872f76d887b242 – Mooing Duck Jul 09 '14 at 22:09
  • Hi, I'll finish my project, then I will see if I have time to improve it :) @Mooing Duck : I'm only doing 2D-plotting with the SDL Library, then all the "x" are the same ! However, the parsing solution explained below became pretty slow : Since I've changed the char *, int parameters with a std::string, it takes me 1 second to parse something like "((3*x)-10)-(x-3)". I'll upload the code tomorrow, perhaps I missed something. Thank you all – user3821901 Jul 09 '14 at 22:16

2 Answers2

2

You can use function pointers for that. I didn't understand well if you want a function-pointer as arguments too but here's how you can do that:

typedef double (*handler) (double);


double add(handler x, handler y);
double sub(handler x, handler y);
double func1(double n);
double func2(double n);

int main()

{

    double (*funcPtr[256]) (double);

    funcPtr['+'] = func1;
    funcPtr['-'] = func2;

    double answer = funcPtr['+'](2));
}

double func1(double n)
{
    return n;
}

double func2(double n)
{
    return n;
}

double add(handler x, handler y) 
{ 
    return x(2) + y(2);
}
double sub(handler x, handler y) 
{ 
    return x(4) - y(2);
}

If it isn't what you're looking for but something like below code let me know and I will edit my answer:

funcPtr[toParse[i]](2, 2); // toParse[i] is '+' it will then call add(2,2)

the 256-size funcPtr array is with respect to sizeof(char). You should make sure index is an actual value in the array or you will access out of bounds.

Jack
  • 16,276
  • 55
  • 159
  • 284
1

A lambda comes to mind. Unfortunately I believe it would be undefined behavior to return a lambda from a function that captures function arguments. You cannot overload build in types like function pointers either. You could do something like this:

#include <iostream>
#include <functional>

struct CombinedFunction{
    enum class Operator{ add, subtract, multiply, divide } op;
    CombinedFunction(std::function<double(double)> f1, std::function<double(double)> f2, Operator op) : f1(std::move(f1)), f2(std::move(f2)), op(op){
    }
    std::function<double(double)> f1, f2;
    double operator()(double d){
        switch (op){
        case Operator::add:
            return f1(d) + f2(d);
            break;
        case Operator::subtract:
            //...
            break;
            //...
        }
    }
};

std::function<double(double)> operator+ (std::function<double(double)> f, std::function<double(double)> g){
    return CombinedFunction(f, g, CombinedFunction::Operator::add);
}

double f(double x){
    return 2 * x;
}
double g(double x){
    return 3 * x;
}

int main(){
    auto h = std::function<double(double)>(f) + g;
    std::cout << h(2);
    auto h2 = f + h + g;
    std::cout << h2(2);
}

It doesn't scale well and it is not very efficient. Maybe there is a template solution that does all this at compile time.

nwp
  • 9,623
  • 5
  • 38
  • 68
  • Hi, I used your solution and everything was fine ! I don't really understand the magic behind "double operator() (double d)" and "return CombinedFunction(...);", but whatever :) I'll change my std::function evaluate (char * toParse, int length) prototype to : std::function evaluate (std::string toParse) later on, so it will be more C++ ! :) Thank you ! – user3821901 Jul 09 '14 at 21:14
  • I think this answer is a touch overcomplicated: http://coliru.stacked-crooked.com/a/f3e3b18ff464a97f – Mooing Duck Jul 09 '14 at 21:33