четверг, 23 июля 2009 г.

Концепции или концепты?

Появился комментарий такого рода:


...давайте концепции не будем обзывать концептами?

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

Конечно, английское concept вообще-то переводится на русский как "концепция", "понятие". Но, как мне кажется, было бы неправильно буквально использовать такой вариант перевода. Дело в том, что "прямой" перевод обозначает - по крайней мере, в русском языке - нечто обобщенное, абстрактное. Некую идею, на основе которой строится какая-то теория, система взглядов и т.п. Например, "концепция бесконечности Вселенной" или что-то подобное.

В нашем же случае речь идет не о какой-либо идее, взгляде, теории - а о конкретном механизме конкретного языка, введенном в него для решения вполне определенных задач (насколько этот механизм адекватен задачам - другой вопрос). И использовать для обозначения языкового механизма слово, традиционно обозначающее нечто "неконкретное", "абстрактное" - было бы неправильным. Тут нужно что-то близкое, но другое. И слово концепт, на мой взгляд, лучше отражает именно конкретность, предметность того, о чем идет речь: совокупность синтаксических и семантических правил, определяющих некий языковой механизм.

В английском языке, по-моему, не столь чувствуются подобные тонкие смысловые различия, потому они довольно легко используют для нового понятия любое более-менее подходящее слово. Не сомневаюсь, что они запросто могут выдать что-то вроде the concept of concepts. Но сказать по-русски "понятие концепции" или "механизм концепций" - как хотите, но это ужасно и выглядит как насилие над языком, и никто не заставит меня это повторить...

И последнее: пафос комментария словно бы предполагает, что C++ concepts уже широко обсуждаются в русскоязычных источниках и там уже вовсю называются концепциями. Рискну предположить, что это все-таки не совсем так. :-)

По всему по этому: только концепты. :-))

Concepts are gone!!

Многие уже знают, но все равно: ошеломляющая новость!


Комитет по стандартизации C++ проголосовал за удаление механизма концептов из нового Стандарта Си++.

Вот ссылка, по которой рассказываются детали и обсуждаются причины и последствия:


Вот на что упал взгляд при первом чтении (перевод вольный):

В понедельник 13 июля на очередном заседании комитета по стандартизации С++ во Франкфурте по результатам голосования концепты были удалены из C++0x. Эта шокирующая новость вызывает множество вопросов и беспокойств... Когда я впервые об этом услышал, я просто не поверил... Концепты представляли собой наиболее существенное добавление в ядро С++ с 1998 года (с момента выпуска первого стандарта - Е.З.). В течение последних пяти лет обсуждение механизма концептов проходило на каждом заседании комитета... После десятков официальных сообщений, описаний и статей, посвященных представлению и продвижению концептов, казалось крайне маловероятным, что непосредственно перед финишной чертой, в результате драматического голосования этот механизм будет выброшен...

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

Помимо очевидной важности этого события "вообще", оно непосредственно влияет и на мою работу: простите, ребята, я уже перевел почти сто страниц текста - громадный кусок главы 14 драфта!.. И что мне теперь, выкидывать все это? Помимо этого, упоминание концептов и понятий, к ним относящихся, разбросано по всему тексту стандарта - и теперь придется лазить по всему тысячестраничному тексту и все эти упоминания вычищать?..

пятница, 17 июля 2009 г.

Примеры комментариев


По просьбе Ilya Kulakov привожу два примера того, как могли бы выглядеть комментарии.

Первый пример (не знаю, хорошо ли будет видно...):




(Для особо внимательных: в этом кусочке есть опечатка. Она уже исправлена. Капчу переделывать лень. :-))

Второй пример - даже не комментарий, а как бы просто "маркер": он говорит, что, мол, в этом месте неплохо бы написать комментарий такого-то содержания. Такие маркеры также приветствуются.



И последнее замечание. На самом деле, комментарии вовсе не обязаны быть "умными". У меня полно коротеньких комментариев типа "об этом подробнее сказано в таком-то разделе", "здесь такой-то термин использован неправильно, надо такой-то" и даже "простите за буквоедство, но здесь опечатка"...

