Именованные параметры Boost

  • Tutorial
Временами от C++ хочется более гибкого механизма параметризации функций. Например, есть у нас функция с двумя обязательными параметрами и большим количеством необязательных.

bool foo(int important, int& pOut, int sometimes = 1, int occasionally = 2, int rarely = 3)
{
//...
}

Проблемы здесь могут быть следующие

  1. Пользователи постоянно путают порядок параметров, тип их практически полностью совпадает, поэтому компилятор ничем помочь не может (разве что иногда со вторым параметром).
  2. Из необязательных параметров чаще всего нужен один, причем если это не sometimes, пользователи вынуждены вспоминать значения по умолчанию, чтобы задать их в вызове явно. Значения по умолчанию разные, так что ошибок снова много
  3. Нет никакой возможности выразить зависимость значений по умолчанию одних параметров от других.


Проблемы эти можно решить по-разному: передавать в качестве параметра структуру, использовать перегрузку функций или даже функции с разными именами… Boost предлагает еще один вариант решения.

Это именованные параметры. Больше мы не привязаны к порядку следования параметров, возможность же задавать осознанные имена при вызове существенно снижает вероятность ошибок. В качестве дополнительного бонуса получаем возможность использовать одни параметры при вычислении значения по умолчанию других.

В качестве примера рассмотрим, как можно определить функцию foo с использованием предлагаемого механизма (имя изменим на namedParametersFoo, чтобы не было путаницы). Для начала подключаем заголовочный файл boost и определяем имена параметров с помощью специального макроса. Обратите внимания — никаких знаков препинания.

#include <boost/parameter.hpp>

BOOST_PARAMETER_NAME(important)
BOOST_PARAMETER_NAME(pOut)
BOOST_PARAMETER_NAME(sometimes)
BOOST_PARAMETER_NAME(occasionally)
BOOST_PARAMETER_NAME(rarely)


далее задаем функцию

BOOST_PARAMETER_FUNCTION(
	(bool),
	namedParametersFoo,
	tag,
	(required 
		(important, *)
		(in_out(pOut), *)
	)
	(optional
		(sometimes, *, important * 2)
		(occasionally, *, 300)
		(rarely, *, 400)
	)
) 
{
// Тело функции
}

Макросу передается четыре параметра, разделяемых запятыми

  1. Тип возвращаемого значения — обязательно в скобках
  2. Имя функции
  3. Пространство имен с параметрами. BOOST_PARAMETER_NAME автоматически помещает объявления в tag
  4. Список параметров

Список параметров имеет свою структуру. Также, заметьте, знаков препинания практически никаких — есть только запятые при определении непосредственных параметров.

Вначале идет блок required. Как следует из названия, он содержит список обязательных параметров. Каждый параметр должен быть помещен в круглые скобки. Для определения обязательного параметра необходимо задать его имя и ограничения типа. В данной статье механизм ограничения типов не рассматривается, поэтому будем просто использовать звездочку. Подробнее на эту тему можно почитать здесь.

Затем следует блок необязательных параметров. Здесь при определении каждого параметра появляется дополнительная возможность задать значение по умолчанию. Как видно из примера выше, можно использовать в выражениях остальные параметры.

Собственно все. Функция поддерживает вызовы «по старинке» — как если бы была определена с обычными необязательными параметрами. Т.е. как будто заработал вариант

bool newFoo(int important, int& pOut, int sometimes = important * 2, int occasionally = 2, int rarely = 3)
{
//...
}

newFoo не скомпилируется из-за зависимости параметра sometimes от important

Т.е. можно использовать namedParametersFoo так
	int s = 0;
	namedParametersFoo(1, s, 1, 1, 1);
	namedParametersFoo(1, s, 1, 1);
	namedParametersFoo(1, s, 1);
	namedParametersFoo(1, s);


Но это еще не все! Задействовав именованные параметры, мы освобождаем себя как от порядка следования аргументов, так и от обязательных ненужных значений по умолчанию. Единственное неудобство — требуется добавлять знак подчеркивания вначале имен.

namedParametersFoo(_sometimes = 1, _important = 111, _pOut = s);

Итак, что в этом примере нового хорошего

  1. Наглядность — видно, что именно мы имеем в виду под числами 1 и 111, вероятность запутаться/ошибиться снижается
  2. Произвольный порядок — необязательный параметр написан первым, и все работает

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

namedParametersFoo(_sometimes = 1, _pOut = s);

не соберется — пропущен необходимый параметр _important

