Получить типы параметров функции C++
Существует ли стандартный способ получения типов аргументов функции и передачи этих типов в виде пакета параметров шаблона? Я знаю, что это возможно в C++, потому что это было сделано раньше.
Я надеялся, что с C++14 или будущим C++1z найдется идиоматический способ реализации arg_types<F>...
Вот:
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>...>();
}
Просто чтобы быть ясно, ответ, утверждающий, что не существует стандартного способа сделать это, не является ответом. Если ответа нет, я бы предпочел, чтобы вопрос оставался без ответа до тех пор, пока решение не будет добавлено в C++500 или до тепловой смерти вселенной, в зависимости от того, что произойдет раньше:)
Изменить: удаленный ответ отметил, что я могу использовать PRETTY_FUNCTION
чтобы получить имена типов параметров. Тем не менее, я хочу реальные типы. Не названия этих типов.
7 ответов
Этот синтаксис немного отличается.
Во-первых, потому что с типами легче работать, чем с пакетами, тип, который содержит пакет. using type=types;
просто экономит мне работу в коде, который генерирует types
:
template<class...>struct types{using type=types;};
Вот рабочая лошадка. Он берет подпись и выдает types<?...>
комплект, содержащий аргументы для подписи. 3 шага, чтобы мы могли получить хороший чистый синтаксис C++14esque:
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;
Вот синтаксическая разница. Вместо непосредственного принятия Params...
мы берем types<Params...>
, Это похоже на шаблон "диспетчеризации тегов", где мы используем вывод типа шаблонной функции для перемещения аргументов в список типов:
template <class...Params>
void some_function(types<Params...>) {
}
мой fopen
отличается, потому что я не хочу беспокоить #include
Вещи:
void* fopen(const char* filename, const char* mode);
И синтаксис не основан на fopen
, а скорее тип fopen
, Если у вас есть указатель, вам нужно сделать decltype(*func_ptr)
или что-то подобное. Или мы могли бы увеличить верх для обработки R(*)(Args...)
для удобства использования:
int main(){
some_function(args_t<decltype(fopen)>{});
}
Обратите внимание, что это не работает с перегруженными функциями и не работает с объектами функций.
В общем, подобные вещи плохая идея, потому что обычно вы знаете, как взаимодействуете с объектом.
Вышеприведенное будет полезно, только если вы захотите взять функцию (или указатель на функцию) и вытолкнуть некоторые аргументы из некоторого стека и вызвать его на основе ожидаемых параметров или чего-то подобного.
Вдохновленный @Yakk, вот немного упрощенная версия:
Сначала мы определяем вспомогательную метафункцию для хранения типов аргументов функции в виде кортежа.
template<typename Sig> struct signature; template<typename Ret, typename...Args>{ using type=tuple<Args...>; };
Мы используем концепцию для ограничения ввода как функции
template concept is_fun=is_function_v<Fun>;
Вот наша функция «аргументы» для получения типов входных аргументов. В зависимости от входного параметра мы перегружаем функцию «аргументы», чтобы принимать как ссылку, так и не ссылку. (свободная функция всегда передается по ссылке. Нам даже не нужно иметь тело функции, достаточно только возвращаемого типа, так как это метафункция.
template<is_fun T> auto arguments(const T& t)->signature<T>::type;
Вот тестирование:
void foo(const string&, int, double){} static_assert(is_same_v<decltype(arguments(foo)),tuple<const string&, int, double>>);
Здесь находится моя полноценная версия, которая также поддерживает лямбда, функтор, указатель на функцию-член
Используйте Boost.FunctionTypes и std::index_sequence
, Ниже приведен пример, который печатает типы аргументов функции func
, Вы можете изменить doit
статическая функция, чтобы делать то, что вы хотите. Смотрите это в действии здесь.
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;
}
Заголовки (перемещены сюда, чтобы уменьшить шум в приведенном фрагменте кода):
#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;
Для пользователей Boost,
#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);
Возможно, все может быть намного проще, но это полный пример, показывающий
- Что возвращает функция
- Количество параметров функции
- Имя типа каждого из этих параметров
(Протестировано с использованием MS Visual C++ 2022)
#include <iostream>
#include <string>
template<int N, typename... Ts> using NthTypeOf =
typename std::tuple_element<N, std::tuple<Ts...>>::type;
template <int N, typename R, typename ... Types>
std::string get_arg_type(R(*)(Types ...))
{
return typeid(NthTypeOf<N, Types...>).name();
}
template <typename R, typename ... Types>
constexpr size_t get_arg_count(R(*)(Types ...))
{
return sizeof...(Types);
}
template <typename R, typename ... Types>
constexpr std::string get_return_type(R(*)(Types ...))
{
return typeid(R).name();
}
template <size_t N, size_t I, typename R, typename ... Types>
static void print_arg_type_name(R(*func)(Types ...)) {
std::cout << "Arg" << I << " Type: " << get_arg_type<I>(func) << "\n";
if constexpr (I + 1 < N) print_arg_type_name<N, I + 1>(func);
}
void f(int a, float b, double c, std::string s)
{
}
int main()
{
auto ret_type = get_return_type(f);
std::cout << "Return Type: " << ret_type << "\n";
constexpr size_t N = get_arg_count(f);
std::cout << "Number of Args: " << N << "\n";
print_arg_type_name<N, 0>(f);
}
Спустя годы, но смотрите мое полное решение здесь (производственный уровень, полностью документированный). Например, вам нужен второй аргумент некоторой функции «F» (второй аргумент шаблона начинается с нуля):
using Arg2Type_t = ArgType_t<F, 1>;
Хотите, чтобы его удобное имя было представлено в виде строки (std::basic_string_view):
constexpr auto Arg2TypeName = ArgTypeName_v<F, 1>;
Хотите, чтобы все его (невариативные) аргументы соответствовали вашему первоначальному вопросу (хотя обычно немногим понадобится прямой доступ к ним). Также есть функция для их перебора и вызова собственного функтора для каждого типа аргумента (см. «Цикл по всем аргументам функции» в приведенной выше ссылке):
using ArgTypes = ArgTypes_t<F>;
Среди прочего (количество аргументов, тип возвращаемого значения, cv-квалификаторы нестатических функций-членов и т. д.)
Обратите внимание, что «F» может быть любым необработанным типом функции C++, указателем на тип функции, ссылкой на тип функции (за исключением ссылок на нестатические функции-члены, которые недопустимы в C++), ссылкой на указатель на тип функции и типами функторов. (включая лямбды).
С соответствующим компилятором С++17 (или более поздней версии) вы можете использовать это:
#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();
}