LINUX.ORG.RU

Продолжаю удивляться C++.

 


0

2

Добрый день, уважаемые разработчики!

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

Однако существует ещё такая библиотека, как Boost.Exception. В ней есть такая возможность, как дополнять объекты исключения произвольной информацией с помощью оператора <<. Причём, дополнять можно не просто объекты, а объекты константные. Давайте посмотрим API:

template <class E, class Tag, class T>
    E const & operator<<( E const & x, error_info<Tag,T> const & v );

Итак, константные объекты по мнению разработчиков хорошей библиотеки Boost.Exception могут меняться, что противоречит самой идее константности. А что думаете вы по этому поводу?

const там только потому, что в противном случае, в функцию можно было бы передать только lvalue, что не очень подходит для исключений, где объекты как правило создаются и используются без каких-либо промежуточных переменных. (C) anonymous



Последнее исправление: azelipupenko (всего исправлений: 3)

Ответ на: комментарий от yoghurt

Когда очень хочется, то можно. см. const_cast

Но зачем? Если можно определить семантически корректный API изначально. Продолжаю удивляться.

azelipupenko
() автор топика

А что думаете вы по этому поводу?

Мы думаем две вещи: да, интерфейс логичен; тебе следует открыть для себя ключевое слово mutable.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

Мы думаем две вещи: да, интерфейс логичен

Они думают. И в чём же логика интерфейса, который обещает не изменять объект (E const& же), но сам же его меняет?

тебе следует открыть для себя ключевое слово mutable.

Для себя открыл mutable давно. Только причём тут mutable, когда речь идёт про непонятно зачем нарисованный const в API - это мне ещё предстоит таки открыть, да. Продолжаю удивляться.

azelipupenko
() автор топика
Ответ на: комментарий от azelipupenko

И в чём же логика интерфейса, который обещает не изменять объект (E const& же), но сам же его меняет?

Логика в том, что к объекту _добавляется_ информация. Добавляется, понимаешь? И получается старый объект + добавленная информация.

Для себя открыл mutable давно. Только причём тут mutable

Похоже, тебе трудно понять очевидные вещи. Может, бросить тебе Си++? Там еще и неочевидных вещей полно.

когда речь идёт про непонятно зачем нарисованный const в API - это мне ещё предстоит таки открыть, да

Да, тебе многое нужно открыть.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

Логика в том, что к объекту _добавляется_ информация. Добавляется, понимаешь? И получается старый объект + добавленная информация.

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

std::string str{"tail"};
str.append("gun").append("ner");

Я бы мог согласиться с тобой, опытным таким всем, если бы operator<< возвращал бы новый объект. Но он возвращает E& const, т.е. ссылку на тот же объект (ну а на что же ещё, если не на тот же объект?) Так что нет, не понимаю про «добавляется».

Похоже, тебе трудно понять очевидные вещи. Может, бросить тебе Си++? Там еще и неочевидных вещей полно.

Похоже тебе трудно понять очевидные вещи, если функция принимает аргумент типа const, то она даёт обещание его не изменять. Добавлять информацию - это тоже изменять. Может, тебе бросить C++? Раз ты предлагаешь мне, опытному и прожжёному бросить C++ просто по причине того, что я наехал на авторитетный Boost? :-)

Да, тебе многое нужно открыть.

Ну как бы да, мне нравится учиться. Всю жизнь это буду делать. Тебе тоже советую.

azelipupenko
() автор топика
Ответ на: комментарий от azelipupenko

Логика в том, что к объекту _добавляется_ информация. Добавляется, понимаешь? И получается старый объект + добавленная информация.

Конечно понимаю.

Судя по всему тому, что ты написал дальше - нет, не понимаешь.

если функция принимает аргумент типа const, то она даёт обещание его не изменять.

Это, конечно же, не так. Она имеет полное право менять mutable-поля.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

mutable - хак для логической константности, тут ее нет. Если уж ссылаться на очевидность, очевидно что инсертеры меняют объект.

anonymous
()
Ответ на: комментарий от tailgunner

Судя по всему тому, что ты написал дальше - нет, не понимаешь.

Судья из тебя нулевой.

Это, конечно же, не так. Она имеет полное право менять mutable-поля.

Это если она друг или член. Но ты навёл меня на мысль о том, что логичность API в данном случае состоит в том, что operator<< даёт обещание не изменять объект типа E, но т.к. он является наследником boost::exception, то operator<< может менять mutable-члены именно boost::exception. Спасибо тебе, добрый дядя.

azelipupenko
() автор топика
Ответ на: комментарий от anonymous

mutable - хак для логической константности

Да.

тут ее нет

