Trouble calling instance method from ChaiScript


#1

I have a C++ class that looks something like the following:

class foo
{

void myMethod(int i, int j);
}

and an instance variable
> foo f

I want to be able to call myMethod of that specific instance (i.e, f.myMethod(1,2) ) from ChaiScript.

After following the descriptions in the cheatsheet for registering methods, running into a compile error “Multiple overloads of ‘handle’ instantiate to the same signature…”, hunting through StackOverflow for a solution, I finally came up with the following way to register the method
chai.add(chaiscript::fun(static_cast<void (foo::*)(int, int)>(&foo::myMethod)), "f");

This is clearly not right as the actual instance is not included anywhere. How is this supposed to work?

Thanks in advance.

NB - I’ve tried replacing &foo::myMethod with things like &this->myMethod but I just get various other syntax errors


#2

Do you want a function that always calls the method on the same object such as:

// in chaiscript
myMethod(1,2) // implicitly calls f.myMethod(1,2);

-or-

// in chaiscript
f.myMethod(1,2) // explicitly calling the method `myMethod` on the object f

-Jason


#3

Right now I want the first.

The second version would actually offer some tantalizing functionality to our app that I hadn’t considered so in fact I’d love to see how to do both.

My guess is that once you show me, I’ll kick myself — I’m hating that my C++ skills have gotten so rusty!

Really appreciate your responding though.

Cheers,
D


#4

First, the bound method

// c++
class foo
{
  public: 
    int myMethod(int i, int j);
};

int main()
{
  // we have a chaiscript and we have a Foo, so let's use a Lambda and pass that to chaiscript
  foo f;
  chai.add(chaiscript::fun([&f](int i, int j){ return f.myMethod(i,j); }, "myBoundMethod");
  chai.eval("myMethod(1,2)"); // calls f.myMethod(1,2)
}

I believe that covers both options.
Or, if you want the user to be able to actually call the method explicitly

// c++
class foo
{
  public: 
    int myMethod(int i, int j);
};

int main()
{
  // we have a chaiscript and we have a Foo, so let's use a Lambda and pass that to chaiscript
  foo f;
  chai.add(chaiscript::fun(static_cast<void (foo::*)(int, int)>(&foo::myMethod)), "myMethod");  // add method
  chai.add(chaiscript::var(&f), "f"); // add object
  chai.eval("f.myMethod(1,2)"); // call method
}

#5

Thank you so much - the lambda version works perfectly fine as does the other mechanism where I didn’t realize that I had to pass in the instance as a separate variable. Much as I dislike macros, I found it convenient to make one to encapsulate the second mechanism so as to avoid having to manually write all that template stuff.
E.g

#define InjectInstanceMethod(returntype, classname, methodname, argtype, objectname)
chai.add(chaiscript::fun(static_cast<returntype (classname::*)argtype>(&classname::methodname)), #methodname);
chai.add(chaiscript::var(&objectname), #objectname);

so that one can just write

InjectInstanceMethod (int, foo, myMethod, (int, int), f)

which is easier to write/understand/read (IMO)

(The ordering of args to that macro might need to be changed to suit one’s personal tastes :slight_smile:


#6

I’ve avoided adding macros to ChaiScript, but they certainly can be helpful for cases like this.


#7

Is there a way to use non-lambda version to specify the declaration for a specific instance?

In other words, something like the second method where all types (including return type) are defined explicitly but it applies to whatever ‘this’ from which it was invoked?


#8

If I understand you correctly, you could do:

// c++
class foo
{
  public: 
    int myMethod(int i, int j);
};

int main()
{
  foo f;
  // note the trailing return type ->int in the lambda declaration
  chai.add(chaiscript::fun([&f](int i, int j)->int{ return f.myMethod(i,j); }, "myBoundMethod");
  chai.eval("myMethod(1,2)"); // calls f.myMethod(1,2)
}

or you could define your own C++ function that access some global or something, but I don’t recommend that.

I guess if this doesn’t answer your question I need to ask what specific objection do you have? Implicitly defined return types, the use of a lambda?

-Jason


#9

It’s not a big deal - I’ve simply never been a fan of anonymous functions but in this case the body of the anonymous function is probably never going to be more than a single method call.

I believe I have gotten to the point where it’s clear to me that ChaiScript will in fact do the job that I need so I’m very appreciative of all the work you have put into this project. I do wish I could reload a script and simply have redefined functions updated but I can live with simply destroying/creating a new interpreter.


#10

I’ve noticed a trend where anytime I make things more lax in the interpreter I get push back from users (like implicitly defined member values). So I’m considering making the “is redefining a function an error” an option for ChaiScript, so it can go either way depending on the user.

Regarding C++11 lambdas: they are extremely efficient both at compile time and runtime. In many cases more efficient than a free function (I believe it’s because they don’t require linkage in the same way and can be easily inlined). Just FYI.


#11

I don’t think this is around wanting to be more lax. Consider perhaps that there is a difference between the notion of declaring a function twice in a script (that would be redefining a function and should always be an error) and the concept of reloading a script where the whole point is generally to replace what what there with updated versions. I suspect that the reason this is an issue for ChaiScript is because loading a script is viewed as adding the contents of a script to what is already there. This doesn’t just apply to functions. If I loaded script that has a global statement in it, var x = 42 and then I edit that script so that we now have var x = 56, will that also cause an error due to ‘x’ being redeclared?

As for lambdas being more efficient, that may be true but my guess is that on a modern machine that improvement is measured in microseconds (or less) and so unless you’re inside a deeply nested ‘for’ loop where the saving MAY be obvious, most of the time I would view that efficiency argument as one of premature optimization. I would go with readability first, measure the code and only optimize if measurements indicated a critical bottleneck there.


#12

I accept the idea of “reloading” a script, I’ll have to give that more thought, and might solve the quandary I’m having.

Regarding lambdas and efficiency, I’ve actually given an entire talk on how their proper use can not only more efficient, but more readable, and in some cases should be what we do by default to avoid “premature pessimization” (to quote Herb Sutter)


#13

Cool – I look forward to seeing that.

As for lambdas, I don’t want to start a religious war :slight_smile: Languages like Haskell that support proper functional programming do lambdas “right”. To me, the C++ implementation feels like it was hacked in. I’ve found them useful to emulate nested methods (much like what Pascal supported) to keep code from becoming inappropriately global. However, I don’t like them as a mechanism to write anonymous functions, they seem to preempt the whole notion of using named functions (instead of just inlining the code of a function) to properly encapsulate abstraction.