четверг, 16 июля 2009 г.

Перевод Стандарта: на распутье...

Тут вот какое дело.

В последние дни я (в который раз) просмотрел то, что сделал, и пришел к такому неутешительному выводу. Перевод как таковой готов практически полностью - осталось пару-тройку терминов уточнить, да кое-какие стилистические корявости поправить. Работы на неделю. Ну, еще приложения подготовить - еще недели три более-менее спокойной работы. Так что, в принципе, можно (было бы) переходить к к обсуждению технических деталей, связанных с публикацией. Но...

Комментарии! Проблема в том, что их... не то чтобы мало, но недостаточно для того, чтобы дать всей работе в целом некое новое качество, сделать ее в каком-то смысле уникальной - ну и, конечно, полезной для программистов и вообще всех, так или иначе причастных к разработке программ на Си++.

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

Так что сейчас передо мной стоит дилемма: остановить содержательную работу над комментариями и выпускать перевод в том виде, как он есть в данный момент, или... не торопиться и продолжать неспешную работу в расчете на то, что до октября (предполагаемый срок формального опубликования Стандарта Си++) достаточный объем комментариев наберется (но тогда книга выйдет гораздо позже изначально задуманного и, не дай Бог, кто-нибудь опередит :-)).

Какой вариант правильный - честно, не знаю. (И немного рассчитываю на "помощь зала" :-)).

Есть, правда, еще один вариант, связанный с этой самой "помощью зала"... Что если призвать на помощь добровольцев? Идея может быть такой: те, кто считает себя более-менее сведущим в языке и у кого есть содержательные мысли относительно того или иного языкового свойства, могут поступить так:

- Скачать текст предварительного Стандарта.
- Найти в нем раздел/абзац, относящийся к интересующей теме.
- Написать к нему комментарий!
- Прислать его мне. :-)

Может, попробовать?
Если кому-то это предложение покажется интересным, то вот некоторые детали:

- Текст предварительного стандарта находится по адресу
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2800.pdf
- Комментарии пишутся по-русски
- Комментарий должен явно относиться к конкретному абзацу или разделу текста
- Комментарий должен быть коротким (два-три-четыре абзаца текста), нейтральным по стилистике и нести полезную информацию; при этом допускаются как конкретные соображения ("данное правило введено для того-то и того-то"), так и рассуждения общего характера ("в ранних версиях языка это было по-другому" - и почему изменено).
- В комментариях могут быть короткие примеры программ, выдержанные в стилистике других примеров, имеющихся в Стандарте.
- Комментарии могут содержать ссылки на известные публикации, типа книг Страуструпа, Саттера, Александреску, Мейера и т.д., в которых комментируемое свойство объяснено подробнее и понятнее, чем в Стандарте. Допускаются прямые цитаты из подобных источников (разумеется, с указанием этих источников).
- Присылать свои тексты можно либо в виде комментариев к этому посту (что, между прочим, может способствовать их обсуждению и повышению качества), либо непосредственно на мой электронный адрес из профиля.

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

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

(Расписался... Начал "заупокой" и в процессе сам увлекся этой идеей. :-))

Ну, и последнее. Комментарии принимаются по всему тексту "языковой части" стандарта (без библиотеки!- до главы 16 включительно). Но есть наиболее интересные для меня места, в которых комментариев пока недостаточно:

- Лямбда-выражения (раздел 5.1.1) - текст короткий, но важный, и ни одного примера!- вот бы кто-нибудь с "функционально-устроенной головой" высказал свое отношение! :-))
- Шаблоны в целом (глава 14) и механизм концептов в особенности (разделы 14.9, 14.10)
- Совместное использования (глава 13)
- Обработка исключительных ситуаций (глава 15)

Вот как-то так. :-)

четверг, 9 июля 2009 г.

Новое в Си++: концепты

Этот текст содержит краткий обзор основных возможностей концептов – нововведения в новый стандарт Си++, позволяющего накладывать ограничения на параметры шаблонов. Показаны преимущества концептов при диагностике ошибок в шаблонах, использование отображений концептов для адаптации конкретных типов к использованию в качестве параметров шаблонов.

