2

I'm looking for a way to implement a function class

template<class ValueType> class Function { ... }; 

which can be constructed with a function pointer (or functor) that takes any number of arguments of type ValueType and returns ValueType. For example, given these functions:

double foo(double); double bar(double, double); int baz(double, int); 

a Function<double> object could be constructed with either foo or bar but not baz.

Then, a member function call will, given some container of ValueType (or an iterator), call the underlying function with the right number of arguments at runtime.

Is such thing possible?

7
  • This is going to be quite difficult, because you're asking that a single object be able to transparently handle at runtime completely different function signatures. It would be much easier if the number of arguments was part of the template arguments so that you can solve most of the difficulties at compile time (with template helpers), but then it defeats your purpose I guess. Commented Sep 7, 2013 at 9:12
  • 1
    I wonder what the use case for this is. I withdrew my reply for now until I get more clarification about it. Commented Sep 7, 2013 at 9:19
  • @Laszlo Papp I'm writing an expression evaluator. I want to allow user-defined functions to be registered in the evaluator. Therefore, I need a general function class which can hold any function of the allowed form, and call it with the right number of arguments at run-time. Commented Sep 7, 2013 at 9:27
  • Let me ask it more clearly: why do you need this expression evaluator? Commented Sep 7, 2013 at 9:29
  • 1
    IIUC you need to handle functions that have unspecified number of arguments of unspecified type and that information is only known at runtime. Besides fiddling with C-style varargs, which are not type-safe, you are looking for something that is closer to dynamic languages. If this fits your bill you could consider to embed Lua in your code, it has been designed for easy integration with C (especially) and C++ code. Commented Sep 7, 2013 at 10:12

2 Answers 2

3

It is indeed possible, you just have to "recursively" unpack arguments from the container and pass them to the function on the deepest level:

#include <cstddef> #include <utility> #include <stdexcept> #include <functional> #include <type_traits> #include <vector> #include <iostream> namespace detail { template <std::size_t argument_count> struct arguments_unpacker { template <typename Type, typename Function, typename InputIterator, typename... UnpackedArguments> static Type unpack(Function&& function, InputIterator arguments_begin, InputIterator arguments_end, UnpackedArguments&&... unpacked_arguments) { if (arguments_begin == arguments_end) { throw std::invalid_argument("Not enough arguments."); } return arguments_unpacker<argument_count - 1>::template unpack<Type>(std::forward<Function>(function), std::next(arguments_begin), arguments_end, std::forward<UnpackedArguments>(unpacked_arguments)..., *arguments_begin); } }; template <> struct arguments_unpacker<0> { template <typename Type, typename Function, typename InputIterator, typename... UnpackedArguments> static Type unpack(Function&& function, InputIterator arguments_begin, InputIterator arguments_end, UnpackedArguments&&... unpacked_arguments) { if (arguments_begin != arguments_end) { throw std::invalid_argument("Too many arguments."); } return function(std::forward<UnpackedArguments>(unpacked_arguments)...); } }; template <typename MemberFunction> struct member_function_arity; template <typename Result, typename Class, typename... Arguments> struct member_function_arity<Result(Class::*)(Arguments...)> { static constexpr std::size_t value = sizeof...(Arguments); }; template <typename Result, typename Class, typename... Arguments> struct member_function_arity<Result(Class::*)(Arguments...) const> { static constexpr std::size_t value = sizeof...(Arguments); }; template <typename Function> struct function_arity : member_function_arity<decltype(&Function::operator())> {}; template <typename Result, typename... Arguments> struct function_arity<Result(*)(Arguments...)> { static constexpr std::size_t value = sizeof...(Arguments); }; template <typename Result, typename... Arguments> struct function_arity<std::function<Result(Arguments...)>> { static constexpr std::size_t value = sizeof...(Arguments); }; } template <typename Type, typename InputIterator, typename Function> std::function<Type(InputIterator, InputIterator)> variate(Function function) { using namespace detail; return [function](InputIterator arguments_begin, InputIterator arguments_end) { return arguments_unpacker<function_arity<Function>::value>::template unpack<Type>(function, arguments_begin, arguments_end); }; } namespace demo { double a(double x0) { std::cout << "a(" << x0 << ")\n"; return 0.0; } double b(double x0, double x1) { std::cout << "b(" << x0 << ", " << x1 << ")\n"; return 0.0; } double c(double x0, double x1, double x2) { std::cout << "b(" << x0 << ", " << x1 << ", " << x2 << ")\n"; return 0.0; } auto l = [](double x0) mutable { std::cout << "l(" << x0 << ")\n"; return 0.0; }; void run() { using it = std::vector<double>::const_iterator; auto va = variate<double, it>(&a); auto vb = variate<double, it>(&b); auto vc = variate<double, it>(&c); auto vl = variate<double, it>(l); std::vector<double> a1 = {1.0}; std::vector<double> a2 = {1.0, 2.0}; std::vector<double> a3 = {1.0, 2.0, 3.0}; va(begin(a1), end(a1)); vb(begin(a2), end(a2)); vc(begin(a3), end(a3)); vl(begin(a1), end(a1)); } } int main() { demo::run(); return 0; } 

Note that this requres explicitly supplying iterator type. I don't see how it would be possible to remedy that without writing some kind of type erasing any_iterator.

Sign up to request clarification or add additional context in comments.

5 Comments

Wow! That's awesome. Looks like dark magic to me, but still awesome :). Answer accepted!
It fails to variate lambdas. auto vd = variate<double, it>([](double) { return double(); }); won't compile. Any ideas?
Ended up using function_traits instead of function_arity. Lambdas work now! :)
@Avidanborisov You are right, fixed code accordingly. Also note the specialization for mutable lambdas, which function_traits doesn't cover, though it is mentioned in comments.
Given the amount of contortions you have to go through to accomplish this, you should really rethink whether C++ is the right tool here. Lorenzo already suggested lua which can solve this in a much more maintainable way. But +1 for the crazy template black voodoo.
1
#include <functional> #include <boost/any.hpp> template<class ValueType> struct Function { template <typename ...Args> Function(const std::function<ValueType(Args...)>& f) : fn(f) {} template <typename ...Args> ValueType operator () (Args... args) { auto f = boost::any_cast<const std::function<ValueType(Args...)>&>(fn); return f(args...); } boost::any fn; }; int a(int) { return 1; } int b(int, double) { return 2; } int main(int argc, char** argv) { typedef std::vector<Function<int>> Functions; Functions functions { std::function<int(int)>(a), std::function<int(int, double)>(b) }; std::cout << functions[0](1) << functions[1](1, 2.0) << std::endl; } 

2 Comments

I can't just invoke operator() on Function. I need to call the function with arguments extracted from some container at runtime.
@Avidanborisov The whole problem is indeed passing an arbitrary number of parameters to the function at runtime without knowing at compile time how many parameters you need to deal with. How can you call a function if you don't know its signature at compile time?

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.