Skip to content

Instantly share code, notes, and snippets.

@mbasaglia
Created July 6, 2015 09:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mbasaglia/d74b996ad619fd538e2e to your computer and use it in GitHub Desktop.
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
/**
* \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