4

I'm trying to write a generic wrapper (for some script interpreter) for function/class method that would convert all call parameters from string to some arbitrary type T. I'll try to cover topic in points:

  • Script allows to map user function
  • When interpreter tries to process user function - a callback routine is made
  • Callback is intended to take array of objects that describe (one-by-one) arguments' values
  • I already got (template) routines that converts string to arbitrary (basic) type T
  • I would like to wrap user routine (provided externally as variadic std::function<> type) so that conversion from subsequent strings from callback's array to appropriate argument is done automatically

Example:

Prototype for callback routine is as follows:

int CallbackFn(Interp *interp, int argc, const char **argv)

I got (sample) user function:

int UserRoutine(const std::string &in_str, int x);

so std::function would look like:

std::function<int(const std::string&, int)>

Generic conversion routine has syntax:

template <typename T>
T conv(const char *str);

I have specializations that convert:

  1. "const char*" to "std::string"
  2. "const char*" to "int"

so that ideally conversion would look like:

std::string p0 = conv<std::string>(argv[0]);
int p1 = conv<int>(argv[1]);

It could be wrapped all into variadic templates, but std::function<...> arguments do not exactly match the types i'm preparing - e.g. it is very common to pass objects as const T&, while i need to create "pure" type of T.

Any ideas how to handle different means of passing arguments?

raku99
  • 41
  • 1

1 Answers1

4

The first problem is that you are reinventing the wheel, to be honest. The default conversion function is operator>> (istream&, T&). Stuff each argument in a std::stringstream. And obviously, operator<< for the return type.

As you correctly note, you use variadic templates. But I wouldn't bother with generating std::function<int(const std::string&, int)> here. Instead, you always generate the common type std::function<std::string(std::string)>. Each packaged function contains the right argument conversion.

This gives us the declaration

template<typename RET, typename Args...>
std::function<std::string(std::string) (RET (*fptr)(Args...));

The body roughly has to look roughly like

std::tuple<Args...> args;
std::istringstream iss(fromScript);
iss>>args;
std::ostringstream oss;
oss << *fptr(args.get<0>, args.get<1>(), ...);
return oss.str();

For that tricky call to *fptr, see C++11: I can go from multiple args to tuple, but can I go from tuple to multiple args?

[edit] I just noted one bit I missed: " arguments do not exactly match the types i'm preparing - e.g. it is very common to pass objects as const T&, while i need to create "pure" type of T.". I suspect you're looking for std::decay<Arg>.

MSalters
  • 159,923
  • 8
  • 140
  • 320
  • Thanks a lot for the answer :) !! Seems i need to get more familiar with C++11... and focus on class string conversion. – raku99 Mar 26 '18 at 10:14
  • Thanks :) That's another thing i wasn't aware of:) Now i'm trying to wrap all the stuff into a class - i'd like to have "mapped function descriptors" that would hold std::function + name + pointer to interpreter etc. I got it templated for and have std::function with same RET/ARGS... what about pointer to member? I know there is std::bind that can wrap std::function together with object, but can i somehow declare result of std::bind? Or should i just declare another template with additional "typename OBJ" parameter that holds also pointer to the object? – raku99 Mar 27 '18 at 11:40
  • @raku99: A pointer to member is a function with N+1 arguments, the +1 being a hidden `this` parameter. You can indeed use `std::bind` to reduce the arity of a function. The return type is unspecified, since it should not matter. You seem to have the `std::function` and the `std::bind` reversed. The object returned from `std::bind` usually goes into a `std::function`. But your talk about "std::function with the same RET/ARGS" makes me suspect that we are not talking about the same approach anyway. The whole point of `std::function` is type erasure, and you are not doing that. – MSalters Mar 27 '18 at 13:25
  • Sorry for confusion - seems i need to learn a bit before attempting to use std::function and std::bind. Thanks for pointing it out :) !! – raku99 Mar 27 '18 at 13:47