Обзор по моей просьбе написан Станиславом Михалковичем, доцентом Южного Федерального университета (Ростов-на-Дону), и выкладывается здесь с его любезного разрешения. Я сделал только очень небольшие редакторские правки и добавил немного замечаний (синим шрифтом), иногда эмоционально-окрашенных. J

В обзор вошли не все аспекты механизма концептов; в частности, ничего не сказано об архетипах и аксиомах. Но не все же сразу. J От обсуждений типа «зачем это нужно», «нужно ли вообще» давайте пока воздержимся. В том или ином виде ограничения на параметры шаблона можно задавать почти во всех современных языках, содержащих подобные средства: в Аде, в Java/C#, в Scala.

Итак, концепты в новом C++ – это средства задания ограничений на параметры шаблонов. Они представляют собой специальным образом оформленный набор объявлений, которые параметр шаблона должен реализовывать.

Рассмотрим вначале пример шаблона стандартного алгоритма find без использования концептов:

template<typename Iter, typename V>
Iter find(Iter first, Iter last, V v) {
while (first != last && *first != v)
++first;
return first;
}

Очевидно, что тип Iter должен удовлетворять следующим условиям:

  1. Объекты типа Iter должны быть сравнимы на !=.
  2. К объектам типа Iter можно применять префиксную операцию ++.
  3. Объекты типа Iter можно разыменовывать.

Кроме того, имеется условие, связывающее типы Iter и V:

  1. Разыменованные объекты типа Iter можно сравнивать с объектами типа V на !=.

В C++ стандарта 1998/2003 гг. проверка данных условий осуществляется при настройке шаблона. Например, при компиляции вызова find(1,5,0) в результате выведения получим: Iter=int, V=int. При попытке компиляции тела функции find с указанными типами возникнет ошибка: условие 3 не выполняется. Таким образом, ошибка компиляции возникнет на достаточно позднем этапе – при попытке компиляции тела настроенной версии find<int,int>. На практике это приводит к неадекватным по существу и совершенно безумным по форме диагностическим сообщениям, в которых фигурируют длиннейшие абсолютно нечитаемые имена настроенных шаблонов (в которых уже сделаны подстановки).

Рассмотрим теперь версию того же алгоритма find с использованием концептов:

template<typename V>
requires EqualityComparable
Iter find(Iter first, Iter last, V v) { ... }

Здесь тип Iter удовлетворяет концепту InputIterator, содержащему требования 1, 2 и 3, а также типы Iter::value_type и V связаны концептом EqualityComparable, содержащим требование 4. Теперь при вызове find(1,5,0) компилятор осуществит проверку типов Iter=int и V=int на соответствие концептам InputIterator и EqualityComparable, и мы получим сообщение об ошибке вида «Тип int не удовлетворяет концепту InputIterator». При этом в отличие от предыдущего примера тело функции find не будет компилироваться вообще, т.е. ошибка компиляции обнаружится на более раннем этапе.

Заметим, что условие удовлетворения типов концепту может записываться как в секции requires, так и в угловых скобках; в последнем случае ключевое слово typename заменяется на имя концепта, которому должен удовлетворять тип. Задание имени концепта в угловых скобках – более простой способ, он позволяет наложить на тип ограничения одного концепта. Для типов, удовлетворяющих нескольким концептам, а также для концептов, затрагивающих несколько типов, следует использовать вариант с requires, разделяя различные требования символом &&. Так, последний пример можно записать следующим образом:

template<typename Iter, typename V>
requires InputIterator && EqualityComparable
Iter find(Iter first, Iter last, V v) { ... }

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

template<typename V>
requires EqualityComparable
Iter find(Iter first, Iter last, V v) {
while (first < last && *first != v)
++first;
return first;
}

Такое описание find не позволяет использовать его для списков: в итераторе списка отсутствует операция <. Без использования концептов ошибка будет обнаружена только при попытке компиляции тела настроенного шаблона find<list<int>::iterator,int>, т.е. на позднем этапе. При использовании концептов ошибка компиляции будет получена при первой компиляции тела шаблона и проверке входящих в него типов на соответствие концептам InputIterator и EqualityComparable, т.е. на существенно более раннем этапе.

