Introduction
When it comes to evaluating mathematical expressions at runtime you need a math parser. Today there are a variety of solutions available for both commercial and noncommercial use. For instance MTParser, fparser or muParser to name a few. All of them are pretty fast but they do have strict limitations regarding their datatypes and none of them has native support for either strings or arrays. Since these features are probably the most often requested to be added to muParser I decided to extend its engine in order to support strings and arrays. muParserX is the result of this redesign and to my knowledge it's currently the only open source C++ math parsing library having these features. (I don't count Octave since thats a CAS system not a math parser). The following table gives an overview over the fundamental differences between the parsers of the muParser family:
| Parser | Data types | Precision | User defined operators | User defined functions | Locale support |
Licence | Performance (Expr. per second)* |
||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| complex | scalar | string | vector | Binary | Postfix | Infix | Strings as parameters |
Arbitrary number of parameters |
|||||
| muParser | (1) |
![]() |
(2) |
![]() |
double | ![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
MIT | ~ 10.000.000 |
| muParserSSE | ![]() |
![]() |
![]() |
![]() |
float | ![]() |
![]() |
![]() |
![]() |
max. 5 | ![]() |
MIT | ~ 20.000.000 - 100.000.000 |
| muParserX | ![]() |
![]() |
![]() |
![]() |
double | ![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
LGPL V3 | ~ 410.000 |
So much for the good news, the bad news is that: there ain't no such thing as a free lunch. The obvious consequence of this redesign is a loss in performance. You can't expect mathematical expressions to be evaluated with the same speed when they are evaluated using complex numbers. You can't expect callbacks to be equally performant when they have to do runtime type checking on their arguments. So in order not to let the new features compromise muParsers performance i created an experimental version named muParserX and this article will show you how to use it.
Features
- Supported data types: double, integer, complex, boolean, string, array
- Extensible with custom operators (binary, infix or postfix)
- Extensible with custom functions with an arbitrary number of function arguments
- Support for an unlimited number of variables and constants
- No limit on expression complexity
- Reads binary, hexadecimal, complex, integer and string values from expressions and can be extended to read user defined values as well.
- Supports a large variety of predefined operators, functions and constants.
- Written in standard compliant C++ code with no external dependencies.
Predefined Constants
By default the parser supports the following mathematical constants:- The eulerian number with:
e = 2.718281828459045235360287 - The mathematical constant equal to a circle's circumference divided by its diameter.
pi = 3.141592653589793238462643 - The imaginary unit with:
i = sqrt(-1)
Binary operators:
- Standard operators:
"+", "-", "*", "/", "^" - Assignment operators:
"=", "+=", "-=", "*=", "/=" - Logical operators:
"and", "or", "xor","==", "!=", ">", "<", "<=", ">=" - Bit manipulation:
"&", "|", "<<", ">>" - String concatenation:
"//"
Postfix operators:
- Unit postfixes (nano, micro, milli, kilo, giga, mega):
"{n}", "{mu}", "{m}", "{k}", "{G}", "{M}"
Infix operators:
- Sign operator and type conversions:
"-", "(float)", "(int)"
Predefined Functions
- Standard functions:
abs, sin, cos, tan, sinh, cosh, tanh, ln, log, log10, exp, sqrt - Unlimited number of arguments:
min, max, sum - String functions:
str2dbl, strlen, toupper - Complex functions:
real, imag, conj, arg, norm - Array functions:
sizeof
Sample expressions
The next table shows samples of expressions that can be evaluated using muParserX:
| Expression | Result | Explanation |
"hello"=="world" |
false | Comparing strings |
"hello "//"world" |
"hello world" | String concatanation operator |
sin(a+8i) |
... | Support for a variety of predefined functions working with complex numbers. |
va[3]+vb[5] |
... | Support for array variables |
va[3]=9 |
... | Assignment operator in combination with indexed access to a vector |
toupper("hello"//"world") |
"HELLOWORLD" | Transforming a concatenated string to uppercase. |
#010010 |
18 | Interpreting binary values |
0x1eff |
7935 | Interpreting hex values |
#10>0x1eff |
false | Comparing binary and hex values |
1+2-3*4/5^6 |
2.99923 | The standard operators |
(((-9))-e/(((((((pi-(((-7)+(-3)/4/e))))/(((-5))-2)-((pi+(-0))*(sqrt((e+e))*(-8))*(((-pi)+(-pi)-(-9)*(6*5))/(-e)-e))/2)/((((sqrt(2/(-e)+6)-(4-2))+((5/(-2))/(1*(-pi)+3))/8)*pi*((pi/((-2)/(-6)*1*(-1))*(-6)+(-e)))))/((e+(-2)+(-e)*((((-3)*9+(-e)))+(-9)))))))-((((e-7+(((5/pi-(3/1+pi)))))/e)/(-5))/(sqrt((((((1+(-7))))+((((-e)*(-e)))-8))*(-5)/((-e)))*(-6)-((((((-2)-(-9)-(-e)-1)/3))))/(sqrt((8+(e-((-6))+(9*(-9))))*(((3+2-8))*(7+6+(-5))+((0/(-e)*(-pi))+7)))+(((((-e)/e/e)+((-6)*5)*e+(3+(-5)/pi))))+pi))/sqrt((((9))+((((pi))-8+2))+pi))/e*4)*((-5)/(((-pi))*(sqrt(e)))))-(((((((-e)*(e)-pi))/4+(pi)*(-9)))))))+(-pi) |
-12.23016549 | No limit on equation complexity |
Using the library
Adding the library to your projects
At the moment I do not provide static or dynamic libraries for muParserX. In order to use the library you should add all files located in the "muparserx/parser" subdiretory into your own project. The library currently comes with project files for VisualStudio 2008 and a Makefile for the GNU C++ compiler but It should compile on every standard compliant C++ compiler.
Parser Tokens
Before starting i should give you an overview over the classes related to muParserX. Parsing works by splitting the mathematical expression into so called tokens. A token can be either a value, a variable, a function call, an operator or a special character such as brackets or commas. The tokens will be stored internally for processing during the evaluation process. When using the parser you will deal with tokens directly only when setting variables, constants functions or operators.
The parser defines an abstract base class named mup::IToken from which all of its tokens are derived. There are three major kinds of token: Value tokens, Callback tokens and generic tokens. Values represent either constants or variables, callbacks represent functions and operators, generic tokens are used for brackets, commas and special characters.
It's important to know that muParserX handles its tokens via reference counted smartpointers using the mup::TokenPtr<...> template class. There is no need to release their pointers explicitely
with the delete operator. The following typedefs represent smartpointer names:
namespace mup { // Type of a managed pointer storing parser tokens via their base type. typedef TokenPtr<IToken> ptr_tok_type; // Type of a managed pointer storing value tokens via their base type. typedef TokenPtr<IValue> ptr_val_type; // Type of a managed pointer storing binary operator tokens. typedef TokenPtr<IOprtBin> ptr_binop_type; }
Parser initialization
In order to use muParserX you have to include the file mpParser.h into your projects. The parser resides in the namespace mup.#include "mpParser.h" using namespace mup;
The parser functionality is organized in so called packages. A package consits of a set of
mathematical functions, operators, value recognition callbacks and constants. Creating a
parser instance is simple, the constructor takes an optional
variable made up of several flags for activating certain parser packages. For convenience
the parser already defines the enumeration values pckALL_COMPLEX and
pckALL_NON_COMPLEX which can be used to configure the parser for either
complex valued calculation or noncomplex calculations. If this parameter is
omitted the parser will be run in its default mode which means it is using complex
numbers and all available operators and functions will be installed (pckALL_COMPLEX).
ParserX p(pckALL_NON_COMPLEX);
The following bits and bit combinations can be used for activating or deactivating certain parser packages at construction time:
- pckCOMMON = 1
Installs. - pckUNIT = 2
Installs postfic operators for unit conversions. - pckCOMPLEX = 4
Installs complex valued functions, operators and a constant for the imaginary unit i. The packages pckCOMPLEX and pckNON_COMPLEX are mutually exclusive. - pckNON_COMPLEX = 8
Installs noncomplex functions and operators. The packages pckCOMPLEX and pckNON_COMPLEX are mutually exclusive. - pckSTRING = 16
Installs functions for string manipulation and the binary string concetanation operator. - pckALL_COMPLEX = pckCOMMON | pckCOMPLEX | pckSTRING | pckUNIT
- pckALL_NON_COMPLEX = pckCOMMON | pckNON_COMPLEX | pckSTRING | pckUNIT
Representing values
muParserX defines a common interface for classes representing Values. This interface is implemented by the classes mup::Value and mup::Variable.
mup::Value is a variant type class able to store different data types in a single object. If you want to set up a value for use with muParserX you first have to wrap it into an object of this type. Then you need to bind the value to a mup::Variable object which essentially serves as a proxy for value objects.
Important member functions of parser value classes:- float_type GetReal() const
Returns the real part of the value; Only valid if type is integer, float or complex ('i', 'f' or 'c). - float_type GetImag() const
Returns the imaginary part of the value; Only valid if type is complex ('i', 'f' or 'c). - const string_type& GetString()
Returns the string value; Only valid if this value represents a string ('s'). - bool GetBool() const
Returns the boolean value; Only valid if the value represents a bool value ('b'). - const array_type& GetArray() const
Returns the array stored in the value type; Only valid if the value represents an array ('a'). - char_type IValue::GetType() const
Returns the type as a character. - bool IValue::IsArray() const
Returns true if the value is an array. - bool IValue::IsScalar() const
Returns true if the value is a scalar value. (Either integer, float or complex) - bool IValue::IsInteger() const
Returns true if the value is an integer value. - bool IValue::IsComplex() const
Returns true if the value is a complex value. - bool IValue::IsString() const
Returns true if the value is a string value. - bool IValue::IsNonComplexScalar() const
Returns true if either an integer or a float value.
As you may have already guessed by now, the parser supports the following data types:
| Type | Shortcut | Description | Additional qualification |
|---|---|---|---|
| Integer | 'i' | signed Integer values | scalar |
| float | 'f' | double precision floating point value | |
| complex | 'c' | Complex values with double precision for real an imaginary part. | |
| array | 'a' | An array of values; May contain elements of mixed type. | array |
| boolean | 'b' | Boolean values. | other |
| string | 's' | Literal string values. | |
| void | 'v' | For internal use only; signals a variable is not bound to a value. |
Defining variables and constants
Variables can be defined either explicitely in your C++ code or implicitely at parser runtime. Implicit creation of variables is usefull when writing console applications that require dynamic creation of variables (i.e. by using the assignment operator).Explicit declaration of variables
In order to create a value for use with muParserX you have to create a value object first. The value class provides overloaded constructors for all relevant types:
using namespace mup; // ... // Create a complex variable Value cVal(cmplx_type(1, 1)); // Create a string variable Value sVal(_T("Hello World")); // Creating a floating point variable Value fVal(1.1); // Creating an integer variable Value fVal(1); // Creating an boolean variable Value bVal(true); // Create a 3x3 identity matrix Value m1(3, 3, 0); m1[0][0] = 1; m1[1][1] = 1; m1[2][2] = 1; // Create an array Value arr(2, 0); // Arguments: number of elements, default value arr[0] = 2.0; arr[1] = _T("hallo"); // note that arrays can consits of mixed type elements
Once you have a parser value you can bind it to a variable object. A variable serves as a proxy class for value
objects. It merely holds a pointer to the original value and refers all queries to it thus allowing the parser
to change it. After creating the variable object use the DefineVar function in order to add it to the parser. In a similar procedure constants can be defined by using the DefineConst member function
but they can be submitted directly.
ParserX p; Value val( 1.1); Variable var(&val); p.DefineVar("a", var); // Now lets define some constants p.DefineConst("b", val); // Alternatively you could simply use: p.DefineConst("b", 1.1);
![]() |
Once a variable is defined there is no need to call DefineVar again just to change it's value! If you
want to change the value change the submitted value object directly. Remember: The parser has a pointer to this value object and it gets the value directly from there! Consequently you have to make sure the value object exists throughout the lifetime of the parser instance that is using it! |
Implicit declaration of variables
Implicit creation of variables refers to the creation of parser variables at parser runtime. This feature
is turned off by default so in order to use it you have to activate it first by calling the
EnableAutoCreateVar member function:
ParseX p; p.EnableAutoCreateVar(true);
Once you have activated the automatic creation of variables the parser will add a variable every time
it finds unknown input that could represent a variable name without actually having a definition for
such a variable. For instance an expression like a=10 would automatically create a new
variable named a provided the variable isn't defined already.
Evaluating an expression
After setting up your variables properly you can use theSetExpr member function of muParser in order
to define the expression. The next thing you have to do is call Eval to evaluate the expression. The return value is again stored in an object of type mup::Value. The complete code for creating variables, setting up the expression and evaluating the result is shown here:
// Create the parser instance ParserX p; // Create an array of mixed type Value arr(3, 0); arr[0] = 2.0; arr[1] = "this is a string"; // Create some basic values Value cVal(cmplx_type(1, 1)); Value sVal("Hello World"); Value fVal(1.1); // Now add the variable to muParser p.DefineVar("va", Variable(&arr)); p.DefineVar("a", Variable(&cVal)); p.DefineVar("b", Variable(&sVal)); p.DefineVar("c", Variable(&fVal)); p.SetExpr("va[0]+a*strlen(b)-c"); for (int i=0; i<10; ++i) { // evaluate the expression and change the value of // the variable c in each turn cVal = 1.1 * i; Value result = p.Eval(); // print the result console() << result << "\n"; }
Querying Variables or constants
Sometimes its necessary to get a list of all variables or constants currently defined be muParserX. You may either want to query all variables or just the variables used in an expression. The latter may be usefull when you are dealing with a large number of variables and it's not possible to define all of them before evaluating an expression. In order to get the list of all variables currently defined by an instance of muParserX simply use theParserX::GetVar() member function. It returns a map containing the variable names as the keys and pointers to the variable tokens as the values. In order to further process the variable you should cast the token into its proper type (mup::Variable) as shown in the following sample:
// Get a map of all variables used by muParserX var_maptype vmap = parser.GetVar(); for (var_maptype::iterator item = vmap.begin(); item!=vmap.end(); ++item) cout << item->first << "=" << (Variable&)(*(item->second)) << "\n";
Getting the expressions used in an expression does work the same way except that you have to use the ParserX::GetExprVar() member function instead of ParserX::GeVar(). Querying the expression variables does only make sense after having set up an expression using ParserX::SetExpr(...).
// Set the expression parser.SetExpr("a*sin(b)"); // Query the expression variables var_maptype vmap = parser.GetExprVar(); for (var_maptype::iterator item = vmap.begin(); item!=vmap.end(); ++item) cout << " " << item->first << " = " << (Variable&)(*(item->second)) << "\n";Querying all parser constants works the same way by using
ParserX::GetConst() member function:
// Get a map containing all constants val_maptype cmap = parser.GetConst(); for (val_maptype::iterator item = cmap.begin(); item!=cmap.end(); ++item) cout << " " << item->first << " = " << (Value&)(*(item->second)) << "\n";
Extending the library
Like the original muParser this library is designed to allow users to extend it with custom functions or and operators. There is however one fundamental difference: muParserX is using objects as callbacks and not static functions. The following sections will show you how to extend the library with custom callbacks.
Defining custom functions
All custom functions must implement the ICallback interface. This interface provides the ICallback::Clone(), ICallback::Eval(...) and ICallback::GetDesc() member functions. An implementation of a user defined class may look like this:
class MySine : public mup::ICallback { MySine() :ICallback(cmFUNC, "mysin", "f:f") {} virtual void Eval(ptr_val_type &ret, const ptr_val_type *a_pArg, int a_iArgc) { // Get the argument from the argument input vector float_type a = argv[0]->GetFloat(); // The return value is passed by writing it to the reference ret *ret = sin(a); } const char_type* GetDesc() const { return "mysin(x) - A custom sine function"; } IToken* Clone() const { return new MySine(*this); } };
The mup::ICallback constructor takes three argument. The first determines the type of the callback. If you want to add a parser function use cmFUNC. The second is the function name and the last parameter is the function prototype definition.
When entering expressions the parser does the type checking provided the functions and operators do specify a prototype. If the
functions do not specify prototypes the parser leaves the type checking to the function callback implementation. The prototype is a string with the following
format:
Example prototype definitions:
"c:icc" - A complex function taking an integer and two complex values as its parameter."f:ff" - A function taking two floating point arguments returning a float"i:s" - A function taking a single string argument returning an integer value"i:*" - A Function taking an unlimited number of arguments returning an integer"i:v" - A Function without parameter returning an integer value
Operator prototypes are similar to function prototypes. Binary operators are treated like functions taking two arguments, unary operators like functions with a single argument.
Defining custom operators
Defining your own operators is very similar to defining new functions. You have to create a callback object and register it at the parser instance. The parser lets you add unary operators (both infix and prefix) as well as binary operators.
License
For the moment I have decided to put muParserX under the LGPLv3 License. Please note that this license is a bit more restrictive than the MIT-Licence used by the
original muParser. The main reason for changing the licence is that i'm simply dissappointed by the almost complete
lack of donations received for muParser. I have seen download numbers of muParser reach 18000+ over the years but
the total number of donations received is five. So apart from a few notable exceptions thats not exactly what I would
call an overwhelming demonstration of appreciation for my work. On the other side i know how much it sucks finding a library
to solve a specific problem one has to work on just to find out it can't be used because it's published under the GPL.
I don't have a problem with proprietary use of my work but be fair and comply with the LGPL (publish your modifications!)
so others can benefit too.
muParserX - A C++ math parser library with array and string support Copyright (C) 2010 Ingo Berg This program is free software: you can redistribute it and/or modify it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU LESSER GENERAL PUBLIC LICENSE for more details. You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE along with this program. If not, see http://www.gnu.org/licenses.
Benchmarks
Lets be clear about this: Due to it's multiple data types and using complex numbers as it's base type muParserX has to make compromises with regard to absolute parsing performance. However i have been very active in order to minimize this effect and consequently the latest Version is now approximately 500 times faster than the initial release. In other words on a modern computer you should be able to compute well above 100.000 expressions per second with muParserX. This should be sufficient for most applications and its not too bad either when compared to other fast parsers. It's not enough to be one of the top performers though:
Image 1: By comparing benchmark results of different muParserX versions a significant improvement of parsing performance from one version to the next can be shown. This is due to introducing parsing from cached tokens (V1.05), parsing from the reverse polish notation of the expression (V1.07) and from introducing references for returning results from callback objects rather than using dynamically created value objects (V1.08).
The next image shows a comparison with other free math parsers. The top performer in this test are muParserSSE and MathPresso which both are based just in time compilation of the expression so it's not a particulary fair comparison but i'm gonna show it anyway...
Image 2: Due to the overhead imposed by its additional features muParserX does not reach the performance of the other math parsers.
History
V1.0 Initial Release (20090327)
- Complex numbers added with support for basic binary operators
- Vector type added with support for basic operations (v*v, v+v)
- Variable class changed to take a pointer to a value class instead of pointers to basic types such as int or double.
V1.02
- Index operator added
- addition/subtraction of vectors added
V1.03
- basic functions rewritten to complex valued functions
- Unit postfix operators added to the standard setup
V1.04 (20100516)
- Querying of expression variables implemented
- Bugfix for incorrect evaluation of expressions using the index operator added
- Minor optimizations to improve parsing speed (~ 5% increase in speed)
- nil values removed
- Support for functions without parameters added
- UNICODE support added
V1.05 (20100523)
- Parsing performance increased by factor 7 due to caching tokens once an expression is parsed. Successive evaluations will use the cached tokens instead of parsing from string again.
V1.06 (20100711)
- Parsing performance increased by factor 2-3 due to introducing a parsing engine based on the reverse polish notation of an expression.
- The parser now deals properly with the associativity of binary operators.
V1.07 (20100819)
Enhancements:- Parsing performance improved by 20 - 30 % due to removing unnecessary copy constructor calls when returning the final result.
- License changed to LGPL V3
- The parser Value class could not properly handle matrices (vector of vector) uses the reverse polish notation of an expression for the evaluation.
- Error messages did not display the proper type id's when compiled with UNICODE support.
V1.08 (20100907)
New Feature:- Implicit creation of unknown variables at parser runtime is now possibe (i.e. expressions like "a=0"; with a beeing a undefined variable).
- Callbacks can now be organized in packages
- Callbacks split into a complex and a non complex package
- Complex power operator added
- All callbacks now return their result via reference instead of dynamically creating a new Value instance.
- The Value class was optimized for faster construction and copying. All in all the parser is now 5 times faster than V1.07.
Autor: Ingo Berg; Dieser Text und die dazugehörigen Mediendateien stehen, sofern nicht anderweitig angegeben, unter der Creative Commons Namensnennung 3.0 Deutschland Lizenz.
(1)