А сформулируй условия «логической константности».

очевидно что инсертеры меняют объект

Если бы это было не инсертером - всё было бы нормально?

tailgunner ★★★★★
()
Ответ на: комментарий от azelipupenko

Это, конечно же, не так. Она имеет полное право менять mutable-поля.

Это если она друг или член

Если она не друг и не член, она имеет право вызвать друга или члена с тем же эффектом.

tailgunner ★★★★★
()
Ответ на: комментарий от anonymous

mutable - хак для логической константности, тут ее нет. Если уж ссылаться на очевидность, очевидно что инсертеры меняют объект.

Им в Boost нужно было просто добавить перегрузку operator<< для неконстантного аргумента boost::exception&, и чтобы шаблон operator<< не принимал boost::exception, а принимал только наследников boost::exception. Вот тогда всё было бы логично. Но я думаю это для поддержки всякого компиляторного старья.

azelipupenko
() автор топика
Ответ на: комментарий от tailgunner

Если она не друг и не член, она имеет право вызвать друга или члена с тем же эффектом.

Иметь право вызывать друга или члена это не одно и тоже, что «имеет полное право менять mutable-поля». Право вызывать публичные члены - это одно, а изменять mutable-поля - это уже другое. Не получилось у тебя меня поправить :-)

azelipupenko
() автор топика

константные объекты менять нельзя

Да. А неконстантные можно. Даже если они прилетели к тебе по указателю/ссылке на константу.

anonymous
()
Ответ на: комментарий от azelipupenko

Иметь право вызывать друга или члена это не одно и тоже, что «имеет полное право менять mutable-поля»

Утипути, ты начал придираться к формулировкам? Тогда смотри:

azelipupenko> если функция принимает аргумент типа const, то она даёт обещание его не изменять.

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

Не получилось у тебя меня поправить :-)

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

tailgunner ★★★★★
()
Ответ на: комментарий от anonymous

Да. А неконстантные можно. Даже если они прилетели к тебе по указателю/ссылке на константу.

Так если они прилетели ко мне на ссылке на константу, значит я не хочу их менять. Компилятор мне в помощь для этого. А тут они меняются, и компилятору плевать. API не очевидный, но по своему логичный.

azelipupenko
() автор топика
Ответ на: комментарий от tailgunner

если функция принимает аргумент типа const, то она даёт обещание его не изменять.

Это действительно так. Не важно, член или не член эта функция, но константность она должна уважать. То, что где-то там в потрохах есть mutable, пользователей этой функции колышить не должно. Инкапсуляция - это такое. mutable придумали для того, чтобы позволить функциям-членам скрытно менять объекты, как деталь реализации. А когда функция, принимая объект по константной ссылке, дописывает на самом деле в него инфу, которую можно потом получить через публичный API, то грош цена такой константности. Она не нужна в принципе, т.к. везде того и жди, что объект будет изменён «потому что данные в объекте видите ли mutable». Оправдание найдено, все свободны.

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

У тебя получилось показать некоторую логичность Boost API.

azelipupenko
() автор топика
Ответ на: комментарий от tailgunner

А сформулируй условия «логической константности»

Состояние объекта одинаково до и после вызова, как минимум.

Если бы это было не инсертером - всё было бы нормально?

Если бы было немодифицирующей функцией - да, было бы нормально.

Если авторы хотели сказать что добавляемая информация прозрачна (не входит в состояние), то const имеет смысл, да. Но его мало в этой логике, ведь поведение программы зависит от этой информации.

anonymous
()
Ответ на: комментарий от anonymous

Если авторы хотели сказать что добавляемая информация прозрачна (не входит в состояние), то const имеет смысл, да.

Я это понимаю теперь так, что аргумент E& const в шаблоне operator<< обещает не менять E - т.е. пользовательский тип исключения. Если бы там был просто E&, то тогда такого обещания бы не было. Т.е. тут логично.

Но его мало в этой логике, ведь поведение программы зависит от этой информации.

А вот тут да, это, в принципе то, из-за чего я поднял эту тему.

azelipupenko
() автор топика
Ответ на: комментарий от tailgunner

ерунду говоришь. Судя по return value возвращается тот же объект. Поэтому очень вероятно что внутри const_cast (если нет чего-то, о чём не видно из заголовка функции). Поэтому автор такого API, мягко коворя, имеет конечный радиус кривизны рук.

mutable - applies to non-static class members of non-reference non-const type and specifies that the member does not affect the externally visible state of the class (as often used for mutexes, memo caches, lazy evaluation, and access instrumentation). mutable members of const class instances are modifiable. (Note: the C++ language grammar treats mutable as a storage-class-specifier, but it does not affect storage class.)

