Первая попытка удовлетворить требования пользователя может привести к следующему довольно обычному интерфейсу:
template<typename Out, typename In> Out convert (In const&);
template<typename Out, typename In> Out convert (In const&, Out const& fallback);
template<typename Out, typename In> bool convert (Out& result_out, In const&);
template<typename Out, typename In> bool convert (Out& result_out, In const&, Out const& fallback);
со следующим поведением:
- возвращает результат или бросает на неудачуR3a,R4;
- не выбрасывает, возвращает результат или предоставленный запасной вариантR3b,R5,R5c, но неR5a;
- не бросает, пишет результат на<
result_out
>(при успешности), возвращает указание на успех или неудачуR3b,R5,R5a, но неR5c;
- не бросает, записывает результат на<
result_out
>(при успешном) или предоставленный запасной вариант, возвращает указание на успех или неудачуR3b,R5,R5cиR5a.
Подписи No 3 и No 4 являются особенными, поскольку они, по сути, возвращают две вещи — фактический результат (записанный в<result_out
>) и указание на успех или неудачу (возвращенный функциями). Учитывая, что ссылка на<result_out
>передана, фактический экземпляр<result_out
>построен (хранение выделено и инициализировано) вне вызовов функции.
Аналогично сценарию, описанному в разделеКонвертерная подпись, что приводит к дополнительным и ненужным накладным расходам. Действительно, если операция преобразования удалась, то значение инициализации переопределяется (с фактическим результатом), если оно не удается, то значение либо переопределяется по-прежнему (с запасом), либо бессмысленно.
Чтобы избежать накладных расходов, мы можем снова (как в разделеКонвертерная подпись) развернуть<boost::optional
>и изменить подписи на
bool convert (boost::optional<Out>&, In const&);
bool convert (boost::optional<Out>&, In const&, Out const&);
Теперь, когда мы смотрим на #3, мы видим, что признак успеха или неудачи дублируется. А именно, она возвращается из функции и инкапсулируется в<boost::optional<Out>
>. Следовательно, #3 может быть упрощен до
void convert (boost::optional<Out>&, In const&);
или выражается более идиоматично (на C++):
boost::optional<Out> convert (In const&);
До сих пор мы пришли к следующему набору
Out convert (In const&);
Out convert (In const&, Out const&);
boost::optional<Out> convert (In const&);
bool convert (boost::optional<Out>&, In const&, Out const&);
Который в целом выглядит довольно уродливо и, по сути, даже не компилируется как #1 с #3. Хорошо, чтофункционально#1 и #2 больше не нужны, поскольку они дублируют следующие развертывания #3:
Out out1 = boost::convert(in).value();
Out out2 = boost::convert(in).value_or(fallback);
Опять же, мы не обсуждаем эстетические аспекты интерфейса (или синтаксический сахар, который может быть очень субъективным). Вместо этого мы фокусируемся нафункциональной полнотеи до сих пор нам удается поддерживать ту жефункциональную полнотусменьшей.
Оказывается, приложив немного усилий, мы можем обойтись и без самого сложного — #4.
boost::optional<Out> out = boost::convert(in);
bool out_success = out ? true : false;
Out out_value = out.value_or(fallback);
В конечном итоге мы приходим к одному и единственному
boost::optional<Out> convert(In const&);
Важными качествами API является то, что онфункционально завершенинаиболее эффективный способразвернуть выбранную подпись преобразователя (см. разделКонвертерная подпись). Интерфейс<boost::convert()
>обычно оптимизируется (устраняется) при развертывании.
boost::optional<Out> out = boost::convert(in);
API имеет несколько преимуществ, связанных с развертыванием. Во-первых, он точно говорит, что делает. Учитывая, что запрос на преобразование является толькозапросом, API возвращает<boost::optional
>по существу говоря: «Я постараюсь, но я могу потерпеть неудачу. Действуйте так, как считаете нужным». Честно и просто. Я предпочитаю "Я постараюсь. Я могу потерпеть неудачу, но вы не хотите знать об этом. Если я потерплю неудачу, ты умрешь" или вариации в этом направлении.
На более серьезной ноте, хотя интерфейс допускает пакетные конверсии в конвейерном стиле. А именно, попытка преобразовать несколько значений последовательно, сохраняя результаты<boost::optional
>и затем анализируя/проверяя их (без потери информации, было ли каждое отдельное преобразование успешным или нет) каким-то полуавтоматизированным способом.
Опять же, этот API не должен быть единственным APIBoost.Convertобеспечивает. Однако этот API является единственнымсущественнымAPI. Другие API относительно легко извлекаются из него. Например,
template<typename Out, typename In>
Out
convert(In const& in, Out const& fallback)
{
return convert(in).value_or(fallback);
}
Учитывая, что чрезвычайно трудно (если не невозможно) придумать библиотечный API, который мог бы понравиться всем, мы могли бы также остановиться на.необходимAPI и позволяет пользователям создавать свои собственные API (как в примере выше) для удовлетворения своих эстетических предпочтений.
Тем не менее, необходимо признать, что<boost::optional
>является довольно новой концепцией, и некоторые люди неохотно используют ее или считают ее развертывание неоправданно сложным. Следовательно,Boost.Convertобеспечивает альтернативный (более обычный) интерфейс:
Out convert(In const&, Converter const&, Out const& fallback_value);
Out convert(In const&, Converter const&, Functor const& fallback_functor);
Out convert(In const&, Converter const&, boost::throw_on_failure);