Приведем определение концепта EqualityComparable:

template<typename T>
concept EqualityComparable {
bool operator==(T, T);
bool operator!=(T, T);
};

Оно напоминает определение интерфейса в таких языках программирования как Java и C#. Однако, в Java и C# уже при определении типа T надо описать все возможные интерфейсы, которым он удовлетворяет. Концепты позволяют, не затрагивая определение типа T, наложить на него ограничения позже. Такой способ является значительно более гибким, поскольку невозможно заранее предугадать все требования, накладываемые на тип T при его определении.

Функции, определенные внутри концепта, называются ассоциированными функциями. Так, в предыдущем объявлении bool operator==(T, T) является ассоциированной функцией. Кроме этого, внутри концепта могут определяться так называемые ассоциированные типы. Ассоциированные типы, как правило, используются для представления типов параметров и типов возвращаемых значений ассоциированных функций. Например, операция operator*, определенная в концепте InputIterator, возвращает некий тип value_type, который следует определить в концепте InputIterator следующим образом:

concept InputIterator<typename Iter> {
typename value_type;
value_type operator*(Iter);
...
};

Все типы, удовлетворяющие концепту InputIterator, должны определять тип InputIterator::value_type, а ссылка на этот тип может фигурировать, например, при наложении ограничений в секции requires, как в примере с алгоритмом find:

requires EqualityComparable

Обычно концепты определяются со служебным словом auto (такие концепты называются автоматическими):

auto template<typename T>
concept EqualityComparable {
bool operator==(T, T);
bool operator!=(T, T);
};

В этом случае любой тип, имеющий операции == и !=, удовлетворяет концепту. В частности, таковыми являются все стандартные числовые типы, тип string и пр. Если концепт определен без служебного слова auto или если в типе отсутствуют функции или операции с такими именами, то для того, чтобы тип удовлетворял концепту, необходимо объявить для него так называемое отображение концепта (concept_map). Отображение концепта для данного типа должно удовлетворять каждой ассоциированной функции и реализовывать каждый ассоциированный тип из концепта. Например, чтобы тип

struct Person {
string name;
int age;
};

удовлетворял концепту EqualityComparable в смысле равенства только полей name, необходимо объявить следующее отображение концепта:

concept_map EqualityComparable {
bool operator==(const Person& p1, const Person& p2)
{ return p1.name == p2.name; }
bool operator!=(const Person& p1, const Person& p2);
{ return !(p1 == p2); }
};

Чтобы не повторять очевидную реализацию operator!=, в концепте EqualityComparable следует задать для operator!= реализацию по умолчанию:

auto template<typename T>
concept EqualityComparable {
bool operator==(T, T);
bool operator!=(T t1, T t2) { return !(t1 == t2); }
};

В этом случае если объявление операции != в типе или его отображении концепта отсутствует, то берется реализация по умолчанию.

Отображение концепта само может быть шаблонным. Например, чтобы тип vector<T> удовлетворял концепту

concept Stack<typename X> {
typename value_type;
void push(X&, value_type);
void pop(X&);
};

следует определить шаблон отображения концепта

template<typename T>
concept_map Stack > {
typedef T value_type;
void push(std::vector& v, const T& x) { v.push_back(x); }
void pop(std::vector& v) { v.pop_back(); }
};

Обратим внимание, что отображение ассоциированного типа выполняется с помощью директивы typedef.

Концепты могут наследоваться (уточняться). Например:

concept ForwardIterator<typename Iter> { ... };
concept RandomAccessIterator<typename Iter>: ForwardIterator
{ ... };

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

template
void advance(Iter& p, int n) { while (n--) ++p; }
template
void advance(Iter& p, int n) { p += n; }

при вызове

vector<int> v(10);
vector<
int>::iterator vi = v.begin();
advance(vi,5);

предпочтение отдается более специализированной версии advance, в которой тип Iter удовлетворяет концепту RandomAccessIterator.

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