Registering method with packed argument


#1

It would be fantastic to be able to register a method with a packed argument.
To get a little more into my scenario.

Lets say i have a std::set member on a class.
I figured out how to setup a method to unpack them and add to the container.

// C++
auto inst = Foo();
inst.setProps(ENUM_X, ENUM_Y, ENUM_Z); // this works with regular c++

what i need is to figure out how to tell chaiscript about the packed argument

...
engine.add(chaiscript::fun<void(Enum...)>(&Foo::setProps), "setProps");


// Chaiscript
...
inst.setProps(ENUM_X, ENUM_Y, ENUM_Z);

#2

Can you clarify what the signature of setProps is in your C++ code?


#3

I went with the following

// C++
template <typename ...Args>
auto setProps(Args&&... args) -> bool {
	return expand_impl(m_container, std::forward<Args>(args)...);
}
 
template <typename T>
void expand_impl(T) { }
 
template <typename T, typename ...Tail>
void expand_impl(T& container, const Color& head, Tail&&... tail) {
	container.emplace(head);
	expand_impl(container, std::forward<Tail>(tail)...);
}

I hope that helps.
Maybe to clarify what i am trying to do with this.
My first approach was to make an alias for std::vector and expose that to chaiscript in the script code i would then construct a temporary vector templated onto the enum and push_back the values and then pass that to setProps for example.
The C++ code would then loop over that vector and copy over the values, A unpacking solution would be very much cleaner and i believe other people will benefit from that approach if it was decently documented.
One alternative i could think of is to get uniform initialization syntax going inside chaiscript and have it be the same as we have with C++11.

From what i understand using

//chaiscript
var vec = [3, 5, 7];

only refers to a vector collection capable of holding known primitives and not a collection of values in a more general sense.


#4

Something like uniform initialization syntax might be workable, but trying to generically unpack a variadic template function is impossible in C++, since the function doesn’t actually exist until you instantiate it.

It might be possible to give it a set of expansions to work from, or some guidance, like maybe something like:

chai.add(template_fun<somevariadicfunction, void (int), void (int, int), void (int, double, float)>("somevariadicfunction"));

Which would take the function template and register it with the 3 different signatures provided

or maybe

chai.add(expanded_template_fun<somevaridacfunction, int, 2, 5>("somevariadicfunction"));

Which would take the variadic function template and register it 4 times (from 2 to 5 parameters), expanded out like:

void (int, int)
void (int, int, int)
void (int, int, int, int)
void (int, int, int, int, int)

This statement basically describes exactly how ChaiScript works internally. A vector of Boxed_Value that is tested against the expected C++ types in the known C++ function calls that are registered.

That understand is incorrect. You can store any type of value in a vector, and they don’t have to be homogeneous.

var vec = [1, 3.4, "bob", fun(x){ x+2 }, async(fun(){ 34+5 })]
// int, double, string, function object, std::future<Boxed_Value>

That code should be perfectly legitimate (assuming no parser errors ;))

I think you might risk accidentally re-implementing ChaiScript if you try too hard here, and should instead see what parts you can implement in ChaiScript itself.

I cannot help but think that your setProps code seems a bit over-engineered since you know ahead of time (based on m_container) what the types needed will be. In fact, in expand_impl you limit yourself specifically to Color objects.

But I’m probably missing the bigger picture here.

But if I do understand correctly, you could probably do something like this:

// cpp
void setProps(const std::vector<Color> &vec) {
  for(const auto &c : vec) {
    m_container.push_back(c);
  }
}

// somewhere else
chai.add(chaiscript::vector_conversion<std::vector<Color>>()); // maybe this vector conversion is what you were missing??
chai.add(chaiscript::fun(&setProps), "setProps");
// chaiscript
setProps([Color(255,255,255), Color(255,0,0)]);

-Jason


#5

This is exactly what i am already doing.
I’m not trying to re-implement Chaiscript but rather figure out how to best use it as flexible as possible. :wink:
Though since you already saw the Color Enum together with my bulk enum registration pull request.

when i try to call

inst.setProps([red])

an exception is thrown

Error: "Can not find appropriate 'clone' or copy constructor for vector elements" during evaluation...

I expect that is because the enum doesn’t have the required methods.
I would really like to stick with my enum approach since it makes using human readable values accessible inside the script.


#6

Aaah. That should be fixable, but it will take a little bit of research. You literally need to add a copy constructor for your enum type, as if it were any other class. This is something we should be able to make happen almost completely automatically.


#7

Ok, I’m definitely confused… what type is red in your example? Is it an enum? If so - I’m unable to reproduce the issue your are describing.

-Jason


#8

Here is a small testcase.

int main(int argc, char **argv) {
 
	enum Color {
		blue,
		red,
	};
 
	try {
		chaiscript::ChaiScript engine(chaiscript::Std_Lib::library());
 
		engine.add(chaiscript::bootstrap::standard_library::vector_type<std::vector<Color>>("ColorVector"));
		engine.add(chaiscript::vector_conversion<std::vector<Color>>());
 
		engine.add(chaiscript::const_var(blue), "blue");
		engine.add(chaiscript::const_var(red), "red");
		engine.add(chaiscript::user_type<Color>(), "Color");
		engine.eval("return [red]");
	}
	catch (const chaiscript::exception::eval_error &e) {
		std::cout << "Error\n" << e.pretty_print() << '\n';
	}
	return 0;
}