автоматическое преобразование из boost::bind_t в boost::function

У меня есть метод следующей подписи:

template<typename T>
void
register_msg_action(const pmt::pmt_t& name,
      boost::function<T(pmt::pmt_t)> converter,
      boost::function<void(T)> action)

(pmt_t - это полный тип, прежде чем вы спросите)

а также перегрузки, которые принимают T converter(pmt::pmt_t) и void converter(T) (т. е. необработанные функции C/C++), а также все перестановки вышеупомянутых boost::function<> и аргументы функций в стиле C. Это оставляет мне уже 4 разных метода.

Я хотел бы избежать дальнейшего увеличения количества методов. Однако чаще всего я вызываю что-то вроде

register_msg_action(pmt::mp("key"),
    pmt::to_long, /* "raw" function long(pmt_t) */
    boost::bind(&my_class::void_method_of_long, this, _1) /* CAVEAT */
);

Мой подход заключался в том, что аргумент /* CAVEAT */ неявно преобразуется в boost::function<void(T)>, но, похоже, это не так (g++ 5.1.1):

error: no matching function for call to ‘register_msg_action(pmt::pmt_t, boost::function<long int(boost::intrusive_ptr<pmt::pmt_base>)>&, boost::_bi::bind_t<void, void (*)(long int), boost::_bi::list1<boost::arg<1> > >)’
     register_msg_action(pmt::mp("hi"), long_function, boost::bind(&my_class::void_method_of_long, this ,_1));

... все остальные кандидаты (boost::function,boost::function); (T(pmt_t),boost::function); (T(pmt_t), пустота(T))...

test.cc:56:1: note: candidate: template<class T> void register_msg_action(const pmt_t&, T (*)(pmt::pmt_t), boost::function<void(T)>)
 register_msg_action(const pmt::pmt_t& name,
 ^
test.cc:56:1: note:   template argument deduction/substitution failed:
test.cc:80:76: note:   ‘boost::_bi::bind_t<void, void (*)(long int), boost::_bi::list1<boost::arg<1> > >’ is not derived from ‘boost::function<void(T)>’
     register_msg_action(pmt::mp("key"), pmt::to_long, boost::bind(&my_class::void_method_of_long, this, _1));

Теперь, делая

boost::function<void(long)> action (boost::bind(&my_class::void_method_of_long, this, _1));
register_msg_action(pmt::mp("key"), pmt::to_long, action);

прекрасно работает. Поскольку есть даже конструктор, который принимает boost::_bi::bind_t в boost::function, мне интересно, что я должен сделать, чтобы это заработало, без

  • повторная реализация boost::function
  • полагаясь на С++ 11 или более позднюю версию (не может, поддержка устаревшего компилятора)
  • используя boost:phoenix для функционального программирования (попробовал бы это, но версии boost, которые мы должны поддерживать, еще не все имеют phoenix.

Я боюсь добавлять тип третьего аргумента в качестве дополнительного имени типа шаблона, потому что это нарушит безопасность типа списка параметров, которая необходима для гарантии работы action(converter(pmt::pmt_t)), и, честно говоря, я бы предпочел сейчас иметь дело с большим количеством кода, чем проверять пользовательский шаблон. ошибки g++ позже.


person Marcus Müller    schedule 31.08.2015    source источник


Ответы (1)


Проблема в том, что T появляется в подписи register_msg_action в аргументах шаблона boost::function. Затем, если вы не вызываете его с реальным объектом boost::function, его нельзя вывести. Это должно работать, если вы явно укажете аргумент шаблона:

register_msg_action<long>(pmt::mp("key"),
    pmt::to_long, /* "raw" function long(pmt_t) */
    boost::bind(&my_class::void_method_of_long, this, _1)
);

Если вы хотите сохранить возможность вывода T при использовании хотя бы одного аргумента простой функции, у вас есть возможность явно сделать T невыводимым при его boost::function использовании:

template <class T>
struct NonDeduced
{
  typedef T type;
};

// T has to be specified explicitly at call site
template<typename T>
void
register_msg_action(const pmt::pmt_t& name,
      boost::function<typename NonDeduced<T>::type (pmt::pmt_t)> converter,
      boost::function<void(typename NonDeduced<T>::type)> action)

// T deducible from converter
template<typename T>
void
register_msg_action(const pmt::pmt_t& name,
      T converter(pmt::pmt_t),
      boost::function<void(typename NonDeduced<T>::type)> action)

// T deducible from action
template<typename T>
void
register_msg_action(const pmt::pmt_t& name,
      boost::function<typename NonDeduced<T>::type (pmt::pmt_t)> converter,
      void action(T))

// T deducible from both, must match
template<typename T>
void
register_msg_action(const pmt::pmt_t& name,
      T converter(pmt::pmt_t),
      void action(T))

[Живой пример]

person Angew is no longer proud of SO    schedule 31.08.2015
comment
Это действительно так! Я переработаю свой синтаксис, чтобы сделать это функцией, а не дополнительным бременем для пользователя. - person Marcus Müller; 31.08.2015
comment
Кстати, принуждение моего пользователя к указанию аргумента шаблона уменьшит номер моего метода до 1, что, я думаю, является улучшением. Я думаю, что лучше заставить пользователя сделать что-то явно, чем предложить ему возможность сделать что-то, что заставит даже человека с опытом работы с Boost потратить несколько минут, чтобы понять, на что жалуется компилятор. - person Marcus Müller; 31.08.2015