2

I'm using muParser to parse mathematical expressions, and I would like to add a function to the parser whose implementation will be provided by a non-static member function of a class. This excerpt from an example program should give an idea of what I want to do:

struct MyClass { // some boilerplate omitted
    double make_a_value(double a,  double b); // implemented elsewhere
};

int main(const int argc, const char** argv) {
    MyClass instance;
    mu::Parser p;
    p.DefineFun("f", MORE_MAGIC(MyClass::make_a_value, &instance));
    p.SetExpr("f(3, 2)");
    std::cout << p.Eval() << std::endl;
}

where MORE_MAGIC(...) stands for something with the signature double f(double arg1, double arg2) which is equivalent to calling instance->make_a_value(arg1, arg2). I don't know what MORE_MAGIC should be in order for this to work. That's the essence of my question.

The second argument of DefineFun can have any of the following function signatures:

  • double f()
  • double f(double)
  • double f(double, double)
  • and so on up to 10 double arguments
  • double f(double*, int) where the first pointer is an array and the int is its length
  • double f(const char*)
  • double f(const char*, double)
  • double f(const char*, double)

Unfortunately none of these include a void* data parameter that I can use to pass in an instance. It occurred to me to subvert a double* to pass the instance, but the problem is really that muParser doesn't let me pre-define a value to be passed to the function at all; only the arguments taken from the parsed expression get passed.

From reading several other posts (1, 2, 3, 4, 5) t seems that what I need is a bound function, and the preferred way to do it (in C++98) is boost::bind. But I've tried replacing MORE_MAGIC(...) with boost::bind(&wrapper, _1, _2, &instance), with

double wrapper(double a, double b, MyClass* p) {
    return p->make_a_value(a, b);
}

(hopefully correct syntax) and I get compiler errors:

/usr/include/muParserBase.h:134:95: error: no matching function for call to ‘mu::ParserCallback::ParserCallback(boost::_bi::bind_t<double, double (*)(double, double, MyClass*), boost::_bi::list3<boost::arg<1>, boost::arg<2>, boost::_bi::value<MyClass*> > >&, bool&)’
       AddCallback( a_strName, ParserCallback(a_pFun, a_bAllowOpt), m_FunDef, ValidNameChars() );

and so on. Ick.

I guess the problem is that boost::bind returns a boost::function, whereas I need a standard function, and according to this answer it's more or less impossible to get a standard function from a boost::function without some void* to stash the instance pointer. But I'm not sure I'm understanding correctly, having little experience with Boost, so my preliminary question is: can anyone confirm that boost::bind simply will not do what I need it to do?

And my main question: is there any way at all to accomplish this? Even if it involves arcane secrets of C++ wizardry? (Or *gasp* switching to C++11?)

Community
  • 1
  • 1
David Z
  • 128,184
  • 27
  • 255
  • 279

1 Answers1

4

Looks like the muParser API sadly isn't ready for C++.

In C++11 Lambdas can "decay" to function pointers, but this means they have to be stateless, so, you're back at the same spot: you can't bind extra parameters.

Is muParser open source? Perhaps there is a fork that has already amended this design flaw


Oh and, yes, boost::bind cannot break the laws of physics do this for you

sehe
  • 374,641
  • 47
  • 450
  • 633
  • Yes, it is open source and there are a couple forks I know about so I'll take a look and see if they can do that. If not, maybe I should just add the feature myself. – David Z Nov 23 '14 at 14:50
  • Yeah, it's a pretty glaring design flaw anyways, right there on this comprehensive list https://gist.github.com/rmartinho/3176551 (mmm. actually, that's not on the list. Maybe there's a separate one for C API's...?) – sehe Nov 23 '14 at 16:26
  • Does [this](https://code.google.com/p/muparser/issues/detail?id=10) mean that the muParser developers have refused to help with this issue? – crayzeewulf Sep 04 '15 at 22:14
  • @crayzeewulf Wow. It certainly looks like that. I'm appalled, because I thought it was a universal hallmark of bad API design to _not_ provide userdata `void*` arguments ... o.O – sehe Sep 04 '15 at 22:16
  • @sehe The least they can do is allow callbacks wrapped in [std::function](http://en.cppreference.com/w/cpp/utility/functional/function). This will solve a lot of issues associated with using muParser with C++. But that will add dependency on C++11 and the developers may not be interested in this. – crayzeewulf Sep 04 '15 at 22:22
  • That's for a C++ API. I don't know much about their project. And it amounts to the same in more modern vein – sehe Sep 04 '15 at 22:23