Как проверить типы параметров функции?

У меня есть приложение, в котором я создаю функцию marshal_and_apply, которая вызывает другую функцию (или функтор) f с некоторыми аргументами. Задача marshal_and_apply заключается в применении специального маршалинга аргументов в зависимости от типа параметров f.

Если один из параметров f имеет особый тип, marshal_me<T>, то marshal_and_apply будет маршалировать этот параметр через специально выделенное хранилище, прежде чем передать его f. Чтобы выполнить распределение, требования к хранению всех параметров должны быть известны marshal_and_apply, прежде чем какой-либо из них можно будет маршалировать.


Некоторые примеры:

template<typename Function, typename... Args>
void marshal_and_apply(Function f, Args... args);

void func1(int x, int y);
void func2(marshal_me<int> x, int y);
void func3(marshal_me<int> x, marshal_me<int> y, marshal_me<int> z);

// this call would be equivalent to:
// func1(7,13)
marshal_and_apply(func1, 7, 13);

// this call would be equivalent to:
// auto storage = my_allocator(sizeof(int));
// auto x = marshal_me<int>(7, storage);
// func2(x, 13);
marshal_and_apply(func2, 7, 13);

// this call would be equivalent to:
// auto storage = my_allocator(sizeof(int) + sizeof(int) + sizeof(int));
// auto x = marshal_me<int>(7, storage);
// auto y = marshal_me<int>(13, storage + sizeof(int));
// auto z = marshal_me<int>(42, storage + sizeof(int) + sizeof(int));
// func3(x,y,z);
marshal_and_apply(func3, 7, 13, 42);

Похоже, что для решения этой проблемы marshal_and_apply требуется механизм проверки типов параметров f. Я подозреваю, что в общем случае это невозможно, но можно определить, можно ли преобразовать один из специального набора типов (в данном случае marshal_me<T>) в тип определенного параметра.

Как мне построить marshal_and_apply?


person Jared Hoberock    schedule 11.01.2012    source источник
comment
Во втором примере я не вижу, что вы сделали с 7. Вы имели в виду auto x = marshal_me<int>(storage, 7); или что-то в этом роде?   -  person Aaron McDaid    schedule 12.01.2012
comment
@AaronMcDaid Да, я исправил это.   -  person Jared Hoberock    schedule 12.01.2012
comment
Если у marshal_me<int> есть конструктор, принимающий int, вы все равно можете вызвать func2(7, 13). Какие дополнительные преимущества дает обертка?   -  person Ben Jackson    schedule 12.01.2012
comment
@BenJackson Пример надуманный. Полное объяснение выходит за рамки этого поста, но мне нужно особым образом выделить память для параметров функций из-за необычных требований к программированию на GPU.   -  person Jared Hoberock    schedule 12.01.2012


Ответы (1)


Может быть, что-то вроде этого:

template<typename Function, typename... Args>
void marshal_and_apply(Function f, Args &&... args)
{
    f(InspectAndModify<Args>::process(sizeof...(Args), std::forward<Args>(args))...);
}

Теперь определите:

template <typename T> struct InspectAndModify
{
    static T&& process(unsigned int N, T && t)
    {
        return std::forward<T>(t);
    }
};

template <typename T> struct InspectAndModify<marshal_me<T>>
{
     static T&& process(unsigned int N, marshal_me<T> && m)
     {
         /* ... */
     }
};

Нечто совершенно другое: этот подход сначала анализирует сигнатуру функции, а затем выполняет "статическое преобразование" для каждой пары типов, где вы можете вставить специализацию marshal_me:

template <typename T> struct marshal_me { marshal_me(T) { } };

template <typename To, typename From> struct static_transform;

template <typename T> struct static_transform<T, T>
{
  static T go(T t) { return t; }
};

template <typename T> struct static_transform<T, T&>
{
  static T go(T & t) { return t; }
};

template <typename T> struct static_transform<marshal_me<T>, T>
{
  static marshal_me<T> go(T && t) { return std::forward<T>(t); }
};

template<typename T, typename... Args>
struct marshal_impl
{
  template <typename ...Urgs>
  static T go(T(*f)(Urgs...), Args &&... args)
  {
    return f(static_transform<Urgs, Args>::go(std::forward<Args>(args))...);
  }
};

template<typename Function, typename... Args>
void marshal_and_apply(Function f, Args &&... args)
{
  marshal_impl<void, Args...>::go(static_cast<typename std::decay<Function>::type>(f),
                                  std::forward<Args>(args)...);
}
person Kerrek SB    schedule 11.01.2012
comment
Спасибо! Это сработает, но боюсь, что это не решит третий пример. InspectAndModify::process необходимо знать общее количество параметров, которые необходимо обработать, прежде чем он сможет обработать любой из них. - person Jared Hoberock; 12.01.2012
comment
@JaredHoberock: вы можете передать sizeof...(Args) в качестве дополнительного параметра? - person Kerrek SB; 12.01.2012
comment
К какой функции? Подпись marshal_and_apply исправлена, но она может передавать дополнительную информацию InspectAndModify::process. - person Jared Hoberock; 12.01.2012
comment
Обновлено - теперь я передаю общий размер process. - person Kerrek SB; 12.01.2012
comment
Понятно; Я должен был быть более конкретным. Проблема в том, что marshal_and_apply не получает marshal_me объектов в качестве параметров — например, он получает только ints. Таким образом, в вашем коде process никогда не получит marshal_me, и эта специализация никогда не будет реализована. - person Jared Hoberock; 12.01.2012
comment
@JaredHoberock: о, извини, я неправильно понял! По сути, вы хотите различать на основе подписи Function? - person Kerrek SB; 12.01.2012
comment
Точно, по модулю того факта, что функторы могли перегружать operator() разным количеством параметров. - person Jared Hoberock; 12.01.2012
comment
@JaredHoberock: Хорошо, я пробую что-то новое. Он все еще довольно сломан, но давайте посмотрим, как далеко мы продвинемся. Центральным элементом волшебства является шаблон static_transform. - person Kerrek SB; 12.01.2012
comment
@JaredHoberock: Хорошо, я думаю, что теперь это вполне работоспособно - попробуйте! - person Kerrek SB; 12.01.2012
comment
+1 только за static_cast<typename std::decay<Function>::type>(f) - person sehe; 12.01.2012
comment
Бит T(*f)(Urgs...) выглядит именно так, как мне нужно для проверки сигнатуры функции. Я придумал другой подход для проверки объектов функций. Спасибо! - person Jared Hoberock; 12.01.2012
comment
@JaredHoberock: Да, моя версия работает только для указателей на функции. Вам действительно нужно что-то, что соответствует всем вызываемым объектам... - person Kerrek SB; 12.01.2012