Created
July 6, 2015 09:16
-
-
Save mbasaglia/d74b996ad619fd538e2e to your computer and use it in GitHub Desktop.
An example of conversions of run-time value to compile-time objects used in function calls
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* \file | |
* \brief An example of conversions of run-time value to compile-time objects used in function calls | |
*/ | |
#include <iostream> | |
#include <vector> | |
#include <stdexcept> | |
#include <type_traits> | |
/** | |
* \brief Stores any value | |
*/ | |
using Value = std::string; | |
/** | |
* \brief Arguments passed to a function | |
*/ | |
using Arguments = std::vector<Value>; | |
/** | |
* \brief Base template to convert run-time values to the given type | |
*/ | |
template<class T> | |
T convert(const Value& v) | |
{ | |
throw std::domain_error("Invalid conversion requested"); | |
} | |
/** | |
* \brief Specialized conversion to int | |
*/ | |
template<> | |
int convert<int>(const Value& v) | |
{ | |
return std::stoi(v); | |
} | |
/** | |
* \brief Specialized conversion to double | |
*/ | |
template<> | |
double convert<double>(const Value& v) | |
{ | |
return std::stod(v); | |
} | |
/** | |
* \brief Specialized conversion to string | |
*/ | |
template<> | |
Value convert<Value>(const Value& v) | |
{ | |
return v; | |
} | |
namespace detail { | |
/** | |
* \brief Dummy class to a get compile-time sequence of indices from a parameter pack | |
* \tparam Indices Sequence if indices from 0 to sizeof...(Indices) | |
*/ | |
template <int... Indices> | |
struct IndexPack {}; | |
/** | |
* \brief Dummy class that builds an integer pack from a single integer | |
* \param N The number which needs to be converted to a pack | |
* \param Indices Pack of indices for \c IndexPack, starts out empty | |
*/ | |
template <int N, int... Indices> | |
struct IndexPackBuilder : IndexPackBuilder<N-1, N-1, Indices...> {}; | |
/** | |
* \brief Termination for \c IndexPackBuilder | |
*/ | |
template <int... Indices> | |
struct IndexPackBuilder<0, Indices...> : IndexPack<Indices...> {}; | |
/** | |
* \brief Helper for \c call(), uses the \c IndexPack to extract the arguments | |
* \tparam Ret Return type | |
* \tparam Args Function parameter types | |
* \tparam Indices Pack of indices for \c Args, deduced by the dummy parameter | |
*/ | |
template<class Ret, class... Args, int... Indices> | |
Ret call_helper(Ret(*func)(Args...), const Arguments& args, IndexPack<Indices...>) | |
{ | |
if ( args.size() != sizeof...(Args) ) | |
throw std::invalid_argument("Wrong number of arguments"); | |
return func(convert<Args>(args[Indices])...); | |
} | |
/** | |
* \brief Dummy function to find the return type of a pointer to member function | |
* (Not needed in C++14) | |
*/ | |
template <class Class, class Ret, class... Args> | |
Ret member_return(Ret(Class::*)(Args...) const); | |
template <class Class, class Ret, class... Args> | |
Ret member_return(Ret(Class::*)(Args...)); | |
/** | |
* \brief Dummy function to build an index pack for the parameters of a pointer to member function | |
*/ | |
template <class Class, class Ret, class... Args, class Index = IndexPackBuilder<sizeof...(Args)>> | |
Index member_index(Ret(Class::*)(Args...) const){ return {}; } | |
template <class Class, class Ret, class... Args, class Index = IndexPackBuilder<sizeof...(Args)>> | |
Index member_index(Ret(Class::*)(Args...)){ return {}; } | |
/** | |
* \brief Helper for \c call(), uses the \c IndexPack to extract the arguments | |
* \tparam Ret Return type | |
* \tparam Args Function parameter types | |
* \tparam Indices Pack of indices for \c Args, deduced by the dummy parameter | |
*/ | |
template<class Functor, class Ret, class... Args, int... Indices> | |
Ret call_functor_helper(const Functor& functor, const Arguments& args, | |
Ret(Functor::*)(Args...) const, IndexPack<Indices...>) | |
{ | |
if ( args.size() != sizeof...(Args) ) | |
throw std::invalid_argument("Wrong number of arguments"); | |
return functor(convert<Args>(args[Indices])...); | |
} | |
template<class Functor, class Ret, class... Args, int... Indices> | |
Ret call_functor_helper(Functor& functor, const Arguments& args, | |
Ret(Functor::*)(Args...), IndexPack<Indices...>) | |
{ | |
if ( args.size() != sizeof...(Args) ) | |
throw std::invalid_argument("Wrong number of arguments"); | |
return functor(convert<Args>(args[Indices])...); | |
} | |
} // namespace detail | |
/** | |
* \brief Calls a function pointer with the given vector as arguments | |
*/ | |
template<class Ret, class... Args> | |
Ret call(Ret(*func)(Args...), const Arguments& args) | |
{ | |
return detail::call_helper(func, args, detail::IndexPackBuilder<sizeof...(Args)>{}); | |
} | |
/** | |
* \brief Calls a functor with the given vector as arguments | |
*/ | |
template<class Functor, class = typename std::enable_if<std::is_member_function_pointer<decltype(&Functor::operator())>::value>::type> | |
decltype(detail::member_return(&Functor::operator())) | |
call(const Functor& func, const Arguments& args) | |
{ | |
return detail::call_functor_helper(func, args, &Functor::operator(), detail::member_index(&Functor::operator())); | |
} | |
template<class Functor, class = typename std::enable_if<std::is_member_function_pointer<decltype(&Functor::operator())>::value>::type> | |
decltype(detail::member_return(&Functor::operator())) | |
call(Functor& func, const Arguments& args) | |
{ | |
return detail::call_functor_helper(func, args, &Functor::operator(), detail::member_index(&Functor::operator())); | |
} | |
void foo(int i, std::string s) | |
{ | |
std::cout << "foo(" << i << ',' << s << ")\n"; | |
} | |
int main() | |
{ | |
call(foo, {"123","bar"}); | |
auto lambda = [](std::string s, double d) | |
{ | |
std::cout << "lambda(" << s << ',' << d << ")\n"; | |
return 5; | |
}; | |
call(lambda, {"hello","1.23"}); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment