Pull to refresh

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

Reading time 3 min
Views 9.1K
Временами от 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 уже и так используется в проекте — польза однозначно будет.
Tags:
Hubs:
+24
Comments 20
Comments Comments 20

Articles