Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

Доктрины Boost

Anna | 24.06.2014 | нет комментариев

От применения образцов в С лично меня неизменно отпугивало неимение стандартных механизмов задания ограничений параметров. Другими словами, когда разработчик пишет функцию

template <class T>
bool someFunc(T t)
{
	if (t.someCheck()) {
		t.someAction(0);
	}
}

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

  1. Объекты типа T передаются по значению, значит, обязаны иметь открытый копирующий конструктор
  2. Существует открытый способ T::someCheck без параметров, тот, что возвращает значение, приводимое к логическому типу
  3. Существует отрытый способ T::someAction, тот, что может принимать один приводимый к числовому типу параметр
Задача

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

  1. Чтение документации к библиотеке. Если она есть и ясно написана. Но даже в этом случае никто не будет вычитывать документацию всех используемых библиотек перед всяким изменением своего кода. Помнить все данные назубок тоже не всякому по плечу.
  2. Постижение начального кода библиотеки. Тоже занятие на любителя. Причем чем библиотека огромнее и свой план труднее, тем любителей поменьше

Остается еще один вариант — по сути, исключительный механический — отталкиваться от ошибок компиляции. Т.е. сделал метаморфоза, не собирается, ищешь почему… Впрочем те, кто пользовался образцами C знают, на что могут быть схожи сообщения об ошибках. На что желательно, только не на подсказку вида «Поправь вот тут, и все заработает». Изредка сообщение довольно ясно, а изредка оказываешься в дебрях Сторонний библиотеки… Компилятор уведомляет об ошибке в том месте, где она случилась — ему все равно, что изначальный контекст применения там теснее не восстановить.

Разглядим пример (мы еще возвратимся к нему позднее)

Необходимо отсортировать список (типовой контейнер). Ничего не предвещает, пишем

std::list<int>theList;
std::sort(theList.begin(), theList.end());

Не компилируется. В VS2013 оплошность выглядит дальнейшим образом

error C2784: ‘unknown-type std::operator -(std::move_iterator&lt_RanIt&gt &,const std::move_iterator&lt_RanIt2> &)’: could not deduce template argument for ‘std::move_iterator&lt_RanIt> &’ from ‘std::_List_iterator&ltstd::_List_val&ltstd::_List_simple_types<int&gt&gt&gt’ c:\program files (x86)\microsoft visual studio 12.0\vc\include\algorithm 3157 1 MyApp

Но это полбеды — при клике по ошибке мы оказываемся в глубинах стандартной библиотеки algorithm вот в этом месте

template<class _RanIt,
	class _Pr> inline
	void sort(_RanIt _First, _RanIt _Last, _Pr _Pred)
	{	// order [_First, _Last), using _Pred
	_DEBUG_RANGE(_First, _Last);
	_DEBUG_POINTER(_Pred);
	_Sort(_Unchecked(_First), _Unchecked(_Last), _Last - _First, _Pred);
	}

Первая реакция: «Чего?! Отчего вектор сортировался, а список внезапно нет — у обоих контейнеров есть итераторы, оба знают о порядке элементов..» И хорошо еще стандартная библиотека — данный пример избит, и программисты традиционно знают, что случилось. Но представьте, что вас вот так без спасательного круга кинули в недра иной, не такой знаменитой библиотеки…

Решение

Оказывается, решение есть. Инициатива метаморфозы языка в этом направлении существует, но пока в эталон не попала.
А вот библиотека boost поддерживает представление доктрин (concepts), с поддержкой которых дозволено создавать пользовательские ограничения для параметров образцов.

Алгорифм применения доктрин дальнейший. Разработчик совместно со своими библиотеками поставляет изложение нужных для их правильной работы доктрин. Пользователь может в механическом режиме тестировать все свои сущности на соответствие предложенным правилам. При этом ошибки теснее будут значительно внятнее, вида: Класс не поддерживает доктрину «Должен быть конструктор по умолчанию».

Применяя boost, разработчик не обязан всякий раз конструировать доктрины с нуля — библиотека содержит заготовки основных ограничений.

Разглядим пример для функции someFunc, приведенной в начале статьи. Первое правило — присутствие копирующего конструктора покрывается готовой доктриной boost::CopyConstructible, для остальных придется написать тесты вручную.

#include <boost/concept_check.hpp>

template <class T>
struct SomeFuncAppropriate {
public:
	BOOST_CONCEPT_ASSERT((boost::CopyConstructible<T>));
	BOOST_CONCEPT_USAGE(SomeFuncAppropriate)
	{
		bool b = t.someCheck();// способ someCheck, с возвращаемым значением, приводимым к bool
		t.someAction(0);// способ someAction с параметром, приводимым к числу
	}
private:
	T t; // must be data members
};

