Is there any kind of "expression class" (C++)
Asked Answered
D

9

20

I am creating a game that lets the player enter input, changes some states, then checks if a "goal value" is true (obviously this description is muchly simplified), and I want to be able to have that goal value be anything from if the players life is below a certain value to if the amount of enemies remaining is equal to zero. Is there any "expression class" that can hold a simple "value1 compare-operator value2" and check it? ie:

expression goal(x = 4);

if not, does anybody have any suggestions as to how I could develop an expression class?

EDIT: another (more towards what I am trying to achieve) example:

game.init(){ expression goal = FileRead(goalfile); }
game.checkstate(){ if(goal) exit(1); } //exit 1 is the games win state

//another more specific eg.:
class level1 { public: expression goal(total_enemies == 0); };
class level2 { public: expression goal(player.x == goal.x && player.y == goal.y); };
Diaphony answered 10/6, 2009 at 21:27 Comment(0)
E
24

Dynamic expressions

If you want to receive a string from the user and built an expression from that, maybe the C++ Mathematical Expression Library fits your bill?

template<typename T>
void trig_function()
{
   std::string expression_string = "clamp(-1.0,sin(2 * pi * x) + cos(x / 2 * pi),+1.0)";
   T x;
   exprtk::symbol_table<T> symbol_table;
   symbol_table.add_variable("x",x);
   symbol_table.add_constants();

   exprtk::expression<T> expression;
   expression.register_symbol_table(symbol_table);

   exprtk::parser<T> parser;
   parser.compile(expression_string,expression);

   for (x = T(-5.0); x <= T(+5.0); x += 0.001)
   {
      T y = expression.value();
      printf("%19.15f\t%19.15f\n",x,y);
   }
}

There are also the possibility embed a scripting language, such as Lua or Python, which will give you (even) more power. This is something to consider if you're writing a game, since you'll likely want to script large parts of it.

If you're using Qt, you can use QtScript (Javascript-ish) to run expressions that read (static or dynamic) properties from your QObject-derived objects.

Using one of the above keeps you from having to write your own parser, AST and evaluator, however for a small set of operators it shouldn't be too hard to hack together something if you use Boost.Spirit or some other decent parsing library.

Static expressions

For selecting between a set of predefined expressions (i.e. known at compile time), you should store the expression in a polymorphic function object.

For C++11, if that's available to you, use std::function and lambda expressions.

std::function<bool (int, int)> expr = [](int a, int b) { a*2 < b };

For earlier compilers, I recommend function and bind, either in Boost (boost::) or C++0x TR1 (std::), depending on your compiler. Also, Boost.Lambda will be of help here, as it allows you to construct and store expressions for later evaluation. However, if you're not familiar with C++ and templates (or functional programming), it will likely scare you quite a bit.

With that you could write

using namespace boost::lambda;
boost::function<bool (int, int)> myexpr1 = (_1 + _2) > 20;
boost::function<bool (int, int)> myexpr2 = (_1 * _2) > 42;
std::cout << myexpr1(4,7) << " " << myexpr2(2,5);

with bind, it'd look like:

boost::function<bool (Player&)> check = bind(&Player::getHealth, _1) > 20;
Player p1;
if (check(p1)) { dostuff(); }

check = bind(&Player::getGold, _1) < 42;
if (check(p1)) { doOtherStuff(); }
Excreta answered 10/6, 2009 at 21:37 Comment(4)
unfortunately, this is entirely impractical for arbitrary expressions. Which i think is what the poster really wants (despite his example being of "val1 op val2").Gillis
Is it? Using boost::lambda you can combine all sorts of expressions, and even statements and all. What do you mean?Excreta
well, since the boost lambdas are constructed at compile time and just used by the caller. what would you do if the user entered an op combination that you haven't accounted for. Since there is an infinite number of combinations of math operations...certainly you can't statically account for them all.Gillis
ah.. I didn't see that the OP wanted to construct expressions dynamically. It's a bit unclear. I'll edit my post.Excreta
N
1

There is no standard way to compile expressions during runtime. You'll have to do it some other way.

You might consider using a scripting language, like Lua or Python, and embed it in your C++. That would allow your players the ability to program to the extent you want them to.

Nicodemus answered 10/6, 2009 at 22:0 Comment(1)
+1 for Lua recommendation. Lua is becoming almost ubiquitous as the lightweight embeddable engine of choice for games and mobile devices.Intranuclear
C
0

No, there isn't anything like that. Perhaps an expression class is a little too abstract. What about defining various Goal classes with an IsReached() method?

Catheryncatheter answered 10/6, 2009 at 21:34 Comment(0)
P
0

C++ doesn't have this as part of the language -- there is no way to, at runtime, get access to the same stuff that parsed your program.

I'm sure there are numerous third-party arithmetic parser libraries you could use, however.

Poppied answered 10/6, 2009 at 21:35 Comment(1)
I don't think that's what he meant.Alroy
D
0

I'm thinking you can define your own class and work around using the 'assert' keyword, but I may have understood the question wrong.

http://www.cplusplus.com/reference/clibrary/cassert/assert/

Deform answered 10/6, 2009 at 21:37 Comment(0)
W
0

Why not build your own expression classes?

class GoalBase
{
    virtual bool goal() = 0;
};

class Enemies : public GoalBase 
{
   // ..
   private:
      int enemies_;

   public:
      Enemies(int start) : enemies_(start) {}
      void kill() { if (enemies_) --enemies_; }
      bool goal() { return enemies_ == 0; }
};

int main()
{
    Enemies enemiesToKill(5);
    enemiesToKill.kill();    

    // ..
    if (enemiesToKill.goal()) {
        // ..
    }

    return 0;
}

Other classes could have other methods, parameters, operators etc. Use your imagination.

Wendolyn answered 10/6, 2009 at 21:49 Comment(0)
M
0

There's no standard way to do it in C++. One solution is to write your own parser.

Another solution, that I'd recommend: embed a Lua interpreter in your program. Lua is a simple yet powerful programming language, that also has an extremely lightweight (<300kB) and easy-to-use interpreter. Read an introductory article here: http://www.ibm.com/developerworks/linux/library/l-embed-lua/index.html

Having Lua embedded in your game has a number of nice side advantages:

  • you can use it as a powerful configuration language for your game
  • with Lua, you can easily create a command-line interactive environment, which is nice for testing and experiements. For example, you will be able to change the game engine parameters and see the effect immediately, without recompiling. That's especially convenient for "researchy" projects or game programming.
Mikiso answered 10/6, 2009 at 22:15 Comment(0)
S
0

Static expressions

(amendment to Macke's post)

When your expressions are known at compile time, you can use std::function. However, the performance might not be optimal.

You can automatically register test at compile-time and execute them at run-time with (presumambly) minimal runtime overhead, using C++11 templates and macros. A proof-of-concept implementation can be found here.

In the long run, a language feature named "Contracts" could do the job. (N4415, N4435, N4378) Today, there are various libraries available to support contract programming.

Stepson answered 27/7, 2017 at 13:25 Comment(0)
D
-3

There don't seem to be all that many recognised expression evaluation libraries for C++. I wrote my own for CSVfix which you can persuse by looking at the a_expr.h and a_expr.cpp files in the ALib library that forms part of the CSVfix source. The evaluator has not a great deal to recommend itself, except that it does the job and is (IMHO) fairly simple to understand.

Unfurtunately, there is currently no public documentation for the evaluator, and by my own rule of thumb, that which is not documented cannot be reused. However, the unit tests show how it can be used, and the source may give you some ideas on implementing your own evaluator, should the fancy take you.

Disport answered 10/6, 2009 at 21:55 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.