Problem
Given any function (or callable) type Function, how can I get its all arguments types as a tuple type ?
For example, I need a trait function_traits<Function>::arguments, where:
int f();
typename function_traits<decltype(f)>::arguments // => gives me std::tuple<>
void g(int);
typename function_traits<decltype(g)>::arguments // => gives me std::tuple<int>
void h(int, int);
typename function_traits<decltype(h)>::arguments // => gives me std::tuple<int, int>
My thought
First
I need to get the size of arguments, fortunately boost already has implemented function_traits<F>::arity
Then
Generate an std::integer_sequence from 1 to artify, map it to arguments type, but here comes the problem, to map integer_sequence, I need something like this:
function_traits<F>::arg_type<N> // -> N-th arg_type
but boost only provides this:
function_traits<F>::argN_type
Question
How can I implement function_traits<F>::arg_type<N> ? I can use c++ standard up to c++17
Something like this:
#include <tuple>
template<typename x_Function> class
function_traits;
// specialization for functions
template<typename x_Result, typename... x_Args> class
function_traits<x_Result (x_Args...)>
{
public: using arguments = ::std::tuple<x_Args...>;
};
usage example:
#include <type_traits>
int foo(int);
using foo_arguments = function_traits<decltype(foo)>::arguments;
static_assert(1 == ::std::tuple_size<foo_arguments>::value);
static_assert(::std::is_same_v<int, ::std::tuple_element<0, foo_arguments>::type>);
online compiler
Is too late to play?
You can use C++17 so... what about using std::function deduction guides?
template <typename T>
struct function_traits
{
template <typename R, typename ... As>
static std::tuple<As...> pro_args (std::function<R(As...)>);
using arguments = decltype(pro_args(std::function{std::declval<T>()}));
};
The following is a full compiling example
#include <tuple>
#include <functional>
#include <type_traits>
int f ();
void g (int);
void h (int, int);
template <typename T>
struct function_traits
{
template <typename R, typename ... As>
static std::tuple<As...> pro_args (std::function<R(As...)>);
using arguments = decltype(pro_args(std::function{std::declval<T>()}));
};
int main ()
{
static_assert(std::is_same_v<std::tuple<>,
function_traits<decltype(f)>::arguments>);
static_assert(std::is_same_v<std::tuple<int>,
function_traits<decltype(g)>::arguments>);
static_assert(std::is_same_v<std::tuple<int, int>,
function_traits<decltype(h)>::arguments>);
}
Related
Suppose I have a template function:
template<class T>
void whenMatchesType(std::function<void(T*)> action) { ... }
I might invoke this like so:
anObject.whenMatchesType<SomeType>([=](SomeType *value) {
// ...
});
Although C++ is capable of inferring template parameters from arguments of simple, non-template types, I don't seem to be able to omit explicitly specifying the type (as <SomeType>) in this case - even though it is provided as a type parameter to the first argument.
Is there some change to my code - or to my compilation - through which I might avoid this redundancy?
If you need acces to the parameter type you can still take in the callable as it's own template parameter, then use type traits to extract the information.
Here is a simple example.
#include <iostream>
#include <functional>
template <typename T>
struct parameter_type;
template <typename ReturnType, typename ParameterType>
struct parameter_type<std::function<ReturnType(ParameterType)>> {
using ParameterT = ParameterType;
};
template <typename T>
using param_t = typename parameter_type<decltype(std::function{std::declval<T>()})>::ParameterT;
template<class T>
void whenMatchesType(T) {
using Parameter = param_t<T>;
static_assert(std::is_same_v<Parameter, int>, "Only callables that take int as parameter is allowed");
}
int main() {
whenMatchesType([](int){});
//whenMatchesType([](double){});
}
In C++, it's possible to use a type as a template parameter, e.g:
template <typename T>
void MyFn();
It's also possible to use a non-type as a template parameter in some cases, e.g.:
template <int64_t T>
void MyFn2();
My question is whether it's possible to have a "generic" template parameter that can be both? Like:
template <TypenameOrint64_t T>
void MyFn3();
such that both MyFn3<42> and MyFn3<double> would be acceptable.
An example of how I might use this:
template <typename ValType, ValType Head, ValType ...Tail>
struct ListS{
template <typename OutType, template <ValType ArgType> class Fn>
using MapHead = ListS<OutType, Fn<Head>::val, Tail...>;
};
template<int64_t N>
struct SquareS{
static constexpr const int64_t val = N * N;
};
using Sqrd = ListS<int64_t, 3, 4>::MapHead<int64_t, SquareS>;
static_assert(std::is_same<Sqrd, ListS<int64_t, 9, 4>>::value, "Values don't match");
The above is a very rough sketch of a compile-time list of values along with a single compile-time "function" on it. Would it be possible to make something like that also support lists of types, not just lists of non-type template param compatible values, without just duplicating all the code?
Is it possible to have a “generic” template parameter in C++, that can be either a non-type template parameter or a type?
Short answer: no.
Long answer.
No. The best I can imagine to mix types and values is wrap values in types, using std::integral_constant, by example.
So, your desired code, could be written (C++17) almost as follows
#include <utility>
template <typename ...>
struct ListS;
template <typename ValType, ValType Head, ValType ...Tail>
struct ListS<std::integral_constant<ValType, Head>,
std::integral_constant<ValType, Tail>...>
{
template <template <auto> class Fn, typename OutType = ValType>
using MapHead = ListS<std::integral_constant<OutType, Fn<Head>::value>,
std::integral_constant<OutType, Tail>...>;
};
template <auto N>
struct SquareS : public std::integral_constant<decltype(N), N*N>
{ };
int main ()
{
using T1 = ListS<std::integral_constant<long long, 3ll>,
std::integral_constant<long long, 4ll>>;
using T2 = T1::MapHead<SquareS>;
using T3 = ListS<std::integral_constant<long long, 9ll>,
std::integral_constant<long long, 4ll>>;
static_assert( std::is_same_v<T2, T3> );
}
Pre C++17 you can't use auto for the type of the template values so you should make some simple corrections.
You could use function overloading and the auto type deduction that came with C++17
to accomplish something similar.
template<typename myType>
auto myFn3(myType value){
return value;
}
template<auto value> //takes any non-type parameter
auto myFn3(){
return value;
}
int main(){
auto test1_normal = myFn3(3);
auto test1_cast = myFn3<double>(3); //able to perform a cast
auto test1_auto = myFn3<3>();
return 0;
}
Having a list of types as a variadic template argument, it's pretty easy to perform arbitrary type manipulation on them, to get a tuple of modified types as a result. E.g. to wrap each element with a custom wrapper class, one could do:
template<typename T> class Wrapper {};
template<typename ...Values>
using WrappedValues = std::tuple<Wrapper<Values>...>;
using NewTuple = WrappedValues<int, std::string, char>;
static_assert(std::is_same<NewTuple, std::tuple<Wrapper<int>, Wrapper<std::string>, Wrapper<char>>>::value, "");
How to do the same, when having a specialization of std::tuple as an "input"? E.g. what should be placed instead of "???" to make following code compileable:
template<typename T> class Wrapper {};
template<typename Tuple>
using WrappedTupleElements = ???;
using NewTuple = WrappedTupleElements<std::tuple<int, std::string, char>>;
static_assert(std::is_same<NewTuple, std::tuple<Wrapper<int>, Wrapper<std::string>, Wrapper<char>>>::value, "");
I know about the possibility to access types of tuple elements using std::tuple_element and recursive template instantiation, but I don't know how to gather types created this way into one tuple.
Preferred would be pure C++14 answer, but proposals that use C++17, widely available TSes, or external libraries (e.g. boost) are also welcome.
Why not to use additional struct template which allow specialization:
#include <string>
#include <tuple>
#include <type_traits>
template<typename T> class Wrapper {};
template<typename Tuple>
struct WrappedTupleElements;
template <class... Values>
struct WrappedTupleElements<std::tuple<Values...>> {
using type = std::tuple<Wrapper<Values>...>;
};
int main() {
using NewTuple = WrappedTupleElements<std::tuple<int, std::string, char>>::type;
static_assert(std::is_same<NewTuple, std::tuple<Wrapper<int>, Wrapper<std::string>, Wrapper<char>>>::value, "");
}
[live demo]
A common idiom (e.g. boost mpl) is to employ the concept of metafunctions.
A metafunction is a template class which declares a type called result which yields the result type of applying the metafunction on the inputs.
#include <string>
#include <tuple>
#include <type_traits>
template<typename T> class Wrapper {};
namespace metafunction
{
template<class MetaFunction> using result_of = typename MetaFunction::result;
// meta-function which yields Wrapper<Element> from Element
// type: unary metafunction
// arg1 = the type to wrap
// returns Wrapper<arg1>
//
template<class Element>
struct apply_wrapper
{
using result = Wrapper<Element>;
};
template<class Tuple, template<class> class Function>
struct transform_elements;
// meta-function which takes a tuple and a unary metafunction
// and yields a tuple of the result of applying the metafunction
// to each element_type of the tuple.
// type: binary metafunction
// arg1 = the tuple of types to be wrapped
// arg2 = the unary metafunction to apply to each element_type
// returns tuple<result_of<arg2<element>>...> for each element in arg1
template<class...Elements, template<class> class UnaryMetaFunction>
struct transform_elements<std::tuple<Elements...>, UnaryMetaFunction>
{
template<class Arg> using function = UnaryMetaFunction<Arg>;
using result = std::tuple
<
result_of<function<Elements>>...
>;
};
}
int main() {
using input_type = std::tuple<int, std::string, char>;
using namespace metafunction;
using wrapped_tuple = result_of<transform_elements<input_type, apply_wrapper>>;
static_assert(std::is_same<wrapped_tuple, std::tuple<Wrapper<int>, Wrapper<std::string>, Wrapper<char>>>::value, "");
}
Is there a standard way to get the types of a function's arguments and pass around these types as a template parameter pack? I know that this is possible in C++ because it has been done before.
I was hoping that with C++14 or the upcoming C++1z, there would be an idiomatic way to implement arg_types<F>... here:
template <typename ...Params>
void some_function(); // Params = const char* and const char*
FILE* fopen(const char* restrict filename, const char* restrict mode);
int main(){
some_function<arg_types<fopen>...>();
}
Just to be clear, an answer claiming that there is no standard way to do this is not an answer. If there is no answer, I would prefer that the question remain unanswered until the solution is added to C++500 or until the heat death of the universe, whichever happens earlier :)
Edit: A deleted answer noted that I can use PRETTY_FUNCTION to get the names of parameter types. However, I want the actual types. Not the names of those types.
This syntax is slightly different.
First, because types are easier to work with than packs, a type that holds a pack. The using type=types; just saves me work in the code that generates a types:
template<class...>struct types{using type=types;};
Here is the workhorse. It takes a signature, and produces a types<?...> bundle containing the arguments for the signature. 3 steps so we can get nice clean C++14esque syntax:
template<class Sig> struct args;
template<class R, class...Args>
struct args<R(Args...)>:types<Args...>{};
template<class Sig> using args_t=typename args<Sig>::type;
Here is a syntax difference. Instead of directly taking Params..., we take a types<Params...>. This is similar to the "tag dispatching" pattern, where we exploit template function type deduction to move arguments into the type list:
template <class...Params>
void some_function(types<Params...>) {
}
My fopen is different, because I don't want to bother #includeing stuff:
void* fopen(const char* filename, const char* mode);
And the syntax is not based off of fopen, but rather the type of fopen. If you have a pointer, you'd need to do decltype(*func_ptr) or somesuch. Or we could augment the top to handle R(*)(Args...) for ease of use:
template<class Sig>
struct args<Sig*>:args<Sig>{}; // R(*)(Args...) case
template<class Sig>
struct args<Sig&>:args<Sig>{}; // R(&)(Args...) case
then test code:
int main(){
some_function(args_t<decltype(fopen)>{});
}
live example.
Note that this does not work with overloaded functions, nor does it work with function objects.
In general, this kind of thing is a bad idea, because usually you know how you are interacting with an object.
The above would only be useful if you wanted to take a function (or function pointer) and pop some arguments off some stack somewhere and call it based off the parameters it expected, or something similar.
Inspired by #Yakk, here is a slightly simplified version:
First we define helper meta function to store function argment types as tuple.
template<typename Sig>
struct signature;
template<typename R, typename ...Args>
struct signature<R(Args...)>
{
using type = std::tuple<Args...>;
};
We use concept to restrict input as function
template<typename F>
concept is_fun = std::is_function_v<F>;
Here is our function "arguments" to retrieve input's argument types. Depends on input parameter, we overload "arguments" function to accept both reference and non reference.(free function is always passed by reference. We don't even have to have function body, only return type is enough as this is meta function.
template<is_fun F>
auto arguments(const F &) -> typename signature<F>::type;
Here is testing:
void foo(const string &, int, double)
{}
static_assert(std::is_same_v<decltype (arguments(foo)),
std::tuple<const string &, int, double>>);
My full-fledged version is here which also supports lambda, functor, member function pointer
Use Boost.FunctionTypes and std::index_sequence. Below is an example which prints the argument types of the function func. You can change the doit static function to do what you want. See it in action here.
template <typename FuncType>
using Arity = boost::function_types::function_arity<FuncType>;
template <typename FuncType>
using ResultType = typename boost::function_types::result_type<FuncType>::type;
template <typename FuncType, size_t ArgIndex>
using ArgType = typename boost::mpl::at_c<boost::function_types::parameter_types<FuncType>, ArgIndex>::type;
void func(int, char, double) {}
template <typename Func, typename IndexSeq>
struct ArgPrintHelper;
template <typename Func, size_t... Inds>
struct ArgPrintHelper<Func, integer_sequence<size_t, Inds...> >
{
static void doit()
{
string typeNames[] = {typeid(ResultType<Arg>).name(), typeid(ArgType<Func, Inds>).name()...};
for (auto const& name : typeNames)
cout << name << " ";
cout << endl;
}
};
template <typename Func>
void ArgPrinter(Func f)
{
ArgPrintHelper<Func, make_index_sequence<Arity<Func>::value> >::doit();
}
int main()
{
ArgPrinter(func);
return 0;
}
Headers(moved down here to reduce noise in the above code snippet):
#include <boost/function_types/function_type.hpp>
#include <boost/function_types/parameter_types.hpp>
#include <boost/function_types/result_type.hpp>
#include <boost/function_types/function_arity.hpp>
#include <algorithm>
#include <iostream>
#include <string>
#include <type_traits>
#include <typeinfo>
#include <tuple>
#include <utility>
using namespace std;
For boost users, #include <boost/type_traits.hpp>
boost::function_traits<decltype(function)>::arg1_type
boost::function_traits<decltype(function)>::arg2_type
// boost::function_traits<decltype(function)>::argN_type
using FopenArg1 = boost::function_traits<decltype(fopen)>::arg1_type;
using FopenArg2 = boost::function_traits<decltype(fopen)>::arg2_type;
void some_function(FopenArg1, FopenArg2);
Boost Document
With a C++17 (or later) conforming compiler, you can use this:
#include<iostream>
template<typename type, typename...args>
void getFuncInfo(type(*func)(args...))
{
// some code here...
// here my example:
((std::cout << typeid(args).name() << "\n"),...);
}
// every Augments you can imagines...
void someRandomFunction(int a, float b, double c, const char* d, int e[], std::pair<int, const char*> f)
{
}
// test out in main.
int main()
{
getFuncInfo(someRandomFunction);
std::cin.get();
}
I would like to know whether it is possible to define a function type that takes n arguments, just by knowing n and the type of these arguments.
For example, we could have something like :
template<int N> struct test {
typedef void(*)(E,E,E,...) fct; //E is written N times
};
In C++11 it is easy to instanciate a variadic template with N times the same type using recursivity, but I have no idea how to translate that into a valid function pointer type.
As I type those words, the solution came to me. I will answer myself, so the knowledge can help someone in the future.
If you want a simpler solution, you could just make the function take a std::array<T, N>. Initialize the array directly within the parentheses to make it look mostly like a normal function call:
foo({1, 2, 3}) // to pass a std::array<int, 3>
While I was typing I suddenly envisioned the solution in C++11.
The trick is to use a generic Fct template type and specialise it a function type using variadic template type for the argument of the function "specialisation".
template< typename Fct, typename NewArg > struct functional_append_arg;
template< typename Ret, typename... Args, typename NewArg >
struct functional_append_arg< Ret(Args...), NewArg >
{ using type = Ret(Args...,NewArg); };
template< typename Ret, typename Arg, int N> struct functional_repeat_arg;
{ using type = functional_append_arg< functional_repeat_arg<Ret,Arg,N-1>::type, Arg >::type; };
template< typename Ret, typename Arg> struct functional_repeat_arg<Ret,Arg,0>;
{ using type = Ret(); }
We can take advantage of the indices trick here again in lieu of the recursive variadic technique.
#include <cstddef>
#include <utility>
template <typename T, std::size_t N>
struct repeat {
private:
/* Map any index to T. */
template <std::size_t>
using T_ = T;
/* Function pointer that takes Ts. */
template <typename... Ts>
using Fn = void (*)(Ts...);
/* A function returning void (*)(T...). */
template <std::size_t... Is>
static Fn<T_<Is>...> impl(std::index_sequence<Is...>);
public:
/* Get the return type of impl. */
using type = decltype(impl(std::make_index_sequence<N>()));
}; // repeat
static_assert(std::is_same<repeat<int, 5>::type,
void (*)(int, int, int, int, int)>::value, "");