Насколько я могу судить, externally visible state of the class меняется. Но лишь бы компилилось и работало без UB, конечно.

dzidzitop ★★
()
Последнее исправление: dzidzitop (всего исправлений: 1)

какой-то парад чайников в треде. const там только потому, что в противном случае, в функцию можно было бы передать только lvalue, что не очень подходит для исключений, где объекты как правило создаются и используются без каких-либо промежуточных переменных. на, поиграйся:

class Test {
public:
    Test() {
    }
};

/* const */ Test& operator<<(/* const */ Test& test, int val) {
    return test;
}

int main(int argc, char* argv[]) {
    throw Test() << 1;
}
anonymous
()
Ответ на: комментарий от anonymous

А сформулируй условия «логической константности»

Состояние объекта одинаково до и после вызова, как минимум.

Ты просто заменил «логическую константность» на «состояние объекта».

tailgunner ★★★★★
()
Ответ на: комментарий от dzidzitop

ерунду говоришь.

А я считаю, что ерунду говоришь ты.

Судя [...] очень вероятно [...] если [...] насколько я могу судить [...]

Окей.

Хотя я хотел бы услышать определение «externally visible state».

tailgunner ★★★★★
()
Последнее исправление: tailgunner (всего исправлений: 1)
Ответ на: комментарий от anonymous

какой-то парад чайников в треде.

Здравствуй уважаемый хакер!

const там только потому, что в противном случае, в функцию можно было бы передать только lvalue

Т.е. разработчики Boost, которая известна как "...one of the most highly regarded and expertly designed C++ library projects in the world." (C) — Herb Sutter and Andrei Alexandrescu, C++ Coding Standards, показывают своим примером, что эта ваша константность, наряду с корректностью и строгостью API ничто в сравнении с удобством записи однострочных уродливых конструкций как throw() << 1?

azelipupenko
() автор топика

Как человек с опытом в этом дерьме, скажу, что в некоторых ситуациях — применять const_cast приходится... На самом деле очень удобно. Например, константный объект сам внутри себя устраняет поломки — а ты дергаешь всевозможные методы. А вообще, почитай Андрея Александреску «Современное проектирование на С++», там ты и ни такое найдешь

aandrew002
()
Ответ на: комментарий от azelipupenko

Т.е. разработчики Boost, которая известна как "...one of the most highly regarded and expertly designed C++ library projects in the world." (C) — Herb Sutter and Andrei Alexandrescu, C++ Coding Standards, показывают своим примером, что эта ваша константность, наряду с корректностью и строгостью API ничто в сравнении с удобством записи однострочных уродливых конструкций как throw() << 1?

Именно так, за исключением того, что уродливо/красиво – это чистая субъективщина (для кого-то C++ сам по себе уродлив). А еще им нужно поддерживать pre-C++11 конпиляторы, где нет rvalue-ссылок.

anonymous
()
Ответ на: комментарий от azelipupenko

Прочитал как «one of the most highly retarded»

anonymous
()
Ответ на: комментарий от anonymous

Именно так, за исключением того, что уродливо/красиво – это чистая субъективщина (для кого-то C++ сам по себе уродлив).

Ты наверное разработчик Boost, раз так уверен. Но тем лучше. Видишь ли, в данном случае уродство объективное, потому что меняется то, что меняться не должно - константа означает постоянство.

Вот смотри. Из холодного крана должна идти вода холодная, а из горячего - горячая. Представь теперь, что какое-то ТСЖ принимает решение - а пусть по утрам из любого крана идёт вода тёплая, ведь всё равно все умываются и принимают душ по утрам. Зачем им холодная вода или горячая вода по утрам? Ради их удобства сделаем им тёплую, большинство оценит, а кто не оценит - тот субъективно не прав. Мы эксперты, нам виднее.

azelipupenko
() автор топика
Ответ на: комментарий от azelipupenko

Ты забыл процитировать вторую часть комментария:

А еще им нужно поддерживать pre-C++11 конпиляторы, где нет rvalue-ссылок.

anonymous
()
Ответ на: комментарий от azelipupenko

Видишь ли, в данном случае уродство объективное, потому что меняется то, что меняться не должно - константа означает постоянство.

Т.е. это даже не уродство, а извращение, т.е. «противоестественное поведение». По определению.

azelipupenko
() автор топика
Ответ на: комментарий от anonymous

Ты забыл процитировать вторую часть комментария:

На всё был дан ответ и поставлен диагноз. Менять константный объект ради удобства записи - это извращение и безвкусица.

azelipupenko
() автор топика
Ответ на: комментарий от azelipupenko

На всё был дан ответ и поставлен диагноз. Менять константный объект ради удобства записи - это извращение и безвкусица.

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

anonymous
()
Ответ на: комментарий от anonymous

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

Покажи место где ты увидел словоблудие.

А ребята просто хотят, чтобы их код продолжал работать в старых плюсах.

Тогда почему ребята не сделали версию E&& operator<<( E && x, error_info<Tag,T> const & v ) для новых плюсов и не пояснили этот момент в своей документации? Не все же такие умные, как ребята из Boost.

azelipupenko
() автор топика
Ответ на: комментарий от azelipupenko

Тогда почему ребята не сделали версию E&& operator<<( E && x, error_info<Tag,T> const & v ) для новых плюсов и не пояснили этот момент в своей документации? Не все же такие умные, как ребята из Boost.

Для того чтобы залетному пассажиру с утонченным чувством прекрасного было хорошо? Форкни и сделай.

anonymous
()
Ответ на: комментарий от anonymous

Для того чтобы залетному пассажиру с утонченным чувством прекрасного было хорошо?

Чтобы всем пассажирам с утончённым чувством прекрасного было хорошо.

Форкни и сделай.

Спасибо, что-то не хочется.

Ну что сказать. Мне всё понятно. Спасибо всем за дискуссию! Тема закрыта.

azelipupenko
() автор топика
Ответ на: комментарий от azelipupenko

Чтобы всем пассажирам с утончённым чувством прекрасного было хорошо.

буст изначально не лучший пример, если топишь за красоту кода

anonymous
()
Ответ на: комментарий от anonymous

Да, я топлю за красоту кода и за его корректность. Записи throw E() << 1, с явным привкусом безвкусицы, да ещё и через const чтобы обойти законное требование к неконстантной ссылке, которая должна ссылаться на lvalue, меня не впечатляют. Наоборот.

azelipupenko
() автор топика
Ответ на: комментарий от tailgunner

Ты просто заменил «логическую константность» на «состояние объекта»

Состояние тоже расшифровывать? Об изменении состояния судят по изменению поведения. Функция get_error_info вернет разное в зависимости от работы этого оператора => состояние (то самое externally visible state) изменилось => оператор логически неконстантный для E.

anonymous
()
Ответ на: комментарий от azelipupenko

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

anonymous
()
Ответ на: комментарий от anonymous

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

rupert ★★★★★
()
Ответ на: комментарий от azelipupenko

Ну и что, что он создал Linux. Я Windows люблю больше.

то есть ты весь тред задвигал про то как важна красота и непротиворечивость API, а по факту WinAPI тебе нравится больше чем линуксовый POSIX с ништяками?

anonymous
()
Ответ на: комментарий от anonymous

то есть ты весь тред задвигал про то как важна красота и непротиворечивость API, а по факту WinAPI тебе нравится больше чем линуксовый POSIX с ништяками?

Ну так если Линус «наказывает мейнтейнеров» за то, что они любят красоту и непротиворечивость, «возвращая их к реальности», то уж лучше Windows, чем Linux. Там хоть можно вернуться к реальности не через унижения, а через работу с коммерческой платформой для зарабатывания денег ради.

azelipupenko
() автор топика
Ответ на: комментарий от azelipupenko

Тогда почему ребята не сделали версию E&& operator<<( E && x, error_info<Tag,T> const & v ) для новых плюсов и не пояснили этот момент в своей документации?

А там rvalue ref на шаблонный тип случаем не схлопнется в lvalue ref, дав в итоге ту же проблему?

i-rinat ★★★★★
()
Ответ на: комментарий от anonymous

лол, такой ты цветочек

Ну смотри. И унижен не будешь, и к реальности вернёшься, и денег заработаешь. Так что лучше Windows и WinAPI, однозначно.

azelipupenko
() автор топика
Ответ на: комментарий от i-rinat

А там rvalue ref на шаблонный тип случаем не схлопнется в lvalue ref, дав в итоге ту же проблему?

В нормальном коде такие функции все равно нужно писать с использованием std::enable_if и std::is_base_of. Если уже разбираться до конца, то и rvalue-ссылку из этого оператора возвращать нельзя.

anonymous
()
Ответ на: комментарий от anonymous

Если уже разбираться до конца, то и rvalue-ссылку из этого оператора возвращать нельзя.

Почему из std::move можно, а из этого оператора нельзя?

azelipupenko
() автор топика
Ответ на: комментарий от azelipupenko

Ну смотри. И унижен не будешь, и к реальности вернёшься, и денег заработаешь. Так что лучше Windows и WinAPI, однозначно.

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

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.