Может ли C++11/14 шаблон с переменным числом аргументов перебирать аргументы функции?

Я использую шаблоны переменных для сбора статической информации о типах в Isis2, атомарной многоадресной библиотеке (isis2.codeplex.com). Некоторые события Isis2 доставляются через upcall. Например, если вы код

Group g("myGroup");
g.Handlers[UDPATE] += [](string& name, Foo& f) { ... your code };
....
g.OrderedSend(UPDATE, "John Doe", new Foo(...));

затем при получении многоадресной рассылки в группе g, содержащей обновление со строкой и объектом Foo, Isis2 создаст локальный экземпляр объекта Foo и затем вызовет эту лямбду с соответствующими аргументами.

Так вот моя загадка. У меня есть код переменной для сканирования аргументов в OrderedSend и я могу захватить информацию статического типа, необходимую для создания моих сообщений. В итоге я передаю реальному методу OrderedSend одномерный массив аргументов, каждый из которых имеет свой тип, указатель или безопасную ссылку на данные или объект, а для объекта - адрес метода маршалинга. Но чтобы использовать шаблон переменной для сканирования лямбды, мне нужно взглянуть на "внутренний список аргументов" функции в том смысле, что объект, добавляемый в вектор обработчиков, является лямбда-выражением: методы type_traits просто скажут, что является объектом типа "функция". Я после строки и типов Foo, из списка аргументов лямбда. Но, как я вижу, в type_traits.h ничего нет для доступа к списку аргументов.

Специфичная опция GCC-11 - отменить ввод идентификатора и проанализировать полученную строку. Но есть ли особенность шаблона variadic, которая позволила бы мне получить список аргументов лямбды во время компиляции?

2 ответа

Решение
template<class Sig>
struct MessageName {
  std::string name;
  MessageName() = delete;
  MessageName( std::string o ):name(o) {}
  MessageName(MessageName&&)=default;
  MessageName(MessageName const&)=default;
  MessageName& operator=(MessageName&&)=default;
  MessageName& operator=(MessageName const&)=default;
};

// trait to determine if some args are compatible:
template<class Sig, class...Ts>
struct is_compatible : std::false_type {};
template<>
struct is_compatible<void()> : std::true_type {};

template<class A0, class...Args, class T0, class...Ts>
struct is_compatible<void(A0, Args...), T0, Ts...>:
  std::integral_constant<bool,
    std::is_convertible<T0, A0>::value
    && is_compatible< void(Args...), Ts... >::value
  >
{};
struct HandlerMap {
  template<class Sig>
  void add_handler(
    MessageName<Sig> msg,
    block_deduction< std::function<Sig> > handler
  )
  {
    // ...
  }
  template<class Sig, class...Ts>
  typename std::enable_if<is_compatible<Sig, Ts...>::value>::type
  send_message( MessageName<Sig> msg, Ts&&... ts )
  {
    // ...
  }
};

UPDATE токен должен быть типа MessageName, Все MessageNames должен требовать подпись, связанную с ними.

MessageName< void(std::string const&, Foo const&) > UPDATE{"update"};

как и выше.

Затем, когда вы добавляете обработчик, вызов add_handler проверит назначенную функцию на соответствие требуемой подписи и даст вам std::function,

Точно так же, когда вы отправляете сообщение, переданные типы могут быть проверены на соответствие подписи. Вы должны даже преобразовать аргументы в каждый из типов аргументов подписи в теле функции.

Это перемещает как можно большую часть проверки типов во время компиляции, что является хорошим стилем C++.

Нет, это невозможно. Что если объект не лямбда, а структура с перегрузками? Или полиламбда? Вы не можете предполагать, что объект функции имеет одну и только одну подпись - существует множество способов получить более одной.

Вот простой пример:

struct fun {
    int i;
    void operator()(int x) {}
    void operator()(float x) {} 
};

В этой структуре или ее аргументах нет ничего сверхсложного или не-POD.

Другие вопросы по тегам