Получить типы параметров функции 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, вот немного упрощенная версия:

  1. Сначала мы определяем вспомогательную метафункцию для хранения типов аргументов функции в виде кортежа.

            
    template<typename Sig>
    struct signature;
    template<typename Ret, typename...Args>{
        using type=tuple<Args...>;
    };
    
  2. Мы используем концепцию для ограничения ввода как функции

            
    template
    concept is_fun=is_function_v<Fun>;
    
  3. Вот наша функция «аргументы» для получения типов входных аргументов. В зависимости от входного параметра мы перегружаем функцию «аргументы», чтобы принимать как ссылку, так и не ссылку. (свободная функция всегда передается по ссылке. Нам даже не нужно иметь тело функции, достаточно только возвращаемого типа, так как это метафункция.

            
    template<is_fun T> 
    auto arguments(const T& t)->signature<T>::type;
    
  4. Вот тестирование:

              
    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);

Увеличить документ

Возможно, все может быть намного проще, но это полный пример, показывающий

  1. Что возвращает функция
  2. Количество параметров функции
  3. Имя типа каждого из этих параметров

(Протестировано с использованием 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();
    }
Другие вопросы по тегам