На этом все. Хочется добавить, что механизм не самый простой и очевидный, поэтому если получается обойти проблему стандартными средствами языка, то лучше так и делать. К тому же применение именованных параметров boost может негативно сказаться на времени компиляции. Но если все обходные пути получаются кривыми, а boost уже и так используется в проекте — польза однозначно будет.
Share post
AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 20

    +20
    И из за опечатки логи ошибок на 16кб…
      +2
      ага, идеальным решением на все случаи жизни я бы это не назвал
      +6
      Буст хорош тем, что авторам языков программирования нужно туда смотреть и делать все это на языковом уровне. Прямо по списку. Каждая библиотека — фича языка (ну кроме традиционно библиотечных вещей типа ввода-вывода конечно же). А непосредственно такими вот макросами ИМХО лучше не пользоваться:)
        +10
        Рискну навлечь на себя праведный гнев, ввязавшись в холивар…
        Но по-моему (повторяю и подчеркиваю — это мое мнение! — оно не обязано совпадать с вашим):
        «совершенный код» — это тот код, который (1)короче и в котором (2)меньше шансов ошибиться
        Да, приведенный пример решает второй пункт, но никак не коррелирует с моим первым критерием.
        Если у ваших функций 5 параметров, где вперемешку int и int& причем несколько из них заданы по умолчанию — проблема в вашей голове, а не в C++ и boost c подобного рода костылем эту проблему не решит.
          +2
          В статье хотелось бы увидеть пример кода, который генерируется в результате разворачивания этого макроса. Особенно интересно то, во что превращается код вызова с именованными аргументами.

          Касательно самого подхода, имхо, проблема надумана. Нормальная среда разработки подскажет и порядок, и значения по умолчанию для любой функции. А как подсказка будет работать для сгенерированного из макроса кода — не представляю.
            +1
            >>Нормальная среда разработки подскажет и порядок, и значения по умолчанию

            Что если разработчик функции решит поменять значение по умолчанию?

            Ответ «Оторвать разработчику руки» не предлагать плз
              0
              Идеального решения нет, согласен. Наименее стрёмный из того, что мне пришло в голову — это использовать для дефолтных значений аргументов статические поля какого-нибудь специального класса, или на худой конец именованные константы.
                0
                Или структуры передавать, хотя в плюсах нельзя как в С99 инициализировать поля по именам.
                  0
                  Не совсем по теме, но я не понимаю, почему это убрали из C++? По сути, основное обратно несовместимое изменение, причём совершенно не понятно, кому оно помешало.
                    0
                    Его не убирали. С++ основывался на С89 и просто не включил в себя эту фичу.
                  +4
                  Идеальным решением было бы позволить пропускать дефолтные параметры. Но комитет почему-то так не считает.

                  foo (10, default, default, 20);

                  Т.е. то же самое, что я предложил ниже, но работает с любыми типами.
                  Ключевое слово default уже все равно есть, техническая реализация — елементарная.
                0
                > В статье хотелось бы увидеть пример кода, который генерируется в результате разворачивания этого макроса.

                Увы, пример кода, который генерируется в результате разворачивания этого макроса, в статью не поместится :).

                А так пожалуйста, функция с тремя параметрами: pastebin.com/3ntTUfFr

                +2
                Проблему с дефолтными параметрами частично можно решить так. Частично, потому как работает только с интегральными типами

                ideone.com/mT7z0z

                (даю ссылку т.к. тэг source не работает)
                  +1
                  Упаси боже использовать эти костыли в продашкене.
                    +2
                    Эти костыли не просто так появились. Boost.Graph широко использует их. Сходу нагуглил: тут вроде обосновывают.
                    Мне кажется, что под Boost.Graph их и разрабатывали. Не знаю, как объявлять такие функции, а использовать вполне удобно.
                      +2
                      Лучше бы они сделали Linq-style (aka call chains).
                      По производительности за счет инлайна было бы тоже самое, плюс намного читабельнее.
                    +1
                    Относительно простое решение есть через множество методов c return *this:
                    some_object.width( 10 ).height( 15 ).hidden( true );
                    А функции с более чем 2-3 параметрами вообще не стоит создавать имхо, тогда и боротся с ними не придется.
                      +2
                      в целях изучения возможностей С++ — интересно, а вот на практике такое использовать… чего стоит только определение функции, в которое нужно вчитываться даже для того, чтобы понять сколько же там все таки параметров, какое у функции имя и что она возвращает.

                      ИМХО, чего только люди не придумают, лишь бы не слазить с С++.
                      Этот уникальный язык предоставляет настолько богатые возможности, что взяв двух профессионалов С++ нет никакой гарантии, что они поймут код друг друга без нескольких часов предварительного анализа используемых подходов. А так да, если разобраться то «все просто».

                      p.s. Сам ушел несколько лет назад с С++ в C# и с тех пор с содроганием вспоминаю ощущение, когда вместо того, чтобы решать реальную задачу борешься с языком.

                      * вышенаписанное является моим личным мнением и не претендует на абсолютную истинность
                        +1
                        В одном вы правы, за использование именовынных параметров boost на практике я бы лично сильно бил по рукам. Уродливо и нечитаемо

                      • UFO just landed and posted this here

                        Only users with full accounts can post comments. Log in, please.