Выходит, доктрина boost — это конструкция-образец, в качестве параметра которого применяется тестируемый тип. Проверка на соответствие готовым доктринам осуществляется посредством макроса BOOST_CONCEPT_ASSERT. Обратите внимание — в качестве параметра ему передается доктрина в скобках, в результате двойные скобки непременны, хоть и режут глаз.

Пользовательские проверки могут быть реализованы с поддержкой макроса BOOST_CONCEPT_USAGE. Значимо помнить, что все экземпляры, участвующие в тестировании (у нас это T t), обязаны быть объявлены как члены класса, а не как локальные переменные.

Когда доктрина объявлена, на соответствие ей проверять дозволено с применением того же макроса BOOST_CONCEPT_ASSERT. Возможен, у нас есть класс

class SomeClass
{
public:
	SomeClass();
	void someCheck();
	int someAction(int);

private:
	SomeClass(const SomeClass& other);
};

Протестировать его дозволено так

BOOST_CONCEPT_ASSERT((SomeFuncAppropriate<SomeClass>));

Пробуем запустить — сразу получаем ошибку

error C2440: ‘initializing’: cannot convert from ‘void’ to ‘bool’

Причем при клике по ней, нас бросает на нарушенную строчку в определении доктриныSomeFuncAppropriate (в BOOST_CONCEPT_USAGE), где дозволено легко осознать причину задачи — способsomeCheck возвращает void взамен bool. Исправляет, пробуем еще раз…

error C2248: ‘SomeClass::SomeClass’: cannot access private member declared in class ‘SomeClass’ boost\concept_check.hpp

По клике на ошибке оказываемся в начальном коде доктрины

  BOOST_concept(CopyConstructible,(TT))
  {
    BOOST_CONCEPT_USAGE(CopyConstructible) {
      TT a(b);            // require copy constructor
      TT* ptr = &a;       // require address of operator
      const_constraints(a);
      ignore_unused_variable_warning(ptr);
    }
...

Причем курсор указывает на строчку

 TT a(b);            // require copy constructor

Ах да — копирующий конструктор спрятан. Исправляем — сейчас тест проходится (компилируется файл с BOOST_CONCEPT_ASSERT). Значит, класс SomeClass всецело соответствует ожиданиям разработчика функции someFunc. Даже если в грядущем будут добавлены метаморфозы, которые нарушат совместимость, проверка доктрины сразу известит, в чем именно задача.

Возвратимся к примеру с сортировкой std::list с поддержкой std::sort. Выразим в виде доктрины требования к сортируемому контейнеру. Во-первых, std::sort может трудиться только с контейнерами, которые поддерживают произвольный доступ (random access). Соответствующая доктрина имеется в boost (boost::RandomAccessContainer), впрочем ее неудовлетворительно. Также существует требование к содержимому контейнера — его элементы обязаны поддерживать оператор сопоставления «поменьше». Здесь вновь спасает boost с готовой доктриной boost::LessThanComparable.
Комбинируем доктрины в одну

template <class T>
struct Sortable 
{
	public:
		typedef typename std::iterator_traits<typename T::iterator>::value_type content_type;

		BOOST_CONCEPT_ASSERT((boost::RandomAccessContainer<T>));
		BOOST_CONCEPT_ASSERT((boost::LessThanComparable<content_type>));
};

Запускаем проверку

BOOST_CONCEPT_ASSERT((Sortable<std::list<int> >));

Видим

error C2676: binary ‘[‘: ‘const std::list<int,std::allocator<_Ty>>’ does not define this operator or a conversion to a type acceptable to the predefined operator boost\concept_check.hpp

Щелчок по ошибке отправляет нас в начальный код доктрины RandomAccessContainer, давая осознать, что именно она и нарушена. Если заменить std::list на std::vector, проверка доктрины увенчается триумфом. Сейчас испробуем проверить на сортируемость вектор экземпляров SomeClass.

BOOST_CONCEPT_ASSERT((Sortable<std::vector<SomeClass> >));

Контейнер-то сейчас подходящий, но отсортировать его все равно невозможно, так как SomeClass не определяет оператора «поменьше». Об этом мы узнаем сразу

error C2676: binary ‘<’: ‘SomeClass’ does not define this operator or a conversion to a type acceptable to the predefined operator boost\boost\concept_check.hpp

Щелчок по ошибке — и мы оказываемся в исходнике LessThanComparable, понимая, что именно нарушили.

Таким образом, доктрины делают обобщенное программирование в C чуть менее экстремальным. Что не может не радовать!

Источник: programmingmaster.ru

Оставить комментарий
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB